Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite

This commit is contained in:
X1627315083@163.com
2026-04-29 14:50:00 +08:00
6 changed files with 374 additions and 369 deletions

View File

@@ -20,6 +20,11 @@ This directory owns the seller listing edit/create detail page.
- Child components should receive props and emit events only. Do not import listing APIs or mutate parent state directly from children.
- If a new visual section is added to this page, prefer a new child component under `components/` plus shared types in `types.ts`.
## Vue SFC Order
- Vue single-file components must keep sections in this order: `<template>` first, then `<script setup lang="ts">`, then `<style>`.
- When creating or refactoring `.vue` files in this page, preserve that order even if external Vue guidance suggests another layout.
## Image Category Mapping
Detail API images are mapped by `category`:

View File

@@ -1,15 +1,3 @@
<script setup lang="ts">
import type { ListingItem } from "../types"
defineProps<{
sketchList: ListingItem["sketchList"]
}>()
const emit = defineEmits<{
(e: "crop", data: string | null, type: "apparel", index?: number): void
}>()
</script>
<template>
<div class="apparel-container">
<div class="title">
@@ -23,16 +11,30 @@ const emit = defineEmits<{
class="sketch-element flex flex-center"
>
<img class="img-src" :src="item.url || ''" alt="" />
<div class="crop-tool flex flex-center" @click="emit('crop', item.url, 'apparel', index)">
<div
class="crop-tool flex flex-center"
@click="emit('crop', item.url, 'apparel', index)"
>
<SvgIcon name="CCrop" color="#fff" size="12" />
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { ListingItem } from "../types"
defineProps<{
sketchList: ListingItem["sketchList"]
}>()
const emit = defineEmits<{
(e: "crop", data: string | null, type: "apparel", index?: number): void
}>()
</script>
<style lang="less" scoped>
.apparel-container {
.apparel-container {
margin-top: 3rem;
.title {
@@ -82,5 +84,5 @@ const emit = defineEmits<{
}
}
}
}
}
</style>

View File

@@ -1,26 +1,3 @@
<script setup lang="ts">
import Radio from "./Radio.vue"
import type { RadioOption } from "../types"
defineProps<{
productName: string
price: string
desc: string
gender: string
category: string[] | null
genderOptions: RadioOption[]
categoryOptions: RadioOption[]
}>()
const emit = defineEmits<{
(e: "update:productName", value: string): void
(e: "update:price", value: string): void
(e: "update:desc", value: string): void
(e: "update:gender", value: string): void
(e: "update:category", value: string[] | null): void
}>()
</script>
<template>
<div class="form-container flex flex-col">
<div class="form-item">
@@ -102,16 +79,39 @@ const emit = defineEmits<{
</div>
</template>
<script setup lang="ts">
import Radio from "./Radio.vue"
import type { RadioOption } from "../types"
defineProps<{
productName: string
price: string
desc: string
gender: string
category: string[] | null
genderOptions: RadioOption[]
categoryOptions: RadioOption[]
}>()
const emit = defineEmits<{
(e: "update:productName", value: string): void
(e: "update:price", value: string): void
(e: "update:desc", value: string): void
(e: "update:gender", value: string): void
(e: "update:category", value: string[] | null): void
}>()
</script>
<style lang="less" scoped>
.required {
.required {
&::after {
content: "*";
color: #df2b2c;
margin-left: 0.4rem;
}
}
}
.form-container {
.form-container {
row-gap: 3rem;
.form-item {
@@ -191,9 +191,9 @@ const emit = defineEmits<{
}
}
}
}
}
.license-note {
.license-note {
padding: 1.6rem;
column-gap: 1.6rem;
background: #f7f7f7;
@@ -218,5 +218,5 @@ const emit = defineEmits<{
margin-left: 0.4rem;
}
}
}
}
</style>

View File

@@ -1,19 +1,3 @@
<script setup lang="ts">
import { computed } from "vue"
import type { ListingItem } from "../types"
const props = defineProps<{
imageList: ListingItem["prodImageList"]
firstSelectedIndex: number | null
}>()
const emit = defineEmits<{
(e: "select", index: number): void
}>()
const selectedCount = computed(() => props.imageList.filter((item) => item.selected).length)
</script>
<template>
<div class="product-image-list-container">
<div class="title flex align-center space-between">
@@ -45,9 +29,24 @@ const selectedCount = computed(() => props.imageList.filter((item) => item.selec
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue"
import type { ListingItem } from "../types"
const props = defineProps<{
imageList: ListingItem["prodImageList"]
firstSelectedIndex: number | null
}>()
const emit = defineEmits<{
(e: "select", index: number): void
}>()
const selectedCount = computed(() => props.imageList.filter((item) => item.selected).length)
</script>
<style lang="less" scoped>
.product-image-list-container {
.product-image-list-container {
margin-top: 3rem;
.title {
@@ -150,5 +149,5 @@ const selectedCount = computed(() => props.imageList.filter((item) => item.selec
}
}
}
}
}
</style>

View File

@@ -1,22 +1,3 @@
<script setup lang="ts">
import type { TopImageType } from "../types"
defineProps<{
images: Record<TopImageType, string | null>
}>()
const emit = defineEmits<{
(e: "crop", data: string | null, type: TopImageType): void
}>()
const topImageList: TopImageType[] = ["sketch", "mainProductImage", "cover"]
const topImageTitleMap: Record<TopImageType, string> = {
sketch: "SellerListEdit.sketch",
mainProductImage: "SellerListEdit.mainProductImage",
cover: "SellerListEdit.cover"
}
</script>
<template>
<div class="main-image-container flex">
<div
@@ -64,22 +45,40 @@ const topImageTitleMap: Record<TopImageType, string> = {
</div>
</div>
</template>
<script setup lang="ts">
import type { TopImageType } from "../types"
defineProps<{
images: Record<TopImageType, string | null>
}>()
const emit = defineEmits<{
(e: "crop", data: string | null, type: TopImageType): void
}>()
const topImageList: TopImageType[] = ["sketch", "mainProductImage", "cover"]
const topImageTitleMap: Record<TopImageType, string> = {
sketch: "SellerListEdit.sketch",
mainProductImage: "SellerListEdit.mainProductImage",
cover: "SellerListEdit.cover"
}
</script>
<style lang="less" scoped>
.c-svg {
.c-svg {
width: initial;
height: initial;
}
}
.required {
.required {
&::after {
content: "*";
color: #df2b2c;
margin-left: 0.4rem;
}
}
}
.main-image-container {
.main-image-container {
column-gap: 3.5rem;
.main-image-item {
@@ -153,5 +152,5 @@ const topImageTitleMap: Record<TopImageType, string> = {
}
}
}
}
}
</style>

View File

@@ -267,7 +267,7 @@
const handleClickCrop = (data: any, type: string, paramThree: any = []) => {
// 处理来自TopImageSection的调用: (data, type, list)
// 处理来自ApparelSketchList的调用: (data, type, index)
const index = typeof paramThree === 'number' ? paramThree : undefined
const index = typeof paramThree === "number" ? paramThree : undefined
const list = Array.isArray(paramThree) ? paramThree : []
// console.log(data, type)
@@ -376,9 +376,9 @@
})
if (item.mainProductImage) {
params.images.push({
category: 'main_product',
category: "main_product",
imageUrl: item.mainProductImage,
isSeleted:1
isSeleted: 1
})
}
item.prodImageList.forEach((item) => {
@@ -404,7 +404,7 @@
message.error("请先完成封面制作")
return
}
if (status === "publish" && !validatePublishRequired()) return
if (!validatePublishRequired()) return
await handleSaveForm(status)
if (status === "draft") {