feat: 商品详情编辑

This commit is contained in:
zc 2025-11-05 11:29:11 +08:00
parent 8fc07dd9cd
commit 7099bea338
6 changed files with 136 additions and 13 deletions

View File

@ -19,9 +19,10 @@ const useUserStore = defineStore('user', {
this.$reset() this.$reset()
}, },
// 登录 // 登录
onLogin(data: { tokenValue: string; tokenName: string }) { onLogin(data: { tokenValue: string; tokenName: string; userNickName: string }) {
this.tokenValue = data.tokenValue this.tokenValue = data.tokenValue
this.tokenName = data.tokenName this.tokenName = data.tokenName
this.userNickName = data.userNickName
}, },
// 获取用户信息(昵称、头像、角色集合、权限集合) // 获取用户信息(昵称、头像、角色集合、权限集合)
getUserRoleInfo() { getUserRoleInfo() {

View File

@ -5,6 +5,8 @@ interface PreviewItem {
type: 'image' | 'video' type: 'image' | 'video'
} }
export const curPropertyTypeImage = ref(null)
// 选择资源库图片 // 选择资源库图片
export const useImportFile = (fileList: Ref<PreviewItem[]>) => { export const useImportFile = (fileList: Ref<PreviewItem[]>) => {
const showFileExplorer = ref(false) const showFileExplorer = ref(false)
@ -18,10 +20,15 @@ export const useImportFile = (fileList: Ref<PreviewItem[]>) => {
// 处理文件选择 // 处理文件选择
const handleChooseResourceFileCallback = (files: PreviewItem[] = []) => { const handleChooseResourceFileCallback = (files: PreviewItem[] = []) => {
handleChooseFiles(curFileType.value, files, fileList.value) if (curPropertyTypeImage.value) {
curPropertyTypeImage.value.imageUrl = files[0].resourceUrl
} else {
handleChooseFiles(curFileType.value, files, fileList.value)
}
} }
const onClickChooseResourceBtn = (type: 'mainImageUrl' | 'videoUrl' | 'subImageUrl') => { const onClickChooseResourceBtn = (type: 'mainImageUrl' | 'videoUrl' | 'subImageUrl') => {
curPropertyTypeImage.value = null
curFileType.value = type curFileType.value = type
showFileExplorer.value = true showFileExplorer.value = true
} }

View File

@ -21,7 +21,11 @@ export const handleGetDialogData = (
result.adminCategoryId = defaultCheckedNodes.value.admin.slice(-1)[0] result.adminCategoryId = defaultCheckedNodes.value.admin.slice(-1)[0]
defaultCheckedNodes.value.app = [data.appCategoryId1, data.appCategoryId2] defaultCheckedNodes.value.app = [data.appCategoryId1, data.appCategoryId2]
result.isTest = data.isTest result.isTest = data.isTest
result.isNew = data.isNew
result.isFlash = data.isFlash
result.title = data.title result.title = data.title
result.frontPage = data.frontPage
result.status = data.status
result.feature1 = data.feature1 result.feature1 = data.feature1
result.feature2 = data.feature2 result.feature2 = data.feature2
result.htmlContent = data.vvProductDetailList.find((item: any) => item.type === 2)?.detail || '' result.htmlContent = data.vvProductDetailList.find((item: any) => item.type === 2)?.detail || ''
@ -86,7 +90,7 @@ export const handleGetNewAdminCategoryData = (data: any) => {
// 编辑的时候对sku进行重组 // 编辑的时候对sku进行重组
export const handleGetNewSkuList = (data: any) => { export const handleGetNewSkuList = (data: any) => {
return data.map((item: any) => { const arr = data.map((item: any) => {
let categoryPropertyNames = '' let categoryPropertyNames = ''
let name = '' let name = ''
item.vvSkuPropertyValueList.forEach((child: any, index: number) => { item.vvSkuPropertyValueList.forEach((child: any, index: number) => {
@ -101,4 +105,45 @@ export const handleGetNewSkuList = (data: any) => {
categoryPropertyNames categoryPropertyNames
} }
}) })
return sortByPrefix(arr)
}
/**
* name
* @param arr
* @returns
*/
export const sortByPrefix = <T extends { name?: string }>(arr: T[]): T[] => {
if (!Array.isArray(arr) || arr.length === 0) {
return arr
}
// 计算前缀:如果 name 包含 "-",取最后一个 "-" 之前的部分作为前缀
const getPrefix = (name: string): string => {
if (!name) return ''
// 找到最后一个 "-" 的位置,取之前的部分作为前缀
const lastDashIndex = name.lastIndexOf('-')
if (lastDashIndex > 0) {
return name.substring(0, lastDashIndex + 1) // 包含 "-" 分隔符
}
// 如果没有 "-",返回整个字符串
return name
}
// 创建副本并排序
return [...arr].sort((a, b) => {
const prefixA = getPrefix(a.name || '')
const prefixB = getPrefix(b.name || '')
// 先按前缀排序
const prefixCompare = prefixA.localeCompare(prefixB, 'zh-CN')
if (prefixCompare !== 0) {
return prefixCompare
}
// 如果前缀相同,再按完整的 name 排序
return (a.name || '').localeCompare(b.name || '', 'zh-CN')
})
} }

View File

@ -77,9 +77,6 @@
> >
</el-tree-select> </el-tree-select>
</el-form-item> </el-form-item>
<el-form-item label="测试商品">
<el-switch v-model="$dialog.data.isTest" active-value="1" inactive-value="0" />
</el-form-item>
<el-form-item label="特色"> <el-form-item label="特色">
<div class="flex"> <div class="flex">
<el-input <el-input
@ -94,6 +91,22 @@
></el-input> ></el-input>
</div> </div>
</el-form-item> </el-form-item>
<br />
<el-form-item label="测试商品">
<el-switch v-model="$dialog.data.isTest" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="新品">
<el-switch v-model="$dialog.data.isNew" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="限时秒杀">
<el-switch v-model="$dialog.data.isFlash" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="上线">
<el-switch v-model="$dialog.data.status" active-value="online" inactive-value="down" />
</el-form-item>
<el-form-item label="加入首页">
<el-switch v-model="$dialog.data.frontPage" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item> <el-form-item>
<el-popover <el-popover
:visible="visiblePropertyLine" :visible="visiblePropertyLine"
@ -146,10 +159,47 @@
closable closable
effect="plain" effect="plain"
:type="colors[index % 4]" :type="colors[index % 4]"
class="mr-1" class="mr-1 property-type-tag"
size="large"
@click="onClickPropertyValue(item, child)" @click="onClickPropertyValue(item, child)"
@close="onDeletePropertyValue(index, child.id)" @close="onDeletePropertyValue(index, child.id)"
>{{ child.categoryPropertyValue }}</el-tag >
<el-popover title="" placement="top-start" :width="280">
<template #reference>
<el-icon
color="#999"
class="mr-1"
v-if="!child.imageUrl"
@click.stop="onChangePropertyTypeImage(child)"
><Picture
/></el-icon>
<img
v-else
:src="child.imageUrl"
alt=""
class="w-6 h-6 mr-1"
@click.stop="onChangePropertyTypeImage(child)"
/>
</template>
<div>
<file-upload-btn
class="inline-block"
size="small"
accept="image/*"
@change="(val) => (child.imageUrl = val[0].resourceUrl)"
/>
<el-button
type="success"
size="small"
class="ml-2"
@click="(showFileExplorer = true) && (curPropertyTypeImage = child)"
>资源库导入</el-button
>
<el-button type="danger" size="small" class="ml-2" @click="child.imageUrl = ''"
>清空</el-button
>
</div> </el-popover
><span>{{ child.categoryPropertyValue }}</span></el-tag
> >
</div> </div>
<el-input <el-input
@ -233,7 +283,8 @@ import {
defaultAdminCategoryTreeProps, defaultAdminCategoryTreeProps,
defaultCheckedNodes, defaultCheckedNodes,
fileList, fileList,
TSkuList TSkuList,
onChangePropertyTypeImage
} from './use-method' } from './use-method'
import { import {
handleGetDialogData, handleGetDialogData,
@ -258,10 +309,11 @@ import categoryConfig from './category-config.vue'
import wanEditor from './editor.vue' import wanEditor from './editor.vue'
import fileUploadBtn from '@/components/FileUploadBtn/index.vue' import fileUploadBtn from '@/components/FileUploadBtn/index.vue'
import resourceReview from '@/components/ResourceReview/index.vue' import resourceReview from '@/components/ResourceReview/index.vue'
import { useImportFile, handleChooseFiles } from './choose-file-method' import { curPropertyTypeImage, useImportFile, handleChooseFiles } from './choose-file-method'
import FileExplorerDialog from '@/components/FileExplorerDialog/index.vue' import FileExplorerDialog from '@/components/FileExplorerDialog/index.vue'
import { Atrans$DialogRes } from '@/utils/page/config' import { Atrans$DialogRes } from '@/utils/page/config'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { Picture } from '@element-plus/icons-vue'
const route = useRoute() const route = useRoute()
const $dialog = ref<Atrans$DialogRes>({} as Atrans$DialogRes) const $dialog = ref<Atrans$DialogRes>({} as Atrans$DialogRes)
@ -275,7 +327,11 @@ const init = async () => {
appCategoryId: '', appCategoryId: '',
adminCategoryId: '', adminCategoryId: '',
isTest: 0, isTest: 0,
title: '' isNew: 0,
isFlash: 0,
title: '',
frontPage: 0,
status: 'down'
} }
adminCategoryData.value = [] adminCategoryData.value = []
skuList.value = [] skuList.value = []
@ -378,5 +434,11 @@ init()
} }
color: #666; color: #666;
} }
.property-type-tag {
:deep(.el-tag__content) {
display: flex;
align-items: center;
}
}
} }
</style> </style>

View File

@ -319,7 +319,7 @@ export const generateSkuList = (categories: CategoryDataMock): TSkuList[] => {
return combos.map((combo) => { return combos.map((combo) => {
const serialNo = combo.map((opt) => opt.id.toString()).join('-') const serialNo = combo.map((opt) => opt.id.toString()).join('-')
const name = combo.map((opt) => String(opt.categoryPropertyValue)).join('-') const name = combo.map((opt) => String(opt.categoryPropertyValue)).join('-')
const categoryPropertyNames = combo.map((opt) => String(opt.categoryPropertyValue)).join('-') const categoryPropertyNames = combo.map((opt) => String(opt.categoryPropertyName)).join('-')
const originArray = combo const originArray = combo
return { return {
serialNo, serialNo,
@ -352,3 +352,7 @@ const generateAddSkuList = (
}) })
return generateSkuList(filterCategoryData) return generateSkuList(filterCategoryData)
} }
export const onChangePropertyTypeImage = (child: any) => {
console.log(1111, child)
}

View File

@ -21,7 +21,11 @@ const handleLogin = () => {
if (valid) { if (valid) {
api.login.login.post(loginData).then((res) => { api.login.login.post(loginData).then((res) => {
router.push({ path: '/home' }) router.push({ path: '/home' })
user.onLogin({ tokenValue: res.data.token, tokenName: 'mmToken' }) user.onLogin({
tokenValue: res.data.token,
tokenName: 'mmToken',
userNickName: res.data.username
})
}) })
} else { } else {
return false return false