feat: 提交页面
This commit is contained in:
@@ -1748,5 +1748,17 @@ export default {
|
|||||||
productImageSubTitle:' (来自设计集)',
|
productImageSubTitle:' (来自设计集)',
|
||||||
apparelSketchTitle:'服装线稿图 ',
|
apparelSketchTitle:'服装线稿图 ',
|
||||||
apparelSketchSubTitle:' (来自设计集)',
|
apparelSketchSubTitle:' (来自设计集)',
|
||||||
|
productName:'商品名称',
|
||||||
|
price:'价格',
|
||||||
|
productDescription:'商品描述',
|
||||||
|
designFor:'目标人群',
|
||||||
|
productCategory:'商品类别',
|
||||||
|
categoryTips:'请选择所有适用选项',
|
||||||
|
policy:'默认情况下,所有销售均遵循平台的许可政策——买家在下载后将获得使用许可',
|
||||||
|
learnMore:'了解更多',
|
||||||
|
draftSaved: '草稿已保存',
|
||||||
|
draftDesc: '您的商品已保存为草稿。\n您可以继续编辑,或稍后在“我的商品”中发布。',
|
||||||
|
listingLive:'商品已上架',
|
||||||
|
publishDesc:'您的商品现已上架。\n买家可以浏览并购买您的设计。'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1799,5 +1799,17 @@ export default {
|
|||||||
productImageSubTitle:'(from design collection)',
|
productImageSubTitle:'(from design collection)',
|
||||||
apparelSketchTitle:'Apparel Sketch ',
|
apparelSketchTitle:'Apparel Sketch ',
|
||||||
apparelSketchSubTitle:'(from design collection)',
|
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.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,6 +220,13 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
meta: { enter: "all" },
|
meta: { enter: "all" },
|
||||||
component: () =>
|
component: () =>
|
||||||
import("@/views/SellerDashboard/MyListings/EditDetail/index.vue")
|
import("@/views/SellerDashboard/MyListings/EditDetail/index.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'edit/status/:status',
|
||||||
|
name:'Status',
|
||||||
|
meta:{enter:'all'},
|
||||||
|
component: () =>
|
||||||
|
import("@/views/SellerDashboard/MyListings/EditDetail/Status.vue")
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
78
src/views/SellerDashboard/MyListings/EditDetail/Status.vue
Normal file
78
src/views/SellerDashboard/MyListings/EditDetail/Status.vue
Normal 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>
|
||||||
@@ -4,7 +4,14 @@
|
|||||||
v-for="item in options"
|
v-for="item in options"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
type="button"
|
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)"
|
@click="selectOption(item.key)"
|
||||||
>
|
>
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
@@ -13,6 +20,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue"
|
||||||
|
|
||||||
interface Option {
|
interface Option {
|
||||||
name: string | number
|
name: string | number
|
||||||
value: string | number | boolean
|
value: string | number | boolean
|
||||||
@@ -21,15 +30,40 @@ interface Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string | number | boolean | null // v-model 绑定的值
|
modelValue: string | number | boolean | Array<string | number | boolean> | null
|
||||||
options: Option[] // 按钮选项数组
|
options: Option[] // 按钮选项数组
|
||||||
|
multiple?: boolean // 是否支持多选,默认为 false
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "update:modelValue", value: any): void
|
(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) => {
|
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) {
|
if (props.modelValue !== value) {
|
||||||
emit("update:modelValue", value)
|
emit("update:modelValue", value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,14 @@
|
|||||||
>
|
>
|
||||||
<template #right>
|
<template #right>
|
||||||
<div class="operate-menu flex">
|
<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>
|
<span>{{ $t("SellerListEdit.saveDraft") }}</span>
|
||||||
<SvgIcon name="CSave" size="16" />
|
<SvgIcon name="CSave" size="16" />
|
||||||
</div>
|
</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>
|
<span>{{ $t("SellerListEdit.publish") }}</span>
|
||||||
<SvgIcon name="CPublish" size="16" />
|
<SvgIcon name="CPublish" size="16" />
|
||||||
</div>
|
</div>
|
||||||
@@ -131,7 +134,9 @@
|
|||||||
<div class="right">
|
<div class="right">
|
||||||
<div class="form-container flex flex-col">
|
<div class="form-container flex flex-col">
|
||||||
<div class="form-item">
|
<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">
|
<div class="form-item-value product-name">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="currentListing.productName"
|
v-model:value="currentListing.productName"
|
||||||
@@ -143,7 +148,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-item">
|
<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">
|
<div class="form-item-value price flex align-center">
|
||||||
<span>HK$</span>
|
<span>HK$</span>
|
||||||
<a-input
|
<a-input
|
||||||
@@ -154,7 +159,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-item">
|
<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">
|
<div class="form-item-value desc">
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="currentListing.desc"
|
v-model:value="currentListing.desc"
|
||||||
@@ -167,15 +174,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-item">
|
<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">
|
<div class="form-item-value no-border">
|
||||||
<Radio :options="genderOptions" v-model="currentListing.gender" />
|
<Radio :options="genderOptions" v-model="currentListing.gender" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<div class="form-item-label with-tip">
|
<div class="form-item-label with-tip">
|
||||||
<span class="required">Product Category</span>
|
<span class="required">{{ $t("SellerListEdit.productCategory") }}</span>
|
||||||
<span class="help-text">select all that apply</span>
|
<span class="help-text">{{ $t("SellerListEdit.categoryTips") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-item-value no-border">
|
<div class="form-item-value no-border">
|
||||||
<Radio :options="categoryOptions" v-model="currentListing.category" />
|
<Radio :options="categoryOptions" v-model="currentListing.category" />
|
||||||
@@ -184,9 +193,8 @@
|
|||||||
<div class="license-note flex align-center">
|
<div class="license-note flex align-center">
|
||||||
<img src="@/assets/images/seller/tips.png" class="info-icon" />
|
<img src="@/assets/images/seller/tips.png" class="info-icon" />
|
||||||
<div class="note-copy">
|
<div class="note-copy">
|
||||||
By default, all sales follow the platform's licensing policy — buyers
|
{{ $t("SellerListEdit.policy") }}
|
||||||
will receive a usage license upon download.
|
<a href="javascript:void(0)">{{ $t("SellerListEdit.learnMore") }}</a>
|
||||||
<a href="javascript:void(0)">Learn more</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -207,13 +215,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 SellerHeader from "../../seller-header.vue"
|
||||||
import testImg from "@/assets/images/test.png"
|
import testImg from "@/assets/images/test.png"
|
||||||
import Radio from "./components/Radio.vue"
|
import Radio from "./components/Radio.vue"
|
||||||
import { Https } from "@/tool/https"
|
import { Https } from "@/tool/https"
|
||||||
import { useStore } from "vuex"
|
import { useStore } from "vuex"
|
||||||
|
|
||||||
|
const ROUTER = useRouter()
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "EditDetail"
|
||||||
|
})
|
||||||
|
|
||||||
const STORE = useStore()
|
const STORE = useStore()
|
||||||
|
|
||||||
type CategoryOption = {
|
type CategoryOption = {
|
||||||
@@ -245,7 +260,7 @@ const topImageTitleMap: Record<(typeof topImageList)[number], string> = {
|
|||||||
cover: "Cover"
|
cover: "Cover"
|
||||||
}
|
}
|
||||||
|
|
||||||
const genderOptions = STORE.state.UserHabit?.sex.value||[]
|
const genderOptions = STORE.state.UserHabit?.sex.value || []
|
||||||
|
|
||||||
const fallbackCategoryOptions: Record<string, CategoryOption[]> = {
|
const fallbackCategoryOptions: Record<string, CategoryOption[]> = {
|
||||||
MALE: STORE.state.UserHabit?.MalePosition || [],
|
MALE: STORE.state.UserHabit?.MalePosition || [],
|
||||||
@@ -362,6 +377,18 @@ const handleSelectProdImg = (index: number) => {
|
|||||||
currentListing.value.mainProductImage = ""
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@@ -394,15 +421,15 @@ const handleSelectProdImg = (index: number) => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.25s ease;
|
transition: all 0.25s ease;
|
||||||
|
|
||||||
&.save:hover {
|
&:hover {
|
||||||
background: #000;
|
background: #000;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.publish:hover {
|
// &.publish:hover {
|
||||||
background: #fff;
|
// background: #fff;
|
||||||
color: #000;
|
// color: #000;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-detail-header {
|
.edit-detail-header {
|
||||||
@@ -412,10 +439,10 @@ const handleSelectProdImg = (index: number) => {
|
|||||||
.operate-menu {
|
.operate-menu {
|
||||||
column-gap: 2rem;
|
column-gap: 2rem;
|
||||||
|
|
||||||
.publish {
|
// .publish {
|
||||||
background-color: #000000;
|
// background-color: #000000;
|
||||||
color: #ffffff;
|
// color: #ffffff;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user