code review 商品管理的实现
This commit is contained in:
parent
4a965b8c14
commit
e92361ed40
@ -1,30 +1,38 @@
|
|||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
import type { SpuType } from './type/spuType'
|
import type { SpuType } from './type/spuType' // TODO @puhui999: type 和 api 一起放,简单一点哈~
|
||||||
|
|
||||||
// 获得spu列表
|
// TODO @puhui999:中英文之间有空格
|
||||||
export const getSpuList = (params: any) => {
|
|
||||||
|
// 获得spu列表 TODO @puhui999:这个是 getSpuPage 哈
|
||||||
|
export const getSpuList = (params: PageParam) => {
|
||||||
return request.get({ url: '/product/spu/page', params })
|
return request.get({ url: '/product/spu/page', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得spu列表tabsCount
|
// 获得spu列表tabsCount
|
||||||
export const getTabsCount = () => {
|
export const getTabsCount = () => {
|
||||||
return request.get({ url: '/product/spu/tabsCount' })
|
return request.get({ url: '/product/spu/tabsCount' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建商品spu
|
// 创建商品spu
|
||||||
export const createSpu = (data: SpuType) => {
|
export const createSpu = (data: SpuType) => {
|
||||||
return request.post({ url: '/product/spu/create', data })
|
return request.post({ url: '/product/spu/create', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新商品spu
|
// 更新商品spu
|
||||||
export const updateSpu = (data: SpuType) => {
|
export const updateSpu = (data: SpuType) => {
|
||||||
return request.put({ url: '/product/spu/update', data })
|
return request.put({ url: '/product/spu/update', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新商品spu status
|
// 更新商品spu status
|
||||||
export const updateStatus = (data: { id: number; status: number }) => {
|
export const updateStatus = (data: { id: number; status: number }) => {
|
||||||
return request.put({ url: '/product/spu/updateStatus', data })
|
return request.put({ url: '/product/spu/updateStatus', data })
|
||||||
}
|
}
|
||||||
// 获得商品spu
|
|
||||||
|
// 获得商品 spu
|
||||||
export const getSpu = (id: number) => {
|
export const getSpu = (id: number) => {
|
||||||
return request.get({ url: `/product/spu/get-detail?id=${id}` })
|
return request.get({ url: `/product/spu/get-detail?id=${id}` })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除商品Spu
|
// 删除商品Spu
|
||||||
export const deleteSpu = (id: number) => {
|
export const deleteSpu = (id: number) => {
|
||||||
return request.delete({ url: `/product/spu/delete?id=${id}` })
|
return request.delete({ url: `/product/spu/delete?id=${id}` })
|
||||||
|
@ -355,8 +355,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'productManagementAdd',
|
path: 'productManagementAdd', // TODO @puhui999:最好拆成 add 和 edit 两个路由;添加商品;修改商品
|
||||||
component: () => import('@/views/mall/product/management/addForm.vue'),
|
component: () => import('@/views/mall/product/spu/addForm.vue'),
|
||||||
name: 'ProductManagementAdd',
|
name: 'ProductManagementAdd',
|
||||||
meta: {
|
meta: {
|
||||||
noCache: true,
|
noCache: true,
|
||||||
|
@ -220,6 +220,7 @@ export const PayRefundStatusEnum = {
|
|||||||
name: '退款关闭'
|
name: '退款关闭'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品SPU枚举类
|
* 商品SPU枚举类
|
||||||
*/
|
*/
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// TODO @puhui999:这个方法,可以考虑放到 index.js
|
||||||
/**
|
/**
|
||||||
* 将值复制到目标对象,且以目标对象属性为准,例:target: {a:1} source:{a:2,b:3} 结果为:{a:2}
|
* 将值复制到目标对象,且以目标对象属性为准,例:target: {a:1} source:{a:2,b:3} 结果为:{a:2}
|
||||||
* @param target 目标对象
|
* @param target 目标对象
|
||||||
|
@ -37,7 +37,6 @@ import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components'
|
|||||||
import type { SpuType } from '@/api/mall/product/management/type/spuType' // 业务api
|
import type { SpuType } from '@/api/mall/product/management/type/spuType' // 业务api
|
||||||
import * as managementApi from '@/api/mall/product/management/spu'
|
import * as managementApi from '@/api/mall/product/management/spu'
|
||||||
import * as PropertyApi from '@/api/mall/product/property'
|
import * as PropertyApi from '@/api/mall/product/property'
|
||||||
|
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const { push, currentRoute } = useRouter() // 路由
|
const { push, currentRoute } = useRouter() // 路由
|
||||||
@ -69,7 +68,7 @@ const formData = ref<SpuType>({
|
|||||||
skus: [
|
skus: [
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* 商品价格,单位:分
|
* 商品价格,单位:分 TODO @puhui999:注释放在尾巴哈,简洁一点~
|
||||||
*/
|
*/
|
||||||
price: 0,
|
price: 0,
|
||||||
/**
|
/**
|
||||||
@ -120,6 +119,7 @@ const formData = ref<SpuType>({
|
|||||||
recommendNew: false, // 是否新品
|
recommendNew: false, // 是否新品
|
||||||
recommendGood: false // 是否优品
|
recommendGood: false // 是否优品
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 获得详情 */
|
/** 获得详情 */
|
||||||
const getDetail = async () => {
|
const getDetail = async () => {
|
||||||
const id = query.id as unknown as number
|
const id = query.id as unknown as number
|
||||||
@ -129,6 +129,7 @@ const getDetail = async () => {
|
|||||||
const res = (await managementApi.getSpu(id)) as SpuType
|
const res = (await managementApi.getSpu(id)) as SpuType
|
||||||
formData.value = res
|
formData.value = res
|
||||||
// 直接取第一个值就能得到所有属性的id
|
// 直接取第一个值就能得到所有属性的id
|
||||||
|
// TODO @puhui999:可以直接拿 propertyName 拼接处规格 id + 属性,可以看下商品 uniapp 详情的做法
|
||||||
const propertyIds = res.skus[0]?.properties.map((item) => item.propertyId)
|
const propertyIds = res.skus[0]?.properties.map((item) => item.propertyId)
|
||||||
const PropertyS = await PropertyApi.getPropertyListAndValue({ propertyIds })
|
const PropertyS = await PropertyApi.getPropertyListAndValue({ propertyIds })
|
||||||
await nextTick()
|
await nextTick()
|
||||||
@ -151,6 +152,7 @@ const submitForm = async () => {
|
|||||||
await unref(BasicInfoRef)?.validate()
|
await unref(BasicInfoRef)?.validate()
|
||||||
await unref(DescriptionRef)?.validate()
|
await unref(DescriptionRef)?.validate()
|
||||||
await unref(OtherSettingsRef)?.validate()
|
await unref(OtherSettingsRef)?.validate()
|
||||||
|
// TODO @puhui:直接做深拷贝?这样最终 server 端不满足,不需要恢复
|
||||||
// 处理掉一些无关数据
|
// 处理掉一些无关数据
|
||||||
formData.value.skus.forEach((item) => {
|
formData.value.skus.forEach((item) => {
|
||||||
// 给sku name赋值
|
// 给sku name赋值
|
||||||
@ -166,6 +168,7 @@ const submitForm = async () => {
|
|||||||
const newSliderPicUrls = []
|
const newSliderPicUrls = []
|
||||||
formData.value.sliderPicUrls.forEach((item) => {
|
formData.value.sliderPicUrls.forEach((item) => {
|
||||||
// 如果是前端选的图
|
// 如果是前端选的图
|
||||||
|
// TODO @puhui999:疑问哈,为啥会是 object 呀?
|
||||||
if (typeof item === 'object') {
|
if (typeof item === 'object') {
|
||||||
newSliderPicUrls.push(item.url)
|
newSliderPicUrls.push(item.url)
|
||||||
} else {
|
} else {
|
||||||
@ -224,6 +227,7 @@ const resetForm = async () => {
|
|||||||
}
|
}
|
||||||
/** 关闭按钮 */
|
/** 关闭按钮 */
|
||||||
const close = () => {
|
const close = () => {
|
||||||
|
// TODO @puhui999:是不是不用 reset 呀?close 默认销毁
|
||||||
resetForm()
|
resetForm()
|
||||||
delView(unref(currentRoute))
|
delView(unref(currentRoute))
|
||||||
push('/product/product-management')
|
push('/product/product-management')
|
@ -7,6 +7,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
|
<!-- TODO @puhui999:只能选根节点 -->
|
||||||
<el-form-item label="商品分类" prop="categoryId">
|
<el-form-item label="商品分类" prop="categoryId">
|
||||||
<el-tree-select
|
<el-tree-select
|
||||||
v-model="formData.categoryId"
|
v-model="formData.categoryId"
|
||||||
@ -15,6 +16,7 @@
|
|||||||
check-strictly
|
check-strictly
|
||||||
node-key="id"
|
node-key="id"
|
||||||
placeholder="请选择商品分类"
|
placeholder="请选择商品分类"
|
||||||
|
class="w-1/1"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -25,7 +27,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="单位" prop="unit">
|
<el-form-item label="单位" prop="unit">
|
||||||
<el-select v-model="formData.unit" placeholder="请选择单位">
|
<el-select v-model="formData.unit" placeholder="请选择单位" class="w-1/1">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.PRODUCT_UNIT)"
|
v-for="dict in getIntDictOptions(DICT_TYPE.PRODUCT_UNIT)"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
@ -57,7 +59,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="运费模板" prop="deliveryTemplateId">
|
<el-form-item label="运费模板" prop="deliveryTemplateId">
|
||||||
<el-select v-model="formData.deliveryTemplateId" placeholder="请选择" style="width: 100%">
|
<el-select v-model="formData.deliveryTemplateId" placeholder="请选择" class="w-1/1">
|
||||||
<el-option v-for="item in []" :key="item.id" :label="item.name" :value="item.id" />
|
<el-option v-for="item in []" :key="item.id" :label="item.name" :value="item.id" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -84,9 +86,8 @@
|
|||||||
<!-- 多规格添加-->
|
<!-- 多规格添加-->
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item v-if="formData.specType" label="商品属性">
|
<el-form-item v-if="formData.specType" label="商品属性">
|
||||||
<el-button class="mr-15px mb-10px" @click="AttributesAddFormRef.open()"
|
<!-- TODO @puhui999:参考 https://admin.java.crmeb.net/store/list/creatProduct 添加规格好做么?添加的时候,不用输入备注哈 -->
|
||||||
>添加规格
|
<el-button class="mr-15px mb-10px" @click="AttributesAddFormRef.open">添加规格</el-button>
|
||||||
</el-button>
|
|
||||||
<ProductAttributes :attribute-data="attributeList" />
|
<ProductAttributes :attribute-data="attributeList" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<template v-if="formData.specType && attributeList.length > 0">
|
<template v-if="formData.specType && attributeList.length > 0">
|
||||||
@ -108,17 +109,15 @@
|
|||||||
<script lang="ts" name="ProductManagementBasicInfoForm" setup>
|
<script lang="ts" name="ProductManagementBasicInfoForm" setup>
|
||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
import { defaultProps, handleTree } from '@/utils/tree'
|
import { defaultProps, handleTree } from '@/utils/tree'
|
||||||
import { ElInput } from 'element-plus'
|
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import type { SpuType } from '@/api/mall/product/management/type/spuType'
|
import type { SpuType } from '@/api/mall/product/management/type/spuType'
|
||||||
import { UploadImg, UploadImgs } from '@/components/UploadFile'
|
import { UploadImg, UploadImgs } from '@/components/UploadFile'
|
||||||
import { copyValueToTarget } from '@/utils/object'
|
import { copyValueToTarget } from '@/utils/object'
|
||||||
import { ProductAttributes, ProductAttributesAddForm, SkuList } from './index'
|
import { ProductAttributes, ProductAttributesAddForm, SkuList } from './index'
|
||||||
// 业务Api
|
|
||||||
import * as ProductCategoryApi from '@/api/mall/product/category'
|
import * as ProductCategoryApi from '@/api/mall/product/category'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
propFormData: {
|
propFormData: {
|
||||||
type: Object as PropType<SpuType>,
|
type: Object as PropType<SpuType>,
|
||||||
@ -126,10 +125,11 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
activeName: propTypes.string.def('')
|
activeName: propTypes.string.def('')
|
||||||
})
|
})
|
||||||
const AttributesAddFormRef = ref() // 添加商品属性表单
|
const AttributesAddFormRef = ref() // 添加商品属性表单 TODO @puhui999:小写开头哈
|
||||||
const ProductManagementBasicInfoRef = ref() // 表单Ref
|
const ProductManagementBasicInfoRef = ref() // 表单Ref TODO @puhui999:小写开头哈
|
||||||
|
// TODO @puhui999:attributeList 改成 propertyList,会更统一一点
|
||||||
const attributeList = ref([]) // 商品属性列表
|
const attributeList = ref([]) // 商品属性列表
|
||||||
/** 添加商品属性 */
|
/** 添加商品属性 */ // TODO @puhui999:propFormData 算出来
|
||||||
const addAttribute = (property: any) => {
|
const addAttribute = (property: any) => {
|
||||||
if (Array.isArray(property)) {
|
if (Array.isArray(property)) {
|
||||||
attributeList.value = property
|
attributeList.value = property
|
||||||
@ -162,8 +162,9 @@ const rules = reactive({
|
|||||||
specType: [required],
|
specType: [required],
|
||||||
subCommissionType: [required]
|
subCommissionType: [required]
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将传进来的值赋值给formData
|
* 将传进来的值赋值给 formData
|
||||||
*/
|
*/
|
||||||
watch(
|
watch(
|
||||||
() => props.propFormData,
|
() => props.propFormData,
|
||||||
@ -176,10 +177,11 @@ watch(
|
|||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const emit = defineEmits(['update:activeName'])
|
|
||||||
/**
|
/**
|
||||||
* 表单校验
|
* 表单校验
|
||||||
*/
|
*/
|
||||||
|
const emit = defineEmits(['update:activeName'])
|
||||||
const validate = async () => {
|
const validate = async () => {
|
||||||
// 校验表单
|
// 校验表单
|
||||||
if (!ProductManagementBasicInfoRef) return
|
if (!ProductManagementBasicInfoRef) return
|
||||||
@ -197,7 +199,7 @@ const validate = async () => {
|
|||||||
}
|
}
|
||||||
defineExpose({ validate, addAttribute })
|
defineExpose({ validate, addAttribute })
|
||||||
|
|
||||||
// 分销类型
|
/** 分销类型 */
|
||||||
const changeSubCommissionType = () => {
|
const changeSubCommissionType = () => {
|
||||||
// 默认为零,类型切换后也要重置为零
|
// 默认为零,类型切换后也要重置为零
|
||||||
for (const item of formData.skus) {
|
for (const item of formData.skus) {
|
||||||
@ -205,7 +207,8 @@ const changeSubCommissionType = () => {
|
|||||||
item.subCommissionSecondPrice = 0
|
item.subCommissionSecondPrice = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 选择规格
|
|
||||||
|
/** 选择规格 */
|
||||||
const onChangeSpec = () => {
|
const onChangeSpec = () => {
|
||||||
// 重置商品属性列表
|
// 重置商品属性列表
|
||||||
attributeList.value = []
|
attributeList.value = []
|
@ -25,6 +25,11 @@ const DescriptionFormRef = ref() // 表单Ref
|
|||||||
const formData = ref<SpuType>({
|
const formData = ref<SpuType>({
|
||||||
description: '' // 商品详情
|
description: '' // 商品详情
|
||||||
})
|
})
|
||||||
|
// 表单规则
|
||||||
|
const rules = reactive({
|
||||||
|
description: [required]
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 富文本编辑器如果输入过再清空会有残留,需再重置一次
|
* 富文本编辑器如果输入过再清空会有残留,需再重置一次
|
||||||
*/
|
*/
|
||||||
@ -40,10 +45,7 @@ watch(
|
|||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// 表单规则
|
|
||||||
const rules = reactive({
|
|
||||||
description: [required]
|
|
||||||
})
|
|
||||||
/**
|
/**
|
||||||
* 将传进来的值赋值给formData
|
* 将传进来的值赋值给formData
|
||||||
*/
|
*/
|
||||||
@ -58,10 +60,11 @@ watch(
|
|||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const emit = defineEmits(['update:activeName'])
|
|
||||||
/**
|
/**
|
||||||
* 表单校验
|
* 表单校验
|
||||||
*/
|
*/
|
||||||
|
const emit = defineEmits(['update:activeName'])
|
||||||
const validate = async () => {
|
const validate = async () => {
|
||||||
// 校验表单
|
// 校验表单
|
||||||
if (!DescriptionFormRef) return
|
if (!DescriptionFormRef) return
|
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form ref="OtherSettingsFormRef" :model="formData" :rules="rules" label-width="120px">
|
<el-form ref="OtherSettingsFormRef" :model="formData" :rules="rules" label-width="120px">
|
||||||
<el-row>
|
<el-row>
|
||||||
|
<!-- TODO @puhui999:横着三个哈 -->
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="商品排序" prop="sort">
|
<el-form-item label="商品排序" prop="sort">
|
||||||
@ -40,6 +41,7 @@
|
|||||||
<el-tag class="ml-2" type="warning">拼团</el-tag>
|
<el-tag class="ml-2" type="warning">拼团</el-tag>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<!-- TODO @puhui999:等优惠劵 ok 在搞 -->
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="赠送优惠劵">
|
<el-form-item label="赠送优惠劵">
|
||||||
<el-button>选择优惠券</el-button>
|
<el-button>选择优惠券</el-button>
|
||||||
@ -49,13 +51,12 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="OtherSettingsForm" setup>
|
<script lang="ts" name="OtherSettingsForm" setup>
|
||||||
// 商品推荐
|
|
||||||
import type { SpuType } from '@/api/mall/product/management/type/spuType'
|
import type { SpuType } from '@/api/mall/product/management/type/spuType'
|
||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
import { copyValueToTarget } from '@/utils/object'
|
import { copyValueToTarget } from '@/utils/object'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
propFormData: {
|
propFormData: {
|
||||||
type: Object as PropType<SpuType>,
|
type: Object as PropType<SpuType>,
|
||||||
@ -63,7 +64,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
activeName: propTypes.string.def('')
|
activeName: propTypes.string.def('')
|
||||||
})
|
})
|
||||||
// 商品推荐选项
|
// 商品推荐选项 TODO @puhui999:这种叫 recommendOptions 会更合适哈
|
||||||
const recommend = [
|
const recommend = [
|
||||||
{ name: '是否热卖', value: 'recommendHot' },
|
{ name: '是否热卖', value: 'recommendHot' },
|
||||||
{ name: '是否优惠', value: 'recommendBenefit' },
|
{ name: '是否优惠', value: 'recommendBenefit' },
|
||||||
@ -71,10 +72,10 @@ const recommend = [
|
|||||||
{ name: '是否新品', value: 'recommendNew' },
|
{ name: '是否新品', value: 'recommendNew' },
|
||||||
{ name: '是否优品', value: 'recommendGood' }
|
{ name: '是否优品', value: 'recommendGood' }
|
||||||
]
|
]
|
||||||
// 选中推荐选项
|
const checkboxGroup = ref<string[]>(['recommendHot']) // 选中推荐选项
|
||||||
const checkboxGroup = ref<string[]>(['recommendHot'])
|
/** 选择商品后赋值 */
|
||||||
// 选择商品后赋值
|
|
||||||
const onChangeGroup = () => {
|
const onChangeGroup = () => {
|
||||||
|
// TODO @puhui999:是不是可以遍历 recommend,然后进行是否选中;
|
||||||
checkboxGroup.value.includes('recommendHot')
|
checkboxGroup.value.includes('recommendHot')
|
||||||
? (formData.value.recommendHot = true)
|
? (formData.value.recommendHot = true)
|
||||||
: (formData.value.recommendHot = false)
|
: (formData.value.recommendHot = false)
|
||||||
@ -109,6 +110,7 @@ const rules = reactive({
|
|||||||
giveIntegral: [required],
|
giveIntegral: [required],
|
||||||
virtualSalesCount: [required]
|
virtualSalesCount: [required]
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将传进来的值赋值给formData
|
* 将传进来的值赋值给formData
|
||||||
*/
|
*/
|
||||||
@ -130,10 +132,11 @@ watch(
|
|||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const emit = defineEmits(['update:activeName'])
|
|
||||||
/**
|
/**
|
||||||
* 表单校验
|
* 表单校验
|
||||||
*/
|
*/
|
||||||
|
const emit = defineEmits(['update:activeName'])
|
||||||
const validate = async () => {
|
const validate = async () => {
|
||||||
// 校验表单
|
// 校验表单
|
||||||
if (!OtherSettingsFormRef) return
|
if (!OtherSettingsFormRef) return
|
||||||
@ -149,6 +152,5 @@ const validate = async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ validate })
|
defineExpose({ validate })
|
||||||
</script>
|
</script>
|
@ -71,16 +71,19 @@ watch(
|
|||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
/** 删除标签 tagValue 标签值*/
|
/** 删除标签 tagValue 标签值*/
|
||||||
const handleClose = (index, valueIndex) => {
|
const handleClose = (index, valueIndex) => {
|
||||||
attributeList.value[index].values?.splice(valueIndex, 1)
|
attributeList.value[index].values?.splice(valueIndex, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 显示输入框并获取焦点 */
|
/** 显示输入框并获取焦点 */
|
||||||
const showInput = async (index) => {
|
const showInput = async (index) => {
|
||||||
attributeIndex.value = index
|
attributeIndex.value = index
|
||||||
// 因为组件在ref中所以需要用索引获取对应的Ref
|
// 因为组件在ref中所以需要用索引获取对应的Ref
|
||||||
InputRef.value[index]!.input!.focus()
|
InputRef.value[index]!.input!.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 输入框失去焦点或点击回车时触发 */
|
/** 输入框失去焦点或点击回车时触发 */
|
||||||
const handleInputConfirm = async (index, propertyId) => {
|
const handleInputConfirm = async (index, propertyId) => {
|
||||||
if (inputValue.value) {
|
if (inputValue.value) {
|
@ -25,11 +25,13 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- TODO @puhui999: controls-position="right" 可以去掉哈,不然太长了,手动输入更方便 -->
|
||||||
<el-table-column align="center" label="商品条码" min-width="168">
|
<el-table-column align="center" label="商品条码" min-width="168">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-input v-model="row.barCode" class="w-100%" />
|
<el-input v-model="row.barCode" class="w-100%" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<!-- TODO @puhui999:用户输入的时候,是按照元;分主要是我们自己用; -->
|
||||||
<el-table-column align="center" label="销售价(分)" min-width="168">
|
<el-table-column align="center" label="销售价(分)" min-width="168">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-input-number v-model="row.price" :min="0" class="w-100%" controls-position="right" />
|
<el-input-number v-model="row.price" :min="0" class="w-100%" controls-position="right" />
|
||||||
@ -94,15 +96,14 @@
|
|||||||
</template>
|
</template>
|
||||||
<el-table-column v-if="formData.specType" align="center" fixed="right" label="操作" width="80">
|
<el-table-column v-if="formData.specType" align="center" fixed="right" label="操作" width="80">
|
||||||
<template #default>
|
<template #default>
|
||||||
<el-button v-if="isBatch" link size="small" type="primary" @click="batchAdd"
|
<el-button v-if="isBatch" link size="small" type="primary" @click="batchAdd">
|
||||||
>批量添加
|
批量添加
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-else link size="small" type="primary">删除</el-button>
|
<el-button v-else link size="small" type="primary">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" name="SkuList" setup>
|
<script lang="ts" name="SkuList" setup>
|
||||||
import { UploadImg } from '@/components/UploadFile'
|
import { UploadImg } from '@/components/UploadFile'
|
||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
@ -123,7 +124,7 @@ const props = defineProps({
|
|||||||
isBatch: propTypes.bool.def(false) // 是否批量操作
|
isBatch: propTypes.bool.def(false) // 是否批量操作
|
||||||
})
|
})
|
||||||
const formData = ref<SpuType>() // 表单数据
|
const formData = ref<SpuType>() // 表单数据
|
||||||
// 批量添加时的零时数据
|
// 批量添加时的零时数据 TODO @puhui999:小写开头哈;然后变量都尾注释
|
||||||
const SkuData = ref<SkuType[]>([
|
const SkuData = ref<SkuType[]>([
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -168,13 +169,16 @@ const SkuData = ref<SkuType[]>([
|
|||||||
subCommissionSecondPrice: 0
|
subCommissionSecondPrice: 0
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
/** 批量添加 */
|
/** 批量添加 */
|
||||||
const batchAdd = () => {
|
const batchAdd = () => {
|
||||||
formData.value.skus.forEach((item) => {
|
formData.value.skus.forEach((item) => {
|
||||||
copyValueToTarget(item, SkuData.value[0])
|
copyValueToTarget(item, SkuData.value[0])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableHeaderList = ref<{ prop: string; label: string }[]>([])
|
const tableHeaderList = ref<{ prop: string; label: string }[]>([])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将传进来的值赋值给SkuData
|
* 将传进来的值赋值给SkuData
|
||||||
*/
|
*/
|
||||||
@ -189,6 +193,8 @@ watch(
|
|||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO @芋艿:看看 chatgpt 可以进一步下面几个方法的实现不
|
||||||
/** 生成表数据 */
|
/** 生成表数据 */
|
||||||
const generateTableData = (data: any[]) => {
|
const generateTableData = (data: any[]) => {
|
||||||
// 构建数据结构
|
// 构建数据结构
|
||||||
@ -237,6 +243,7 @@ const generateTableData = (data: any[]) => {
|
|||||||
formData.value.skus.push(row)
|
formData.value.skus.push(row)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 构建所有排列组合 */
|
/** 构建所有排列组合 */
|
||||||
const build = (list: any[]) => {
|
const build = (list: any[]) => {
|
||||||
if (list.length === 0) {
|
if (list.length === 0) {
|
||||||
@ -259,6 +266,7 @@ const build = (list: any[]) => {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 监听属性列表生成相关参数和表头 */
|
/** 监听属性列表生成相关参数和表头 */
|
||||||
watch(
|
watch(
|
||||||
() => props.attributeList,
|
() => props.attributeList,
|
@ -8,6 +8,7 @@
|
|||||||
class="-mb-15px"
|
class="-mb-15px"
|
||||||
label-width="68px"
|
label-width="68px"
|
||||||
>
|
>
|
||||||
|
<!-- TODO @puhui999:https://admin.java.crmeb.net/store/index,参考,使用分类 + 标题搜索 -->
|
||||||
<el-form-item label="品牌名称" prop="name">
|
<el-form-item label="品牌名称" prop="name">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryParams.name"
|
v-model="queryParams.name"
|
||||||
@ -51,6 +52,7 @@
|
|||||||
<Icon class="mr-5px" icon="ep:plus" />
|
<Icon class="mr-5px" icon="ep:plus" />
|
||||||
新增
|
新增
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<!-- TODO @puhui999:增加一个【导出】操作 -->
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
@ -66,6 +68,7 @@
|
|||||||
/>
|
/>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-table v-loading="loading" :data="list">
|
||||||
|
<!-- TODO puhui999: ID 编号的展示 -->
|
||||||
<!-- TODO 暂时不做折叠数据 -->
|
<!-- TODO 暂时不做折叠数据 -->
|
||||||
<!-- <el-table-column type="expand">-->
|
<!-- <el-table-column type="expand">-->
|
||||||
<!-- <template #default="{ row }">-->
|
<!-- <template #default="{ row }">-->
|
||||||
@ -92,6 +95,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
|
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
|
||||||
|
<!-- TODO 价格 / 100.0 -->
|
||||||
<el-table-column align="center" label="商品售价" min-width="90" prop="price" />
|
<el-table-column align="center" label="商品售价" min-width="90" prop="price" />
|
||||||
<el-table-column align="center" label="销量" min-width="90" prop="salesCount" />
|
<el-table-column align="center" label="销量" min-width="90" prop="salesCount" />
|
||||||
<el-table-column align="center" label="库存" min-width="90" prop="stock" />
|
<el-table-column align="center" label="库存" min-width="90" prop="stock" />
|
||||||
@ -105,6 +109,7 @@
|
|||||||
/>
|
/>
|
||||||
<el-table-column fixed="right" label="状态" min-width="80">
|
<el-table-column fixed="right" label="状态" min-width="80">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
<!-- TODO @puhui:是不是不用 Number(row.status) 去比较哈,直接 row.status < 0 -->
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="row.status"
|
v-model="row.status"
|
||||||
:active-value="1"
|
:active-value="1"
|
||||||
@ -119,6 +124,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" fixed="right" label="操作" min-width="150">
|
<el-table-column align="center" fixed="right" label="操作" min-width="150">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
|
<!-- TODO @puhui999:【详情】,可以后面点做哈 -->
|
||||||
<template v-if="queryParams.tabType === 4">
|
<template v-if="queryParams.tabType === 4">
|
||||||
<el-button
|
<el-button
|
||||||
v-hasPermi="['product:spu:delete']"
|
v-hasPermi="['product:spu:delete']"
|
||||||
@ -166,6 +172,7 @@
|
|||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
<!-- https://kailong110120130.gitee.io/vue-element-plus-admin-doc/components/image-viewer.html,可以用这个么? -->
|
||||||
<!-- 必须在表格外面展示。不然单元格会遮挡图层 -->
|
<!-- 必须在表格外面展示。不然单元格会遮挡图层 -->
|
||||||
<el-image-viewer
|
<el-image-viewer
|
||||||
v-if="imgViewVisible"
|
v-if="imgViewVisible"
|
||||||
@ -173,20 +180,21 @@
|
|||||||
@close="imgViewVisible = false"
|
@close="imgViewVisible = false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="ProductManagement" setup>
|
<script lang="ts" name="ProductList" setup>
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import { dateFormatter } from '@/utils/formatTime' // 业务api
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
|
// TODO @puhui999:managementApi=》ProductSpuApi
|
||||||
import * as managementApi from '@/api/mall/product/management/spu'
|
import * as managementApi from '@/api/mall/product/management/spu'
|
||||||
import { ProductSpuStatusEnum } from '@/utils/constants'
|
import { ProductSpuStatusEnum } from '@/utils/constants'
|
||||||
import { TabsPaneContext } from 'element-plus'
|
import { TabsPaneContext } from 'element-plus'
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
const { currentRoute, push } = useRouter() // 路由跳转
|
const { currentRoute, push } = useRouter() // 路由跳转
|
||||||
|
|
||||||
const loading = ref(false) // 列表的加载中
|
const loading = ref(false) // 列表的加载中
|
||||||
const total = ref(0) // 列表的总页数
|
const total = ref(0) // 列表的总页数
|
||||||
const list = ref<any[]>([]) // 列表的数据
|
const list = ref<any[]>([]) // 列表的数据
|
||||||
// tabs数据
|
// tabs 数据
|
||||||
const tabsData = ref([
|
const tabsData = ref([
|
||||||
{
|
{
|
||||||
count: 0,
|
count: 0,
|
||||||
@ -214,7 +222,10 @@ const tabsData = ref([
|
|||||||
type: 4
|
type: 4
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
/** 获得每个 Tab 的数量 */
|
||||||
const getTabsCount = async () => {
|
const getTabsCount = async () => {
|
||||||
|
// TODO @puhui999:这里是不是可以不要 try catch 哈
|
||||||
try {
|
try {
|
||||||
const res = await managementApi.getTabsCount()
|
const res = await managementApi.getTabsCount()
|
||||||
for (let objName in res) {
|
for (let objName in res) {
|
||||||
@ -222,6 +233,7 @@ const getTabsCount = async () => {
|
|||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const imgViewVisible = ref(false) // 商品图预览
|
const imgViewVisible = ref(false) // 商品图预览
|
||||||
const imageViewerList = ref<string[]>([]) // 商品图预览列表
|
const imageViewerList = ref<string[]>([]) // 商品图预览列表
|
||||||
const queryParams = ref({
|
const queryParams = ref({
|
||||||
@ -230,10 +242,13 @@ const queryParams = ref({
|
|||||||
tabType: 0
|
tabType: 0
|
||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
|
|
||||||
|
// TODO @puhui999:可以改成 handleTabClick:更准确一点;
|
||||||
const handleClick = (tab: TabsPaneContext) => {
|
const handleClick = (tab: TabsPaneContext) => {
|
||||||
queryParams.value.tabType = tab.paneName
|
queryParams.value.tabType = tab.paneName
|
||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@ -246,8 +261,10 @@ const getList = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:是不是 changeStatus 和 addToTrash 调用一个统一的方法,去更新状态。这样逻辑会更干净一些。
|
||||||
/**
|
/**
|
||||||
* 更改SPU状态
|
* 更改 SPU 状态
|
||||||
|
*
|
||||||
* @param row
|
* @param row
|
||||||
* @param status 更改前的值
|
* @param status 更改前的值
|
||||||
*/
|
*/
|
||||||
@ -271,7 +288,7 @@ const changeStatus = async (row, status?: number) => {
|
|||||||
)
|
)
|
||||||
await managementApi.updateStatus({ id: row.id, status: row.status })
|
await managementApi.updateStatus({ id: row.id, status: row.status })
|
||||||
message.success('更新状态成功')
|
message.success('更新状态成功')
|
||||||
// 刷新tabs数据
|
// 刷新 tabs 数据
|
||||||
await getTabsCount()
|
await getTabsCount()
|
||||||
// 刷新列表
|
// 刷新列表
|
||||||
await getList()
|
await getList()
|
||||||
@ -288,8 +305,10 @@ const changeStatus = async (row, status?: number) => {
|
|||||||
: ProductSpuStatusEnum.DISABLE.status
|
: ProductSpuStatusEnum.DISABLE.status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加入回收站
|
* 加入回收站
|
||||||
|
*
|
||||||
* @param row
|
* @param row
|
||||||
* @param status
|
* @param status
|
||||||
*/
|
*/
|
||||||
@ -299,6 +318,7 @@ const addToTrash = (row, status) => {
|
|||||||
row.status = status
|
row.status = status
|
||||||
changeStatus(row, num)
|
changeStatus(row, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
@ -313,6 +333,7 @@ const handleDelete = async (id: number) => {
|
|||||||
await getList()
|
await getList()
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品图预览
|
* 商品图预览
|
||||||
* @param imgUrl
|
* @param imgUrl
|
||||||
@ -321,6 +342,7 @@ const imagePreview = (imgUrl: string) => {
|
|||||||
imageViewerList.value = [imgUrl]
|
imageViewerList.value = [imgUrl]
|
||||||
imgViewVisible.value = true
|
imgViewVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
const handleQuery = () => {
|
const handleQuery = () => {
|
||||||
getList()
|
getList()
|
||||||
@ -334,16 +356,20 @@ const resetQuery = () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增或修改
|
* 新增或修改
|
||||||
* @param id
|
*
|
||||||
|
* @param id 商品 SPU 编号
|
||||||
*/
|
*/
|
||||||
const openForm = (id?: number) => {
|
const openForm = (id?: number) => {
|
||||||
|
// 修改
|
||||||
if (typeof id === 'number') {
|
if (typeof id === 'number') {
|
||||||
push('/product/productManagementAdd?id=' + id)
|
push('/product/productManagementAdd?id=' + id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 新增
|
||||||
push('/product/productManagementAdd')
|
push('/product/productManagementAdd')
|
||||||
}
|
}
|
||||||
// 监听路由变化更新列表
|
|
||||||
|
// 监听路由变化更新列表 TODO @puhui999:这个是必须加的么?
|
||||||
watch(
|
watch(
|
||||||
() => currentRoute.value,
|
() => currentRoute.value,
|
||||||
() => {
|
() => {
|
||||||
@ -353,6 +379,7 @@ watch(
|
|||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
/** 初始化 **/
|
/** 初始化 **/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTabsCount()
|
getTabsCount()
|
Loading…
Reference in New Issue
Block a user