feat: 面包屑导航
This commit is contained in:
@@ -1,8 +1,37 @@
|
||||
import { createRouter, createWebHistory, RouteRecordRaw, createWebHashHistory } from "vue-router"
|
||||
import type { RouteLocationNormalizedLoaded, RouteLocationRaw } from "vue-router"
|
||||
import store from "@/store"
|
||||
import { Https } from "@/tool/https"
|
||||
import { getCookie, setCookie } from "@/tool/cookie"
|
||||
|
||||
type SellerRouteMetaValue<T> = T | ((route: RouteLocationNormalizedLoaded) => T)
|
||||
type SellerBreadcrumbItem = {
|
||||
title?: SellerRouteMetaValue<string>
|
||||
titleKey?: SellerRouteMetaValue<string>
|
||||
to?: SellerRouteMetaValue<RouteLocationRaw>
|
||||
}
|
||||
|
||||
const myListingsBreadcrumb: SellerBreadcrumbItem = {
|
||||
title: "My Listings",
|
||||
to: { name: "myListingsIndex" }
|
||||
}
|
||||
const selectCollectionBreadcrumb: SellerBreadcrumbItem = {
|
||||
title: "Select Collection",
|
||||
to: { name: "myListingsSelect" }
|
||||
}
|
||||
const selectSketchBreadcrumb: SellerBreadcrumbItem = {
|
||||
title: "Select Sketch"
|
||||
}
|
||||
const editListingBreadcrumb: SellerBreadcrumbItem = {
|
||||
title: "Edit Listing Details"
|
||||
}
|
||||
const statusBreadcrumb: SellerBreadcrumbItem = {
|
||||
titleKey: (route) =>
|
||||
route.params.status === "publish"
|
||||
? "SellerListEdit.listingLive"
|
||||
: "SellerListEdit.draftSaved"
|
||||
}
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "/",
|
||||
@@ -167,7 +196,11 @@ const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "becomeSeller",
|
||||
name: "becomeSeller",
|
||||
meta: { enter: "all" },
|
||||
meta: {
|
||||
enter: "all",
|
||||
sellerHeaderTitle: "Apply to Become a Seller",
|
||||
sellerHeaderTip: "Join the Stylish Parade and start selling your design work"
|
||||
},
|
||||
component: () => import("@/views/SellerDashboard/BecomeSeller/index.vue")
|
||||
},
|
||||
{
|
||||
@@ -185,7 +218,7 @@ const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "myListings",
|
||||
name: "myListings",
|
||||
meta: { enter: "all" },
|
||||
meta: { enter: "all", sellerBreadcrumb: myListingsBreadcrumb },
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
@@ -196,35 +229,74 @@ const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: "index",
|
||||
name: "myListingsIndex",
|
||||
meta: { enter: "all" },
|
||||
meta: {
|
||||
enter: "all",
|
||||
sellerHeaderTitle: "My Listings",
|
||||
sellerHeaderTip: "Active listings and unpublished inventory.",
|
||||
sellerBreadcrumbs: [myListingsBreadcrumb]
|
||||
},
|
||||
component: () =>
|
||||
import("@/views/SellerDashboard/MyListings/main/index.vue")
|
||||
},
|
||||
{
|
||||
path: "select",
|
||||
name: "myListingsSelect",
|
||||
meta: { enter: "all" },
|
||||
meta: {
|
||||
enter: "all",
|
||||
sellerHeaderTitle: "Select Collection",
|
||||
sellerBreadcrumbs: [
|
||||
myListingsBreadcrumb,
|
||||
selectCollectionBreadcrumb
|
||||
]
|
||||
},
|
||||
component: () =>
|
||||
import("@/views/SellerDashboard/MyListings/createSelect/index.vue")
|
||||
},
|
||||
{
|
||||
path: "select/:collectionId",
|
||||
name: "myListingsSelectItem",
|
||||
meta: { enter: "all" },
|
||||
meta: {
|
||||
enter: "all",
|
||||
sellerHeaderTitle: "Select Collection",
|
||||
sellerBreadcrumbs: [
|
||||
myListingsBreadcrumb,
|
||||
selectCollectionBreadcrumb,
|
||||
selectSketchBreadcrumb
|
||||
]
|
||||
},
|
||||
component: () =>
|
||||
import("@/views/SellerDashboard/MyListings/createSelectItem/index.vue")
|
||||
},
|
||||
{
|
||||
path: "edit",
|
||||
name: "EditDetail",
|
||||
meta: { enter: "all" },
|
||||
meta: {
|
||||
enter: "all",
|
||||
sellerHeaderTitle: "Edit Listing Details",
|
||||
sellerBreadcrumbs: [
|
||||
myListingsBreadcrumb,
|
||||
selectCollectionBreadcrumb,
|
||||
selectSketchBreadcrumb,
|
||||
editListingBreadcrumb
|
||||
]
|
||||
},
|
||||
component: () =>
|
||||
import("@/views/SellerDashboard/MyListings/EditDetail/index.vue")
|
||||
},
|
||||
{
|
||||
path:'edit/status/:status',
|
||||
name:'Status',
|
||||
meta:{enter:'all'},
|
||||
path: "edit/status/:status",
|
||||
name: "Status",
|
||||
meta: {
|
||||
enter: "all",
|
||||
sellerHeaderTitle: "Edit Listing Details",
|
||||
sellerBreadcrumbs: [
|
||||
myListingsBreadcrumb,
|
||||
selectCollectionBreadcrumb,
|
||||
selectSketchBreadcrumb,
|
||||
editListingBreadcrumb,
|
||||
statusBreadcrumb
|
||||
]
|
||||
},
|
||||
component: () =>
|
||||
import("@/views/SellerDashboard/MyListings/EditDetail/Status.vue")
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<div class="become-seller">
|
||||
<seller-header
|
||||
title="Apply to Become a Seller"
|
||||
tip="Join the Stylish Parade and start selling your design work"
|
||||
/>
|
||||
<seller-header />
|
||||
<div class="content">
|
||||
<seller-apply v-if="applyStatus === null" @submit="onSubmit" />
|
||||
<seller-review v-else />
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
<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="" />
|
||||
|
||||
@@ -2,13 +2,6 @@
|
||||
<div class="edit-detail-wrapper 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' }
|
||||
]"
|
||||
>
|
||||
<template #right>
|
||||
<div class="operate-menu flex">
|
||||
|
||||
@@ -40,14 +40,7 @@ defineExpose({})
|
||||
</script>
|
||||
<template>
|
||||
<div class="create-select">
|
||||
<seller-header
|
||||
title="Select Collection"
|
||||
:breadcrumbs="[
|
||||
{title:'My Listings', name:'myListingsIndex'},
|
||||
{title:'Select Collection', name: 'myListingsSelect' }
|
||||
]"
|
||||
>
|
||||
</seller-header>
|
||||
<seller-header />
|
||||
<div class="content">
|
||||
<div class="title">
|
||||
<div class="left">
|
||||
@@ -152,4 +145,4 @@ defineExpose({})
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -149,14 +149,7 @@ const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<div class="create-select-item">
|
||||
<seller-header
|
||||
title="Select Collection"
|
||||
:breadcrumbs="[
|
||||
{title:'My Listings', name:'myListingsIndex'},
|
||||
{title:'Select Collection', name: 'myListingsSelect' },
|
||||
{title:'Select Sketch', name: 'myListingsSelectItem' }
|
||||
]"
|
||||
>
|
||||
<seller-header>
|
||||
<template #right>
|
||||
<div class="header-right">
|
||||
<div class="chooseNum">
|
||||
@@ -383,4 +376,4 @@ const {} = toRefs(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -41,10 +41,7 @@ const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<div class="myListings-seller">
|
||||
<seller-header
|
||||
title="My Listings"
|
||||
tip="Active listings and unpublished inventory."
|
||||
>
|
||||
<seller-header>
|
||||
<template #right>
|
||||
<div class="button" @click="newListing">
|
||||
<span>New Listing</span>
|
||||
@@ -99,4 +96,4 @@ const {} = toRefs(data);
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -4,24 +4,20 @@
|
||||
<svg-icon name="seller-back" size="24" />
|
||||
</div>
|
||||
<div class="content">
|
||||
<span class="title" v-show="title">{{ title }}</span>
|
||||
<span class="tip" v-show="tip">{{ tip }}</span>
|
||||
<div class="breadcrumbs" v-show="breadcrumbs.length > 0">
|
||||
<template v-for="(v, i) in breadcrumbs" :key="i">
|
||||
<span class="title" v-show="displayTitle">{{ displayTitle }}</span>
|
||||
<span class="tip" v-show="displayTip">{{ displayTip }}</span>
|
||||
<div class="breadcrumbs" v-show="breadcrumbList.length > 1">
|
||||
<template v-for="(v, i) in breadcrumbList" :key="`${v.title}-${i}`">
|
||||
<span
|
||||
class="title"
|
||||
:class="{
|
||||
last: i === breadcrumbs.length - 1
|
||||
last: i === breadcrumbList.length - 1,
|
||||
clickable: i < breadcrumbList.length - 1
|
||||
}"
|
||||
@click="
|
||||
() => {
|
||||
const index = -(breadcrumbs.length - i - 1)
|
||||
if (index < 0) router.go(index)
|
||||
}
|
||||
"
|
||||
@click="onBreadcrumbClick(v, i)"
|
||||
>{{ v.title }}</span
|
||||
>
|
||||
<span class="icon" v-show="i < breadcrumbs.length - 1">
|
||||
<span class="icon" v-show="i < breadcrumbList.length - 1">
|
||||
<svg-icon name="seller-arrow_right_solid" size="10" />
|
||||
</span>
|
||||
</template>
|
||||
@@ -33,25 +29,150 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from "vue"
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue"
|
||||
import { useI18n } from "vue-i18n"
|
||||
import { useRoute, useRouter } from "vue-router"
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
tip: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
breadcrumbs: {
|
||||
type: Array, // { title: string, name: string }
|
||||
default: () => []
|
||||
import type { RouteLocationNormalizedLoaded, RouteLocationRaw } from "vue-router"
|
||||
|
||||
type RouteMetaValue<T> = T | ((route: RouteLocationNormalizedLoaded) => T)
|
||||
type SellerBreadcrumbSource =
|
||||
| string
|
||||
| {
|
||||
title?: RouteMetaValue<string>
|
||||
titleKey?: RouteMetaValue<string>
|
||||
to?: RouteMetaValue<RouteLocationRaw>
|
||||
name?: string
|
||||
path?: string
|
||||
}
|
||||
type SellerBreadcrumbItem = {
|
||||
title: string
|
||||
to?: RouteLocationRaw
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string
|
||||
tip?: string
|
||||
breadcrumbs?: SellerBreadcrumbSource[]
|
||||
}>(),
|
||||
{
|
||||
title: "",
|
||||
tip: "",
|
||||
breadcrumbs: () => []
|
||||
}
|
||||
})
|
||||
)
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
|
||||
const resolveMetaValue = <T,>(value?: RouteMetaValue<T>) => {
|
||||
if (typeof value === "function") {
|
||||
return (value as (route: RouteLocationNormalizedLoaded) => T)(route)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
const resolveRouteLocation = (to?: RouteLocationRaw) => {
|
||||
if (!to || typeof to === "string") return to
|
||||
const location = to as any
|
||||
if (location.name) {
|
||||
return {
|
||||
...location,
|
||||
params: {
|
||||
...route.params,
|
||||
...location.params
|
||||
}
|
||||
}
|
||||
}
|
||||
return to
|
||||
}
|
||||
|
||||
const resolveBreadcrumb = (source: SellerBreadcrumbSource) => {
|
||||
if (typeof source === "string") {
|
||||
return {
|
||||
title: source
|
||||
}
|
||||
}
|
||||
|
||||
const titleKey = resolveMetaValue(source.titleKey)
|
||||
const title = titleKey ? t(titleKey) : resolveMetaValue(source.title)
|
||||
const to = resolveRouteLocation(resolveMetaValue(source.to))
|
||||
const fallbackTo = source.name ? { name: source.name } : source.path
|
||||
|
||||
return {
|
||||
title: title || "",
|
||||
to: resolveRouteLocation(to || fallbackTo)
|
||||
}
|
||||
}
|
||||
|
||||
const resolveTitle = (title?: RouteMetaValue<string>, titleKey?: RouteMetaValue<string>) => {
|
||||
const key = resolveMetaValue(titleKey)
|
||||
return key ? t(key) : resolveMetaValue(title) || ""
|
||||
}
|
||||
|
||||
const autoBreadcrumbs = computed(() => {
|
||||
const currentRecord = route.matched[route.matched.length - 1]
|
||||
const customBreadcrumbs = currentRecord?.meta?.sellerBreadcrumbs
|
||||
|
||||
if (Array.isArray(customBreadcrumbs)) {
|
||||
return customBreadcrumbs
|
||||
.map((breadcrumb) => resolveBreadcrumb(breadcrumb as SellerBreadcrumbSource))
|
||||
.filter((breadcrumb) => breadcrumb.title)
|
||||
}
|
||||
|
||||
return route.matched
|
||||
.map((record) => record.meta?.sellerBreadcrumb)
|
||||
.filter(
|
||||
(breadcrumb): breadcrumb is SellerBreadcrumbSource =>
|
||||
typeof breadcrumb === "string" ||
|
||||
(typeof breadcrumb === "object" && breadcrumb !== null)
|
||||
)
|
||||
.map(resolveBreadcrumb)
|
||||
.filter((breadcrumb) => breadcrumb.title)
|
||||
})
|
||||
|
||||
const breadcrumbList = computed<SellerBreadcrumbItem[]>(() => {
|
||||
if (props.breadcrumbs.length) {
|
||||
return props.breadcrumbs
|
||||
.map(resolveBreadcrumb)
|
||||
.filter((breadcrumb) => breadcrumb.title)
|
||||
}
|
||||
return autoBreadcrumbs.value
|
||||
})
|
||||
|
||||
const displayTitle = computed(() => {
|
||||
return (
|
||||
props.title ||
|
||||
resolveTitle(
|
||||
route.meta.sellerHeaderTitle as RouteMetaValue<string> | undefined,
|
||||
route.meta.sellerHeaderTitleKey as RouteMetaValue<string> | undefined
|
||||
) ||
|
||||
breadcrumbList.value[breadcrumbList.value.length - 1]?.title ||
|
||||
""
|
||||
)
|
||||
})
|
||||
|
||||
const displayTip = computed(() => {
|
||||
return (
|
||||
props.tip ||
|
||||
resolveTitle(
|
||||
route.meta.sellerHeaderTip as RouteMetaValue<string> | undefined,
|
||||
route.meta.sellerHeaderTipKey as RouteMetaValue<string> | undefined
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
const onBreadcrumbClick = (breadcrumb: SellerBreadcrumbItem, index: number) => {
|
||||
if (index >= breadcrumbList.value.length - 1) return
|
||||
if (breadcrumb.to) {
|
||||
router.push(breadcrumb.to)
|
||||
return
|
||||
}
|
||||
|
||||
const historyIndex = -(breadcrumbList.value.length - index - 1)
|
||||
if (historyIndex < 0) router.go(historyIndex)
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.seller-header {
|
||||
@@ -90,8 +211,8 @@
|
||||
font-family: "pingfang_regular";
|
||||
color: #999;
|
||||
font-size: 1.4rem;
|
||||
cursor: pointer;
|
||||
&:not(.last) {
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user