fix: 完善 SPU 查看详情
This commit is contained in:
parent
e555977757
commit
39c92cb944
@ -37,7 +37,7 @@ export interface Spu {
|
|||||||
brandId?: number | null // 商品品牌编号
|
brandId?: number | null // 商品品牌编号
|
||||||
specType?: boolean // 商品规格
|
specType?: boolean // 商品规格
|
||||||
subCommissionType?: boolean // 分销类型
|
subCommissionType?: boolean // 分销类型
|
||||||
skus: Sku[] // sku数组
|
skus?: Sku[] // sku数组
|
||||||
description?: string // 商品详情
|
description?: string // 商品详情
|
||||||
sort?: number // 商品排序
|
sort?: number // 商品排序
|
||||||
giveIntegral?: number // 赠送积分
|
giveIntegral?: number // 赠送积分
|
||||||
|
@ -379,6 +379,19 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
title: '编辑商品',
|
title: '编辑商品',
|
||||||
activeMenu: '/product/product-spu'
|
activeMenu: '/product/product-spu'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'productSpuDetail/:spuId(\\d+)',
|
||||||
|
component: () => import('@/views/mall/product/spu/addForm.vue'),
|
||||||
|
name: 'productSpuDetail',
|
||||||
|
meta: {
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
canTo: true,
|
||||||
|
icon: 'ep:view',
|
||||||
|
title: '商品详情',
|
||||||
|
activeMenu: '/product/product-spu'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -312,26 +312,30 @@ export const handleTree2 = (data, id, parentId, children, rootId) => {
|
|||||||
* @param nodeId 需要判断在什么层级的数据
|
* @param nodeId 需要判断在什么层级的数据
|
||||||
* @param level 检查的级别, 默认检查到二级
|
* @param level 检查的级别, 默认检查到二级
|
||||||
*/
|
*/
|
||||||
export const checkSelectedNode = (tree: any[], nodeId, level = 2) => {
|
export const checkSelectedNode = (tree: any[], nodeId: any, level = 2): boolean => {
|
||||||
if (typeof tree === 'undefined' || !Array.isArray(tree) || tree.length === 0) {
|
if (typeof tree === 'undefined' || !Array.isArray(tree) || tree.length === 0) {
|
||||||
console.warn('tree must be an array')
|
console.warn('tree must be an array')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验是否是一级节点
|
// 校验是否是一级节点
|
||||||
if (tree.some((item) => item.id === nodeId)) {
|
if (tree.some((item) => item.id === nodeId)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归计数
|
// 递归计数
|
||||||
let count = 1
|
let count = 1
|
||||||
|
|
||||||
// 深层次校验
|
// 深层次校验
|
||||||
function performAThoroughValidation(arr) {
|
function performAThoroughValidation(arr: any[]): boolean {
|
||||||
count += 1
|
count += 1
|
||||||
for (const item of arr) {
|
for (const item of arr) {
|
||||||
if (item.id === nodeId) {
|
if (item.id === nodeId) {
|
||||||
return true
|
return true
|
||||||
} else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
|
} else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
|
||||||
performAThoroughValidation(item.children)
|
if (performAThoroughValidation(item.children)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -341,11 +345,15 @@ export const checkSelectedNode = (tree: any[], nodeId, level = 2) => {
|
|||||||
count = 1
|
count = 1
|
||||||
if (performAThoroughValidation(item.children)) {
|
if (performAThoroughValidation(item.children)) {
|
||||||
// 找到后对比是否是期望的层级
|
// 找到后对比是否是期望的层级
|
||||||
if (count >= level) return true
|
if (count >= level) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取节点的完整结构
|
* 获取节点的完整结构
|
||||||
* @param tree 树数据
|
* @param tree 树数据
|
||||||
@ -369,7 +377,10 @@ export const treeToString = (tree: any[], nodeId) => {
|
|||||||
str += `/${item.name}`
|
str += `/${item.name}`
|
||||||
return true
|
return true
|
||||||
} else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
|
} else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
|
||||||
performAThoroughValidation(item.children)
|
str += `/${item.name}`
|
||||||
|
if (performAThoroughValidation(item.children)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<BasicInfoForm
|
<BasicInfoForm
|
||||||
ref="basicInfoRef"
|
ref="basicInfoRef"
|
||||||
v-model:activeName="activeName"
|
v-model:activeName="activeName"
|
||||||
|
:is-detail="isDetail"
|
||||||
:propFormData="formData"
|
:propFormData="formData"
|
||||||
/>
|
/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
@ -12,6 +13,7 @@
|
|||||||
<DescriptionForm
|
<DescriptionForm
|
||||||
ref="descriptionRef"
|
ref="descriptionRef"
|
||||||
v-model:activeName="activeName"
|
v-model:activeName="activeName"
|
||||||
|
:is-detail="isDetail"
|
||||||
:propFormData="formData"
|
:propFormData="formData"
|
||||||
/>
|
/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
@ -19,6 +21,7 @@
|
|||||||
<OtherSettingsForm
|
<OtherSettingsForm
|
||||||
ref="otherSettingsRef"
|
ref="otherSettingsRef"
|
||||||
v-model:activeName="activeName"
|
v-model:activeName="activeName"
|
||||||
|
:is-detail="isDetail"
|
||||||
:propFormData="formData"
|
:propFormData="formData"
|
||||||
/>
|
/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
@ -42,11 +45,12 @@ import { convertToInteger, formatToFraction } from '@/utils'
|
|||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const { push, currentRoute } = useRouter() // 路由
|
const { push, currentRoute } = useRouter() // 路由
|
||||||
const { params } = useRoute() // 查询参数
|
const { params, name } = useRoute() // 查询参数
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
const { delView } = useTagsViewStore() // 视图操作
|
||||||
|
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
const activeName = ref('basicInfo') // Tag 激活的窗口
|
const activeName = ref('basicInfo') // Tag 激活的窗口
|
||||||
|
const isDetail = ref(false) // 是否查看详情
|
||||||
const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>() // 商品信息Ref
|
const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>() // 商品信息Ref
|
||||||
const descriptionRef = ref<ComponentRef<typeof DescriptionForm>>() // 商品详情Ref
|
const descriptionRef = ref<ComponentRef<typeof DescriptionForm>>() // 商品详情Ref
|
||||||
const otherSettingsRef = ref<ComponentRef<typeof OtherSettingsForm>>() // 其他设置Ref
|
const otherSettingsRef = ref<ComponentRef<typeof OtherSettingsForm>>() // 其他设置Ref
|
||||||
@ -90,12 +94,13 @@ const formData = ref<ProductSpuApi.Spu>({
|
|||||||
|
|
||||||
/** 获得详情 */
|
/** 获得详情 */
|
||||||
const getDetail = async () => {
|
const getDetail = async () => {
|
||||||
|
console.log(name)
|
||||||
const id = params.spuId as number
|
const id = params.spuId as number
|
||||||
if (id) {
|
if (id) {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res = (await ProductSpuApi.getSpu(id)) as ProductSpuApi.Spu
|
const res = (await ProductSpuApi.getSpu(id)) as ProductSpuApi.Spu
|
||||||
res.skus.forEach((item) => {
|
res.skus!.forEach((item) => {
|
||||||
// 回显价格分转元
|
// 回显价格分转元
|
||||||
item.price = formatToFraction(item.price)
|
item.price = formatToFraction(item.price)
|
||||||
item.marketPrice = formatToFraction(item.marketPrice)
|
item.marketPrice = formatToFraction(item.marketPrice)
|
||||||
@ -123,7 +128,7 @@ const submitForm = async () => {
|
|||||||
// 深拷贝一份, 这样最终 server 端不满足,不需要恢复,
|
// 深拷贝一份, 这样最终 server 端不满足,不需要恢复,
|
||||||
const deepCopyFormData = cloneDeep(unref(formData.value))
|
const deepCopyFormData = cloneDeep(unref(formData.value))
|
||||||
// 兜底处理 sku 空数据
|
// 兜底处理 sku 空数据
|
||||||
formData.value.skus.forEach((sku) => {
|
formData.value.skus!.forEach((sku) => {
|
||||||
// 因为是空数据这里判断一下商品条码是否为空就行
|
// 因为是空数据这里判断一下商品条码是否为空就行
|
||||||
if (sku.barCode === '') {
|
if (sku.barCode === '') {
|
||||||
const index = deepCopyFormData.skus.findIndex(
|
const index = deepCopyFormData.skus.findIndex(
|
||||||
@ -171,7 +176,6 @@ const close = () => {
|
|||||||
delView(unref(currentRoute))
|
delView(unref(currentRoute))
|
||||||
push('/product/product-spu')
|
push('/product/product-spu')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getDetail()
|
await getDetail()
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form ref="productSpuBasicInfoRef" :model="formData" :rules="rules" label-width="120px">
|
<el-form
|
||||||
|
v-if="!isDetail"
|
||||||
|
ref="productSpuBasicInfoRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="商品名称" prop="name">
|
<el-form-item label="商品名称" prop="name">
|
||||||
@ -115,18 +121,72 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
<ProductAttributesAddForm ref="attributesAddFormRef" :propertyList="propertyList" />
|
<ProductAttributesAddForm ref="attributesAddFormRef" :propertyList="propertyList" />
|
||||||
|
<!-- 详情跟表单放在一块可以共用已有功能,再抽离成组件有点过度封装的感觉 -->
|
||||||
|
<Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
|
||||||
|
<template #categoryId="{ row }"> {{ categoryString(row.categoryId) }}</template>
|
||||||
|
<template #brandId="{ row }">
|
||||||
|
{{ brandList.find((item) => item.id === row.brandId)?.name }}
|
||||||
|
</template>
|
||||||
|
<template #specType="{ row }">
|
||||||
|
{{ row.specType ? '多规格' : '单规格' }}
|
||||||
|
</template>
|
||||||
|
<template #subCommissionType="{ row }">
|
||||||
|
{{ row.subCommissionType ? '自行设置' : '默认设置' }}
|
||||||
|
</template>
|
||||||
|
<template #picUrl="{ row }">
|
||||||
|
<el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
|
||||||
|
</template>
|
||||||
|
<template #sliderPicUrls="{ row }">
|
||||||
|
<el-image
|
||||||
|
v-for="(item, index) in row.sliderPicUrls"
|
||||||
|
:key="index"
|
||||||
|
:src="item.url"
|
||||||
|
class="w-60px h-60px mr-10px"
|
||||||
|
@click="imagePreview(row.sliderPicUrls)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #skus>
|
||||||
|
<SkuList
|
||||||
|
ref="skuDetailListRef"
|
||||||
|
:is-detail="isDetail"
|
||||||
|
:prop-form-data="formData"
|
||||||
|
:propertyList="propertyList"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Descriptions>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="ProductSpuBasicInfoForm" setup>
|
<script lang="ts" name="ProductSpuBasicInfoForm" setup>
|
||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
import { copyValueToTarget } from '@/utils'
|
import { copyValueToTarget } from '@/utils'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { checkSelectedNode, defaultProps, handleTree } from '@/utils/tree'
|
import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree'
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import type { Spu } from '@/api/mall/product/spu'
|
import type { Spu } from '@/api/mall/product/spu'
|
||||||
import { UploadImg, UploadImgs } from '@/components/UploadFile'
|
import { UploadImg, UploadImgs } from '@/components/UploadFile'
|
||||||
import { ProductAttributes, ProductAttributesAddForm, SkuList } from './index'
|
import { ProductAttributes, ProductAttributesAddForm, SkuList } from './index'
|
||||||
|
import { basicInfoSchema } from './spu.data'
|
||||||
|
import { createImageViewer } from '@/components/ImageViewer'
|
||||||
import * as ProductCategoryApi from '@/api/mall/product/category'
|
import * as ProductCategoryApi from '@/api/mall/product/category'
|
||||||
import { getSimpleBrandList } from '@/api/mall/product/brand'
|
import { getSimpleBrandList } from '@/api/mall/product/brand'
|
||||||
|
import { isArray } from '@/utils/is'
|
||||||
|
|
||||||
|
// ====== 商品详情相关操作 ======
|
||||||
|
const { allSchemas } = useCrudSchemas(basicInfoSchema)
|
||||||
|
/** 商品图预览 */
|
||||||
|
const imagePreview = (args) => {
|
||||||
|
const urlList = []
|
||||||
|
if (isArray(args)) {
|
||||||
|
args.forEach((item) => {
|
||||||
|
urlList.push(item.url)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
urlList.push(args)
|
||||||
|
}
|
||||||
|
createImageViewer({
|
||||||
|
urlList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// ====== end ======
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
@ -135,7 +195,8 @@ const props = defineProps({
|
|||||||
type: Object as PropType<Spu>,
|
type: Object as PropType<Spu>,
|
||||||
default: () => {}
|
default: () => {}
|
||||||
},
|
},
|
||||||
activeName: propTypes.string.def('')
|
activeName: propTypes.string.def(''),
|
||||||
|
isDetail: propTypes.bool.def(false) // 是否作为详情组件
|
||||||
})
|
})
|
||||||
const attributesAddFormRef = ref() // 添加商品属性表单
|
const attributesAddFormRef = ref() // 添加商品属性表单
|
||||||
const productSpuBasicInfoRef = ref() // 表单 Ref
|
const productSpuBasicInfoRef = ref() // 表单 Ref
|
||||||
@ -149,11 +210,11 @@ const formData = reactive<Spu>({
|
|||||||
name: '', // 商品名称
|
name: '', // 商品名称
|
||||||
categoryId: null, // 商品分类
|
categoryId: null, // 商品分类
|
||||||
keyword: '', // 关键字
|
keyword: '', // 关键字
|
||||||
unit: '', // 单位
|
unit: null, // 单位
|
||||||
picUrl: '', // 商品封面图
|
picUrl: '', // 商品封面图
|
||||||
sliderPicUrls: [], // 商品轮播图
|
sliderPicUrls: [], // 商品轮播图
|
||||||
introduction: '', // 商品简介
|
introduction: '', // 商品简介
|
||||||
deliveryTemplateId: 1, // 运费模版
|
deliveryTemplateId: null, // 运费模版
|
||||||
brandId: null, // 商品品牌
|
brandId: null, // 商品品牌
|
||||||
specType: false, // 商品规格
|
specType: false, // 商品规格
|
||||||
subCommissionType: false, // 分销类型
|
subCommissionType: false, // 分销类型
|
||||||
@ -273,9 +334,16 @@ const categoryList = ref([]) // 分类树
|
|||||||
const nodeClick = () => {
|
const nodeClick = () => {
|
||||||
if (!checkSelectedNode(categoryList.value, formData.categoryId)) {
|
if (!checkSelectedNode(categoryList.value, formData.categoryId)) {
|
||||||
formData.categoryId = null
|
formData.categoryId = null
|
||||||
message.warning('必须选择二级节点!!')
|
message.warning('必须选择二级及以下节点!!')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取分类的节点的完整结构
|
||||||
|
* @param categoryId 分类id
|
||||||
|
*/
|
||||||
|
const categoryString = (categoryId) => {
|
||||||
|
return treeToString(categoryList.value, categoryId)
|
||||||
|
}
|
||||||
const brandList = ref([]) // 精简商品品牌列表
|
const brandList = ref([]) // 精简商品品牌列表
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 获得分类树
|
// 获得分类树
|
||||||
|
@ -1,10 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form ref="descriptionFormRef" :model="formData" :rules="rules" label-width="120px">
|
<el-form
|
||||||
|
v-if="!isDetail"
|
||||||
|
ref="descriptionFormRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
<!--富文本编辑器组件-->
|
<!--富文本编辑器组件-->
|
||||||
<el-form-item label="商品详情" prop="description">
|
<el-form-item label="商品详情" prop="description">
|
||||||
<Editor v-model:modelValue="formData.description" />
|
<Editor v-model:modelValue="formData.description" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<Descriptions
|
||||||
|
v-if="isDetail"
|
||||||
|
:data="formData"
|
||||||
|
:schema="allSchemas.detailSchema"
|
||||||
|
class="descriptionFormDescriptions"
|
||||||
|
>
|
||||||
|
<!-- 展示 HTML 内容 -->
|
||||||
|
<template #description="{ row }">
|
||||||
|
<div style="width: 600px" v-html="row.description"></div>
|
||||||
|
</template>
|
||||||
|
</Descriptions>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="DescriptionForm" setup>
|
<script lang="ts" name="DescriptionForm" setup>
|
||||||
import type { Spu } from '@/api/mall/product/spu'
|
import type { Spu } from '@/api/mall/product/spu'
|
||||||
@ -12,6 +29,9 @@ import { Editor } from '@/components/Editor'
|
|||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { copyValueToTarget } from '@/utils'
|
import { copyValueToTarget } from '@/utils'
|
||||||
|
import { descriptionSchema } from './spu.data'
|
||||||
|
|
||||||
|
const { allSchemas } = useCrudSchemas(descriptionSchema)
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -19,7 +39,8 @@ const props = defineProps({
|
|||||||
type: Object as PropType<Spu>,
|
type: Object as PropType<Spu>,
|
||||||
default: () => {}
|
default: () => {}
|
||||||
},
|
},
|
||||||
activeName: propTypes.string.def('')
|
activeName: propTypes.string.def(''),
|
||||||
|
isDetail: propTypes.bool.def(false) // 是否作为详情组件
|
||||||
})
|
})
|
||||||
const descriptionFormRef = ref() // 表单Ref
|
const descriptionFormRef = ref() // 表单Ref
|
||||||
const formData = ref<Spu>({
|
const formData = ref<Spu>({
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form ref="otherSettingsFormRef" :model="formData" :rules="rules" label-width="120px">
|
<el-form
|
||||||
|
v-if="!isDetail"
|
||||||
|
ref="otherSettingsFormRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
@ -50,12 +56,38 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
|
||||||
|
<template #recommendHot="{ row }">
|
||||||
|
{{ row.recommendHot ? '是' : '否' }}
|
||||||
|
</template>
|
||||||
|
<template #recommendBenefit="{ row }">
|
||||||
|
{{ row.recommendBenefit ? '是' : '否' }}
|
||||||
|
</template>
|
||||||
|
<template #recommendBest="{ row }">
|
||||||
|
{{ row.recommendBest ? '是' : '否' }}
|
||||||
|
</template>
|
||||||
|
<template #recommendNew="{ row }">
|
||||||
|
{{ row.recommendNew ? '是' : '否' }}
|
||||||
|
</template>
|
||||||
|
<template #recommendGood="{ row }">
|
||||||
|
{{ row.recommendGood ? '是' : '否' }}
|
||||||
|
</template>
|
||||||
|
<template #activityOrders>
|
||||||
|
<el-tag>默认</el-tag>
|
||||||
|
<el-tag class="ml-2" type="success">秒杀</el-tag>
|
||||||
|
<el-tag class="ml-2" type="info">砍价</el-tag>
|
||||||
|
<el-tag class="ml-2" type="warning">拼团</el-tag>
|
||||||
|
</template>
|
||||||
|
</Descriptions>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="OtherSettingsForm" setup>
|
<script lang="ts" name="OtherSettingsForm" setup>
|
||||||
import type { Spu } from '@/api/mall/product/spu'
|
import type { Spu } from '@/api/mall/product/spu'
|
||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { copyValueToTarget } from '@/utils'
|
import { copyValueToTarget } from '@/utils'
|
||||||
|
import { otherSettingsSchema } from './spu.data'
|
||||||
|
|
||||||
|
const { allSchemas } = useCrudSchemas(otherSettingsSchema)
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
@ -64,7 +96,8 @@ const props = defineProps({
|
|||||||
type: Object as PropType<Spu>,
|
type: Object as PropType<Spu>,
|
||||||
default: () => {}
|
default: () => {}
|
||||||
},
|
},
|
||||||
activeName: propTypes.string.def('')
|
activeName: propTypes.string.def(''),
|
||||||
|
isDetail: propTypes.bool.def(false) // 是否作为详情组件
|
||||||
})
|
})
|
||||||
|
|
||||||
const otherSettingsFormRef = ref() // 表单Ref
|
const otherSettingsFormRef = ref() // 表单Ref
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-table
|
<el-table
|
||||||
|
v-if="!isDetail"
|
||||||
:data="isBatch ? skuList : formData!.skus"
|
:data="isBatch ? skuList : formData!.skus"
|
||||||
border
|
border
|
||||||
class="tabNumWidth"
|
class="tabNumWidth"
|
||||||
@ -21,8 +22,8 @@
|
|||||||
min-width="120"
|
min-width="120"
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<!-- TODO puhui999:展示成蓝色,有点区分度哈 fix: 字体加粗,颜色使用 #99a9bf 蓝色有点不好看哈哈-->
|
<!-- TODO puhui999:展示成蓝色,有点区分度哈 fix-->
|
||||||
<span style="font-weight: bold; color: #99a9bf">
|
<span style="font-weight: bold; color: #40aaff">
|
||||||
{{ row.properties[index]?.valueName }}
|
{{ row.properties[index]?.valueName }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
@ -108,6 +109,84 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<el-table
|
||||||
|
v-if="isDetail"
|
||||||
|
:data="formData!.skus"
|
||||||
|
border
|
||||||
|
max-height="500"
|
||||||
|
size="small"
|
||||||
|
style="width: 99%"
|
||||||
|
>
|
||||||
|
<el-table-column align="center" label="图片" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template v-if="formData!.specType && !isBatch">
|
||||||
|
<!-- 根据商品属性动态添加 -->
|
||||||
|
<el-table-column
|
||||||
|
v-for="(item, index) in tableHeaders"
|
||||||
|
:key="index"
|
||||||
|
:label="item.label"
|
||||||
|
align="center"
|
||||||
|
min-width="80"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<!-- TODO puhui999:展示成蓝色,有点区分度哈 fix-->
|
||||||
|
<span style="font-weight: bold; color: #40aaff">
|
||||||
|
{{ row.properties[index]?.valueName }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column align="center" label="商品条码" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.barCode }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="销售价(元)" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.price }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="市场价(元)" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.marketPrice }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="成本价(元)" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.costPrice }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="库存" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.stock }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="重量(kg)" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.weight }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="体积(m^3)" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.volume }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template v-if="formData!.subCommissionType">
|
||||||
|
<el-table-column align="center" label="一级返佣(元)" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.subCommissionFirstPrice }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="二级返佣(元)" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.subCommissionSecondPrice }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="SkuList" setup>
|
<script lang="ts" name="SkuList" setup>
|
||||||
import { PropType, Ref } from 'vue'
|
import { PropType, Ref } from 'vue'
|
||||||
@ -115,6 +194,7 @@ import { copyValueToTarget } from '@/utils'
|
|||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { UploadImg } from '@/components/UploadFile'
|
import { UploadImg } from '@/components/UploadFile'
|
||||||
import type { Property, Sku, Spu } from '@/api/mall/product/spu'
|
import type { Property, Sku, Spu } from '@/api/mall/product/spu'
|
||||||
|
import { createImageViewer } from '@/components/ImageViewer'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
propFormData: {
|
propFormData: {
|
||||||
@ -125,7 +205,8 @@ const props = defineProps({
|
|||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
isBatch: propTypes.bool.def(false) // 是否作为批量操作组件
|
isBatch: propTypes.bool.def(false), // 是否作为批量操作组件
|
||||||
|
isDetail: propTypes.bool.def(false) // 是否作为 sku 详情组件
|
||||||
})
|
})
|
||||||
const formData: Ref<Spu | undefined> = ref<Spu>() // 表单数据
|
const formData: Ref<Spu | undefined> = ref<Spu>() // 表单数据
|
||||||
const skuList = ref<Sku[]>([
|
const skuList = ref<Sku[]>([
|
||||||
@ -143,20 +224,27 @@ const skuList = ref<Sku[]>([
|
|||||||
}
|
}
|
||||||
]) // 批量添加时的临时数据
|
]) // 批量添加时的临时数据
|
||||||
|
|
||||||
|
/** 商品图预览 */
|
||||||
|
const imagePreview = (imgUrl: string) => {
|
||||||
|
createImageViewer({
|
||||||
|
urlList: [imgUrl]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/** 批量添加 */
|
/** 批量添加 */
|
||||||
const batchAdd = () => {
|
const batchAdd = () => {
|
||||||
formData.value!.skus.forEach((item) => {
|
formData.value!.skus!.forEach((item) => {
|
||||||
copyValueToTarget(item, skuList.value[0])
|
copyValueToTarget(item, skuList.value[0])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除 sku */
|
/** 删除 sku */
|
||||||
const deleteSku = (row) => {
|
const deleteSku = (row) => {
|
||||||
const index = formData.value!.skus.findIndex(
|
const index = formData.value!.skus!.findIndex(
|
||||||
// 直接把列表转成字符串比较
|
// 直接把列表转成字符串比较
|
||||||
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
|
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
|
||||||
)
|
)
|
||||||
formData.value!.skus.splice(index, 1)
|
formData.value!.skus!.splice(index, 1)
|
||||||
}
|
}
|
||||||
const tableHeaders = ref<{ prop: string; label: string }[]>([]) // 多属性表头
|
const tableHeaders = ref<{ prop: string; label: string }[]>([]) // 多属性表头
|
||||||
/**
|
/**
|
||||||
@ -221,13 +309,13 @@ const generateTableData = (propertyList: any[]) => {
|
|||||||
subCommissionSecondPrice: 0
|
subCommissionSecondPrice: 0
|
||||||
}
|
}
|
||||||
// 如果存在属性相同的 sku 则不做处理
|
// 如果存在属性相同的 sku 则不做处理
|
||||||
const index = formData.value!.skus.findIndex(
|
const index = formData.value!.skus!.findIndex(
|
||||||
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
|
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
|
||||||
)
|
)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
formData.value!.skus.push(row)
|
formData.value!.skus!.push(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +324,7 @@ const generateTableData = (propertyList: any[]) => {
|
|||||||
*/
|
*/
|
||||||
const validateData = (propertyList: any[]) => {
|
const validateData = (propertyList: any[]) => {
|
||||||
const skuPropertyIds = []
|
const skuPropertyIds = []
|
||||||
formData.value!.skus.forEach((sku) =>
|
formData.value!.skus!.forEach((sku) =>
|
||||||
sku.properties
|
sku.properties
|
||||||
?.map((property) => property.propertyId)
|
?.map((property) => property.propertyId)
|
||||||
.forEach((propertyId) => {
|
.forEach((propertyId) => {
|
||||||
|
105
src/views/mall/product/spu/components/spu.data.ts
Normal file
105
src/views/mall/product/spu/components/spu.data.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { CrudSchema } from '@/hooks/web/useCrudSchemas'
|
||||||
|
|
||||||
|
export const basicInfoSchema = reactive<CrudSchema[]>([
|
||||||
|
{
|
||||||
|
label: '商品名称',
|
||||||
|
field: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '关键字',
|
||||||
|
field: 'keyword'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '商品简介',
|
||||||
|
field: 'introduction'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '商品分类',
|
||||||
|
field: 'categoryId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '商品品牌',
|
||||||
|
field: 'brandId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '商品封面图',
|
||||||
|
field: 'picUrl'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '商品轮播图',
|
||||||
|
field: 'sliderPicUrls'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '商品视频',
|
||||||
|
field: 'videoUrl'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '单位',
|
||||||
|
field: 'unit',
|
||||||
|
dictType: DICT_TYPE.PRODUCT_UNIT
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '规格类型',
|
||||||
|
field: 'specType'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '分销类型',
|
||||||
|
field: 'subCommissionType'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '物流模版',
|
||||||
|
field: 'deliveryTemplateId'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '商品属性列表',
|
||||||
|
field: 'skus'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
export const descriptionSchema = reactive<CrudSchema[]>([
|
||||||
|
{
|
||||||
|
label: '商品详情',
|
||||||
|
field: 'description'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
export const otherSettingsSchema = reactive<CrudSchema[]>([
|
||||||
|
{
|
||||||
|
label: '商品排序',
|
||||||
|
field: 'sort'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '赠送积分',
|
||||||
|
field: 'giveIntegral'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '虚拟销量',
|
||||||
|
field: 'virtualSalesCount'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否热卖推荐',
|
||||||
|
field: 'recommendHot'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否优惠推荐',
|
||||||
|
field: 'recommendBenefit'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否精品推荐',
|
||||||
|
field: 'recommendBest'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否新品推荐',
|
||||||
|
field: 'recommendNew'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否优品推荐',
|
||||||
|
field: 'recommendGood'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '赠送的优惠劵',
|
||||||
|
field: 'giveCouponTemplateIds'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '活动显示排序',
|
||||||
|
field: 'activityOrders'
|
||||||
|
}
|
||||||
|
])
|
@ -171,8 +171,13 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" fixed="right" label="操作" min-width="200">
|
<el-table-column align="center" fixed="right" label="操作" min-width="200">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<!-- TODO @puhui999:【详情】,可以后面点做哈 -->
|
<!-- TODO @puhui999:【详情】,可以后面点做哈 fix-->
|
||||||
<el-button v-hasPermi="['product:spu:update']" link type="primary" @click="openDetail">
|
<el-button
|
||||||
|
v-hasPermi="['product:spu:update']"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="openDetail(row.id)"
|
||||||
|
>
|
||||||
详情
|
详情
|
||||||
</el-button>
|
</el-button>
|
||||||
<template v-if="queryParams.tabType === 4">
|
<template v-if="queryParams.tabType === 4">
|
||||||
@ -284,12 +289,14 @@ const getTabsCount = async () => {
|
|||||||
const queryParams = ref({
|
const queryParams = ref({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
tabType: 0
|
tabType: 0,
|
||||||
|
name: '',
|
||||||
|
categoryId: null
|
||||||
}) // 查询参数
|
}) // 查询参数
|
||||||
const queryFormRef = ref() // 搜索的表单Ref
|
const queryFormRef = ref() // 搜索的表单Ref
|
||||||
|
|
||||||
const handleTabClick = (tab: TabsPaneContext) => {
|
const handleTabClick = (tab: TabsPaneContext) => {
|
||||||
queryParams.value.tabType = tab.paneName
|
queryParams.value.tabType = tab.paneName as number
|
||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,8 +407,8 @@ const openForm = (id?: number) => {
|
|||||||
/**
|
/**
|
||||||
* 查看商品详情
|
* 查看商品详情
|
||||||
*/
|
*/
|
||||||
const openDetail = () => {
|
const openDetail = (id?: number) => {
|
||||||
message.alert('查看详情未完善!!!')
|
push('/product/productSpuDetail' + id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 导出按钮操作 */
|
/** 导出按钮操作 */
|
||||||
@ -436,12 +443,12 @@ const categoryString = (categoryId) => {
|
|||||||
return treeToString(categoryList.value, categoryId)
|
return treeToString(categoryList.value, categoryId)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 校验所选是否为二级节点
|
* 校验所选是否为二级及以下节点
|
||||||
*/
|
*/
|
||||||
const nodeClick = () => {
|
const nodeClick = () => {
|
||||||
if (!checkSelectedNode(categoryList.value, queryParams.value.categoryId)) {
|
if (!checkSelectedNode(categoryList.value, queryParams.value.categoryId)) {
|
||||||
queryParams.value.categoryId = null
|
queryParams.value.categoryId = null
|
||||||
message.warning('必须选择二级节点!!')
|
message.warning('必须选择二级及以下节点!!')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** 初始化 **/
|
/** 初始化 **/
|
||||||
|
Loading…
Reference in New Issue
Block a user