feat: 提交页面

This commit is contained in:
2026-04-16 17:31:31 +08:00
parent d4d104c690
commit 9e12d54540
6 changed files with 194 additions and 24 deletions

View File

@@ -1748,5 +1748,17 @@ export default {
productImageSubTitle:' (来自设计集)',
apparelSketchTitle:'服装线稿图 ',
apparelSketchSubTitle:' (来自设计集)',
productName:'商品名称',
price:'价格',
productDescription:'商品描述',
designFor:'目标人群',
productCategory:'商品类别',
categoryTips:'请选择所有适用选项',
policy:'默认情况下,所有销售均遵循平台的许可政策——买家在下载后将获得使用许可',
learnMore:'了解更多',
draftSaved: '草稿已保存',
draftDesc: '您的商品已保存为草稿。\n您可以继续编辑或稍后在“我的商品”中发布。',
listingLive:'商品已上架',
publishDesc:'您的商品现已上架。\n买家可以浏览并购买您的设计。'
}
}

View File

@@ -1799,5 +1799,17 @@ export default {
productImageSubTitle:'(from design collection)',
apparelSketchTitle:'Apparel Sketch ',
apparelSketchSubTitle:'(from design collection)',
productName:'Product Name',
price:'Price',
productDescription:'Product Description',
designFor:'Designed For',
productCategory:'Product Category',
categoryTips:'select all that apply',
policy:'By default, all sales follow the platform\'s licensing policy — buyers will receive a usage license upon download.',
learnMore:'Learn more',
draftSaved: 'Draft Saved',
draftDesc: 'Your listing has been saved as a draft. \nYou can continue editing or publish it later from My Listings.',
listingLive:'Listing Live',
publishDesc:'Your listing is now live on the marketplace.\nBuyers can discover and purchase your design.'
}
}

View File

@@ -220,6 +220,13 @@ const routes: Array<RouteRecordRaw> = [
meta: { enter: "all" },
component: () =>
import("@/views/SellerDashboard/MyListings/EditDetail/index.vue")
},
{
path:'edit/status/:status',
name:'Status',
meta:{enter:'all'},
component: () =>
import("@/views/SellerDashboard/MyListings/EditDetail/Status.vue")
}
]
},

View File

@@ -0,0 +1,78 @@
<template>
<div class="status-wrapper flex flex-col flex-1">
<seller-header
class="edit-detail-header"
title="Edit Listing Details"
:breadcrumbs="[
{ title: 'My Listings', name: 'myListingsIndex' },
{ title: 'Select Collection', name: 'myListingsSelect' },
{ title: 'Select Sketch', name: 'myListingsSelectItem' },
{ title: 'Edit Listing Details', name: 'EditDetail' },
{ title: $t(title), name: 'Status' }
]"
/>
<div class="status-container flex flex-col flex-1 flex-center">
<img src="@/assets/images/seller/success-0.png" class="icon" alt="" />
<div class="title">{{ $t(title) }}</div>
<div class="desc">
{{ $t(desc) }}
</div>
<div class="btn">Back to My Listings</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from "vue"
import { useRoute } from "vue-router"
import SellerHeader from "../../seller-header.vue"
const ROUTE = useRoute()
const title = computed(() => {
if (ROUTE.params.status === "publish") return "SellerListEdit.listingLive"
if (ROUTE.params.status === "draft") return "SellerListEdit.draftSaved"
})
const desc = computed(() => {
if (ROUTE.params.status === "publish") return "SellerListEdit.publishDesc"
if (ROUTE.params.status === "draft") return "SellerListEdit.draftDesc"
})
</script>
<style lang="less" scoped>
.status-wrapper {
.status-container {
row-gap: 2.4rem;
font-weight: 400;
.title {
font-size: 2.2rem;
}
.icon {
width: 8.33rem;
height: 8.33rem;
}
.desc {
width: 58.2rem;
text-align: center;
white-space: pre-line;
color: #585858;
font-size: 1.8rem;
}
.btn {
margin-top: 3.6rem;
height: 6rem;
width: 30rem;
border-radius: 4rem;
text-align: center;
line-height: 6rem;
padding: 0 2rem;
font-size: 1.6rem;
column-gap: 0.8rem;
cursor: pointer;
background-color: #000;
color: #fff;
}
}
}
</style>

View File

@@ -4,7 +4,14 @@
v-for="item in options"
:key="item.key"
type="button"
:class="['radio-button', { 'is-active': modelValue === item.key }]"
:class="[
'radio-button',
{
'is-active': multiple
? selectedValues.includes(item.key)
: modelValue === item.key
}
]"
@click="selectOption(item.key)"
>
{{ item.name }}
@@ -13,6 +20,8 @@
</template>
<script setup lang="ts">
import { computed } from "vue"
interface Option {
name: string | number
value: string | number | boolean
@@ -21,15 +30,40 @@ interface Option {
}
const props = defineProps<{
modelValue: string | number | boolean | null // v-model 绑定的值
modelValue: string | number | boolean | Array<string | number | boolean> | null
options: Option[] // 按钮选项数组
multiple?: boolean // 是否支持多选,默认为 false
}>()
const emit = defineEmits<{
(e: "update:modelValue", value: any): void
}>()
const multiple = props.multiple === true
const selectedValues = computed(() => {
if (!multiple) {
return typeof props.modelValue === 'undefined' || props.modelValue === null
? []
: [props.modelValue]
}
return Array.isArray(props.modelValue) ? props.modelValue : []
})
const selectOption = (value: any) => {
if (multiple) {
const current = Array.isArray(props.modelValue) ? [...props.modelValue] : []
const index = current.indexOf(value)
if (index >= 0) {
current.splice(index, 1)
} else {
current.push(value)
}
emit("update:modelValue", current)
return
}
if (props.modelValue !== value) {
emit("update:modelValue", value)
}

View File

@@ -12,11 +12,14 @@
>
<template #right>
<div class="operate-menu flex">
<div class="menu-btn flex align-center save">
<div class="menu-btn flex align-center save" @click="handleClickMenu('draft')">
<span>{{ $t("SellerListEdit.saveDraft") }}</span>
<SvgIcon name="CSave" size="16" />
</div>
<div class="menu-btn flex align-center publish">
<div
class="menu-btn flex align-center publish"
@click="handleClickMenu('publish')"
>
<span>{{ $t("SellerListEdit.publish") }}</span>
<SvgIcon name="CPublish" size="16" />
</div>
@@ -131,7 +134,9 @@
<div class="right">
<div class="form-container flex flex-col">
<div class="form-item">
<div class="form-item-label required">Product Name</div>
<div class="form-item-label required">
{{ $t("SellerListEdit.productName") }}
</div>
<div class="form-item-value product-name">
<a-input
v-model:value="currentListing.productName"
@@ -143,7 +148,7 @@
</div>
</div>
<div class="form-item">
<div class="form-item-label required">Price</div>
<div class="form-item-label required">{{ $t("SellerListEdit.price") }}</div>
<div class="form-item-value price flex align-center">
<span>HK$</span>
<a-input
@@ -154,7 +159,9 @@
</div>
</div>
<div class="form-item">
<div class="form-item-label required">Product Description</div>
<div class="form-item-label required">
{{ $t("SellerListEdit.productDescription") }}
</div>
<div class="form-item-value desc">
<a-textarea
v-model:value="currentListing.desc"
@@ -167,15 +174,17 @@
</div>
</div>
<div class="form-item">
<div class="form-item-label required">Design for</div>
<div class="form-item-label required">
{{ $t("SellerListEdit.designFor") }}
</div>
<div class="form-item-value no-border">
<Radio :options="genderOptions" v-model="currentListing.gender" />
</div>
</div>
<div class="form-item">
<div class="form-item-label with-tip">
<span class="required">Product Category</span>
<span class="help-text">select all that apply</span>
<span class="required">{{ $t("SellerListEdit.productCategory") }}</span>
<span class="help-text">{{ $t("SellerListEdit.categoryTips") }}</span>
</div>
<div class="form-item-value no-border">
<Radio :options="categoryOptions" v-model="currentListing.category" />
@@ -184,9 +193,8 @@
<div class="license-note flex align-center">
<img src="@/assets/images/seller/tips.png" class="info-icon" />
<div class="note-copy">
By default, all sales follow the platform's licensing policy — buyers
will receive a usage license upon download.
<a href="javascript:void(0)">Learn more</a>
{{ $t("SellerListEdit.policy") }}
<a href="javascript:void(0)">{{ $t("SellerListEdit.learnMore") }}</a>
</div>
</div>
</div>
@@ -207,13 +215,20 @@
</template>
<script setup lang="ts">
import { computed, ref, watch } from "vue"
import { computed, ref, watch, defineOptions } from "vue"
import { useRouter } from "vue-router"
import SellerHeader from "../../seller-header.vue"
import testImg from "@/assets/images/test.png"
import Radio from "./components/Radio.vue"
import { Https } from "@/tool/https"
import { useStore } from "vuex"
const ROUTER = useRouter()
defineOptions({
name: "EditDetail"
})
const STORE = useStore()
type CategoryOption = {
@@ -245,7 +260,7 @@ const topImageTitleMap: Record<(typeof topImageList)[number], string> = {
cover: "Cover"
}
const genderOptions = STORE.state.UserHabit?.sex.value||[]
const genderOptions = STORE.state.UserHabit?.sex.value || []
const fallbackCategoryOptions: Record<string, CategoryOption[]> = {
MALE: STORE.state.UserHabit?.MalePosition || [],
@@ -362,6 +377,18 @@ const handleSelectProdImg = (index: number) => {
currentListing.value.mainProductImage = ""
}
}
const handleClickMenu = (status: "draft" | "publish") => {
if (status === "draft") {
// save draft logic
console.log("Saving draft...", currentListing.value)
ROUTER.push({ name: "Status", params: { status: "draft" } })
} else if (status === "publish") {
// publish logic
console.log("Publishing...", currentListing.value)
ROUTER.push({ name: "Status", params: { status: "publish" } })
}
}
</script>
<style lang="less" scoped>
@@ -394,15 +421,15 @@ const handleSelectProdImg = (index: number) => {
cursor: pointer;
transition: all 0.25s ease;
&.save:hover {
&:hover {
background: #000;
color: #fff;
}
&.publish:hover {
background: #fff;
color: #000;
}
// &.publish:hover {
// background: #fff;
// color: #000;
// }
}
.edit-detail-header {
@@ -412,10 +439,10 @@ const handleSelectProdImg = (index: number) => {
.operate-menu {
column-gap: 2rem;
.publish {
background-color: #000000;
color: #ffffff;
}
// .publish {
// background-color: #000000;
// color: #ffffff;
// }
}
}