修复前端迁移后的类型错误
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
type ScreenshotKey,
|
||||
} from '@/features/listings/api/listingOptions'
|
||||
import { createListing } from '@/features/listings/api/listings'
|
||||
import { usePricingCalculator } from '@/composables/usePricingCalculator'
|
||||
import { usePricingCalculator } from '@/shared/composables/usePricingCalculator'
|
||||
import {
|
||||
buildPublishDraft,
|
||||
clearRecord,
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
readPublishDraft,
|
||||
removePublishDraft,
|
||||
writePublishDraft,
|
||||
} from '@/composables/usePublishDraft'
|
||||
} from '@/features/seller/composables/usePublishDraft'
|
||||
import type { PublishForm } from '@/types/publish'
|
||||
import { commonOnlineTimes, dailyLossOptions, formatNumber, roundRatio } from '@/utils/pricing'
|
||||
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
.publish-page {
|
||||
max-width: 1180px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 20px 40px;
|
||||
}
|
||||
|
||||
.publish-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 300px;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.form-column,
|
||||
.summary-column {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.summary-column {
|
||||
align-self: start;
|
||||
position: sticky;
|
||||
top: 84px;
|
||||
}
|
||||
|
||||
.field-row,
|
||||
.input-block {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.field-row + .field-row,
|
||||
.compact-grid + .field-row,
|
||||
.level-panel,
|
||||
.time-preset-panel,
|
||||
.region-panel,
|
||||
.ratio-panel,
|
||||
.price-grid,
|
||||
.remark {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.field-row label,
|
||||
.input-block > span,
|
||||
.quantity-meta strong {
|
||||
color: #0f172a;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.field-row span,
|
||||
.input-block b,
|
||||
.quantity-meta span,
|
||||
.upload-copy span {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.field-hint {
|
||||
margin: 6px 0 0;
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.compact-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.compact-grid.two {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.level-panel,
|
||||
.time-preset-panel,
|
||||
.region-panel,
|
||||
.ratio-panel,
|
||||
.summary-panel {
|
||||
padding: 16px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.level-row,
|
||||
.time-preset-row,
|
||||
.region-title,
|
||||
.skin-title {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.level-row + .level-row,
|
||||
.time-preset-row + .time-preset-row,
|
||||
.skin-group + .skin-group {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.level-options,
|
||||
.region-grid,
|
||||
.mode-toggle,
|
||||
.ratio-mode-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.level-btn,
|
||||
.region-btn,
|
||||
.mode-toggle button,
|
||||
.ratio-mode-btn,
|
||||
.recommend-button,
|
||||
.upload-add {
|
||||
border: 1px solid #dbe3ee;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
color: #334155;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.level-btn,
|
||||
.region-btn,
|
||||
.mode-toggle button {
|
||||
min-height: 32px;
|
||||
padding: 0 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.level-btn.active,
|
||||
.region-btn.active,
|
||||
.mode-toggle button.active,
|
||||
.ratio-mode-btn.active {
|
||||
border-color: #2563eb;
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.quantity-table {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.quantity-header,
|
||||
.quantity-item {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(160px, 1fr) 100px 120px 116px 80px;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quantity-header {
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.quantity-item {
|
||||
padding: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.quantity-item.disabled {
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.quantity-meta small,
|
||||
.quantity-status,
|
||||
.quantity-price {
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.time-preset-content,
|
||||
.deposit-control {
|
||||
display: grid;
|
||||
grid-template-columns: 160px minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.upload-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.upload-item {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding: 14px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.upload-copy {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.upload-copy strong {
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.upload-copy small,
|
||||
.deposit-breakdown {
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.upload-preview {
|
||||
position: relative;
|
||||
aspect-ratio: 16 / 9;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background: #e2e8f0;
|
||||
}
|
||||
|
||||
.upload-preview img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.upload-preview button {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.upload-add {
|
||||
display: grid;
|
||||
min-height: 112px;
|
||||
place-items: center;
|
||||
align-content: center;
|
||||
gap: 6px;
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.hidden-file {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.recommend-button {
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
color: #2563eb;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.deposit-breakdown {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ratio-reference {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ratio-reference span,
|
||||
.price-cell span,
|
||||
.summary-main span,
|
||||
.summary-list span {
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ratio-reference strong {
|
||||
color: #2563eb;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.ratio-panel p {
|
||||
margin: 8px 0 14px;
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ratio-mode-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.ratio-mode-btn,
|
||||
.ratio-custom-card {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
border: 1px solid #dbe3ee;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ratio-mode-btn span {
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.price-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.price-cell {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.price-cell strong,
|
||||
.summary-list strong {
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.price-cell.accent strong,
|
||||
.summary-main strong {
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.summary-panel {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.summary-main {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding-bottom: 14px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.summary-main strong {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.summary-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin: 14px 0;
|
||||
}
|
||||
|
||||
.summary-list div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.summary-actions {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.summary-actions .el-button {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.publish-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.summary-column {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.compact-grid,
|
||||
.compact-grid.two,
|
||||
.upload-grid,
|
||||
.price-grid,
|
||||
.ratio-mode-grid,
|
||||
.time-preset-content,
|
||||
.deposit-control {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.quantity-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quantity-item {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Delete, DocumentChecked, Picture, RefreshRight, UploadFilled } from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
import { usePublishForm } from '@/composables/usePublishForm'
|
||||
import { usePublishForm } from '@/features/seller/composables/usePublishForm'
|
||||
import OptionChips from './components/OptionChips.vue'
|
||||
import PublishSection from './components/PublishSection.vue'
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<script setup lang="ts">
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
options: Array<string | number>
|
||||
modelValue?: string | number
|
||||
activeValues?: Array<string | number>
|
||||
keyPrefix?: string
|
||||
compact?: boolean
|
||||
suffix?: string
|
||||
}>(),
|
||||
{
|
||||
modelValue: undefined,
|
||||
activeValues: () => [],
|
||||
keyPrefix: '',
|
||||
compact: false,
|
||||
suffix: '',
|
||||
},
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
select: [value: string | number]
|
||||
}>()
|
||||
|
||||
function isActive(value: string | number) {
|
||||
return props.activeValues.includes(value) || props.modelValue === value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="option-chips" :class="{ compact }">
|
||||
<button
|
||||
v-for="option in options"
|
||||
:key="`${keyPrefix}${option}`"
|
||||
type="button"
|
||||
:class="{ active: isActive(option) }"
|
||||
@click="emit('select', option)"
|
||||
>
|
||||
{{ option }}{{ suffix }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.option-chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.option-chips button {
|
||||
min-height: 34px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid #dbe3ee;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
color: #334155;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s, background 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.option-chips.compact button {
|
||||
min-height: 30px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.option-chips button.active {
|
||||
border-color: #2563eb;
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
title: string
|
||||
description?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="publish-section">
|
||||
<header class="publish-section-header">
|
||||
<h2>{{ title }}</h2>
|
||||
<p v-if="description">{{ description }}</p>
|
||||
</header>
|
||||
<slot />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.publish-section {
|
||||
padding: 20px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.publish-section + .publish-section {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.publish-section-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.publish-section-header h2 {
|
||||
margin: 0;
|
||||
color: #0f172a;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.publish-section-header p {
|
||||
margin: 0;
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user