商品管理: 初步完成相关组件
This commit is contained in:
parent
ab1685a741
commit
61218ae711
0
src/api/mall/product/management/sku.ts
Normal file
0
src/api/mall/product/management/sku.ts
Normal file
15
src/api/mall/product/management/spu.ts
Normal file
15
src/api/mall/product/management/spu.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
import type { SpuType } from './type/spuType'
|
||||||
|
|
||||||
|
// 获得sku列表
|
||||||
|
export const getSkuList = (params: any) => {
|
||||||
|
return request.get({ url: '/product/sku/list', params })
|
||||||
|
}
|
||||||
|
// 创建商品spu
|
||||||
|
export const createSpu = (data: SpuType) => {
|
||||||
|
return request.post({ url: '/product/spu/create', data })
|
||||||
|
}
|
||||||
|
// 更新商品spu
|
||||||
|
export const updateSpu = (data: SpuType) => {
|
||||||
|
return request.put({ url: '/product/spu/update', data })
|
||||||
|
}
|
75
src/api/mall/product/management/type/skuType.ts
Normal file
75
src/api/mall/product/management/type/skuType.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
export interface Property {
|
||||||
|
/**
|
||||||
|
* 属性编号
|
||||||
|
*
|
||||||
|
* 关联 {@link ProductPropertyDO#getId()}
|
||||||
|
*/
|
||||||
|
propertyId?: number
|
||||||
|
/**
|
||||||
|
* 属性值编号
|
||||||
|
*
|
||||||
|
* 关联 {@link ProductPropertyValueDO#getId()}
|
||||||
|
*/
|
||||||
|
valueId?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkuType {
|
||||||
|
/**
|
||||||
|
* 商品 SKU 编号,自增
|
||||||
|
*/
|
||||||
|
id?: number
|
||||||
|
/**
|
||||||
|
* SPU 编号
|
||||||
|
*/
|
||||||
|
spuId?: number
|
||||||
|
/**
|
||||||
|
* 属性数组,JSON 格式
|
||||||
|
*/
|
||||||
|
properties?: Property[]
|
||||||
|
/**
|
||||||
|
* 商品价格,单位:分
|
||||||
|
*/
|
||||||
|
price?: number
|
||||||
|
/**
|
||||||
|
* 市场价,单位:分
|
||||||
|
*/
|
||||||
|
marketPrice?: number
|
||||||
|
/**
|
||||||
|
* 成本价,单位:分
|
||||||
|
*/
|
||||||
|
costPrice?: number
|
||||||
|
/**
|
||||||
|
* 商品条码
|
||||||
|
*/
|
||||||
|
barCode?: string
|
||||||
|
/**
|
||||||
|
* 图片地址
|
||||||
|
*/
|
||||||
|
picUrl?: string
|
||||||
|
/**
|
||||||
|
* 库存
|
||||||
|
*/
|
||||||
|
stock?: number
|
||||||
|
/**
|
||||||
|
* 商品重量,单位:kg 千克
|
||||||
|
*/
|
||||||
|
weight?: number
|
||||||
|
/**
|
||||||
|
* 商品体积,单位:m^3 平米
|
||||||
|
*/
|
||||||
|
volume?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一级分销的佣金,单位:分
|
||||||
|
*/
|
||||||
|
subCommissionFirstPrice?: number
|
||||||
|
/**
|
||||||
|
* 二级分销的佣金,单位:分
|
||||||
|
*/
|
||||||
|
subCommissionSecondPrice?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品销量
|
||||||
|
*/
|
||||||
|
salesCount?: number
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
import { SkuType } from './skuType'
|
||||||
|
|
||||||
export interface SpuType {
|
export interface SpuType {
|
||||||
name?: string // 商品名称
|
name?: string // 商品名称
|
||||||
categoryId?: number | undefined // 商品分类
|
categoryId?: number | undefined // 商品分类
|
||||||
@ -10,6 +12,7 @@ export interface SpuType {
|
|||||||
selectRule?: string // 选择规格 TODO 暂时定义
|
selectRule?: string // 选择规格 TODO 暂时定义
|
||||||
specType?: boolean // 商品规格
|
specType?: boolean // 商品规格
|
||||||
subCommissionType?: boolean // 分销类型
|
subCommissionType?: boolean // 分销类型
|
||||||
|
skus?: SkuType[] // sku数组
|
||||||
description?: string // 商品详情
|
description?: string // 商品详情
|
||||||
sort?: string // 商品排序
|
sort?: string // 商品排序
|
||||||
giveIntegral?: number // 赠送积分
|
giveIntegral?: number // 赠送积分
|
@ -34,12 +34,14 @@
|
|||||||
<script lang="ts" name="ProductManagementForm" setup>
|
<script lang="ts" name="ProductManagementForm" setup>
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||||
import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components'
|
import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components'
|
||||||
import { SpuType } from '@/api/mall/product/management/type' // const { t } = useI18n() // 国际化
|
import type { SpuType } from '@/api/mall/product/management/type/spuType'
|
||||||
|
// 业务api
|
||||||
|
import * as managementApi from '@/api/mall/product/management/spu'
|
||||||
|
|
||||||
// const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
// const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const { push, currentRoute } = useRouter() // 路由
|
const { push, currentRoute } = useRouter() // 路由
|
||||||
// const { query } = useRoute() // 查询参数
|
const { query } = useRoute() // 查询参数
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
const { delView } = useTagsViewStore() // 视图操作
|
||||||
|
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
@ -70,10 +72,17 @@ const formData = ref<SpuType>({
|
|||||||
recommendGood: false // 是否优品
|
recommendGood: false // 是否优品
|
||||||
})
|
})
|
||||||
/** 获得详情 */
|
/** 获得详情 */
|
||||||
const getDetail = async () => {}
|
const getDetail = async () => {
|
||||||
|
const id = query.id as unknown as number
|
||||||
|
if (!id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 提交按钮 */
|
/** 提交按钮 */
|
||||||
const submitForm = async () => {
|
const submitForm = async () => {
|
||||||
|
// 提交请求
|
||||||
|
formLoading.value = true
|
||||||
// TODO 三个表单逐一校验,如果有一个表单校验不通过则切换到对应表单,如果有两个及以上的情况则切换到最前面的一个并弹出提示消息
|
// TODO 三个表单逐一校验,如果有一个表单校验不通过则切换到对应表单,如果有两个及以上的情况则切换到最前面的一个并弹出提示消息
|
||||||
// 校验各表单
|
// 校验各表单
|
||||||
try {
|
try {
|
||||||
@ -81,9 +90,20 @@ const submitForm = async () => {
|
|||||||
await unref(DescriptionRef)?.validate()
|
await unref(DescriptionRef)?.validate()
|
||||||
await unref(OtherSettingsRef)?.validate()
|
await unref(OtherSettingsRef)?.validate()
|
||||||
// 校验都通过后提交表单
|
// 校验都通过后提交表单
|
||||||
console.log(formData.value)
|
const data = formData.value as SpuType
|
||||||
} catch {}
|
const id = query.id as unknown as number
|
||||||
|
if (!id) {
|
||||||
|
await managementApi.createSpu(data)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
} else {
|
||||||
|
await managementApi.updateSpu(data)
|
||||||
|
message.success(t('common.updateSuccess'))
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置表单
|
* 重置表单
|
||||||
*/
|
*/
|
||||||
|
@ -58,7 +58,6 @@
|
|||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-button class="ml-20px">运费模板</el-button>
|
<el-button class="ml-20px">运费模板</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<!-- TODO 商品规格和分销类型切换待定 -->
|
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="商品规格" props="specType">
|
<el-form-item label="商品规格" props="specType">
|
||||||
<el-radio-group v-model="formData.specType" @change="changeSpecType(formData.specType)">
|
<el-radio-group v-model="formData.specType" @change="changeSpecType(formData.specType)">
|
||||||
@ -67,45 +66,41 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<!-- TODO 商品规格和分销类型切换待定 -->
|
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="分销类型" props="subCommissionType">
|
<el-form-item label="分销类型" props="subCommissionType">
|
||||||
<el-radio-group
|
<el-radio-group v-model="formData.subCommissionType" @change="changeSubCommissionType">
|
||||||
v-model="formData.subCommissionType"
|
|
||||||
@change="changeSubCommissionType(formData.subCommissionType)"
|
|
||||||
>
|
|
||||||
<el-radio :label="false">默认设置</el-radio>
|
<el-radio :label="false">默认设置</el-radio>
|
||||||
<el-radio :label="true" class="radio">自行设置</el-radio>
|
<el-radio :label="true" class="radio">自行设置</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<!-- 多规格添加-->
|
<!-- 多规格添加-->
|
||||||
<el-col v-if="formData.specType" :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item label="选择规格" prop="">
|
<el-form-item v-if="formData.specType" label="商品属性" prop="">
|
||||||
<div class="acea-row">
|
<el-button class="mr-15px" @click="AttributesAddFormRef.open()">添加规格</el-button>
|
||||||
<el-select v-model="formData.selectRule">
|
<ProductAttributes :attribute-data="attributeList" />
|
||||||
<el-option
|
</el-form-item>
|
||||||
v-for="item in []"
|
<el-form-item>
|
||||||
:key="item.id"
|
<SkuList :sku-data="formData.skus" :subCommissionType="formData.subCommissionType" />
|
||||||
:label="item.ruleName"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
<el-button class="mr-20px" type="primary" @click="confirm">确认</el-button>
|
|
||||||
<el-button class="mr-15px" @click="addRule">添加规格</el-button>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<ProductAttributesAddForm ref="AttributesAddFormRef" @success="addAttribute" />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="ProductManagementBasicInfoForm" setup>
|
<script lang="ts" name="ProductManagementBasicInfoForm" setup>
|
||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
import type { SpuType } from '@/api/mall/product/management/type'
|
import type { SpuType } from '@/api/mall/product/management/type/spuType'
|
||||||
import { UploadImg, UploadImgs } from '@/components/UploadFile'
|
import { UploadImg, UploadImgs } from '@/components/UploadFile'
|
||||||
|
import SkuList from './SkuList/index.vue'
|
||||||
|
import ProductAttributesAddForm from './ProductAttributesAddForm.vue'
|
||||||
|
import ProductAttributes from './ProductAttributes.vue'
|
||||||
import { copyValueToTarget } from '@/utils/object'
|
import { copyValueToTarget } from '@/utils/object'
|
||||||
|
// 业务Api
|
||||||
import * as ProductCategoryApi from '@/api/mall/product/category'
|
import * as ProductCategoryApi from '@/api/mall/product/category'
|
||||||
|
import * as PropertyApi from '@/api/mall/product/property'
|
||||||
import { defaultProps, handleTree } from '@/utils/tree'
|
import { defaultProps, handleTree } from '@/utils/tree'
|
||||||
|
import { ElInput } from 'element-plus'
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -114,9 +109,21 @@ const props = defineProps({
|
|||||||
default: () => {}
|
default: () => {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const AttributesAddFormRef = ref() // 添加商品属性表单
|
||||||
const ProductManagementBasicInfoRef = ref() // 表单Ref
|
const ProductManagementBasicInfoRef = ref() // 表单Ref
|
||||||
const formData = ref<SpuType>({
|
// 属性列表
|
||||||
|
const attributeList = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '颜色',
|
||||||
|
attributeValues: [{ id: 1, name: '白色' }]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const addAttribute = async (propertyId: number) => {
|
||||||
|
const data = await PropertyApi.getPropertyValuePage({ id: propertyId })
|
||||||
|
console.log(data)
|
||||||
|
}
|
||||||
|
const formData = reactive<SpuType>({
|
||||||
name: '', // 商品名称
|
name: '', // 商品名称
|
||||||
categoryId: undefined, // 商品分类
|
categoryId: undefined, // 商品分类
|
||||||
keyword: '', // 关键字
|
keyword: '', // 关键字
|
||||||
@ -124,10 +131,46 @@ const formData = ref<SpuType>({
|
|||||||
picUrl: '', // 商品封面图
|
picUrl: '', // 商品封面图
|
||||||
sliderPicUrls: [], // 商品轮播图
|
sliderPicUrls: [], // 商品轮播图
|
||||||
introduction: '', // 商品简介
|
introduction: '', // 商品简介
|
||||||
deliveryTemplateId: '', // 运费模版
|
deliveryTemplateId: 1, // 运费模版
|
||||||
selectRule: '', // 选择规则 TODO 暂定
|
selectRule: '', // 选择规则 TODO 暂定
|
||||||
specType: false, // 商品规格
|
specType: false, // 商品规格
|
||||||
subCommissionType: false // 分销类型
|
subCommissionType: false, // 分销类型
|
||||||
|
skus: [
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 商品价格,单位:分
|
||||||
|
*/
|
||||||
|
price: 0,
|
||||||
|
/**
|
||||||
|
* 市场价,单位:分
|
||||||
|
*/
|
||||||
|
marketPrice: 0,
|
||||||
|
/**
|
||||||
|
* 成本价,单位:分
|
||||||
|
*/
|
||||||
|
costPrice: 0,
|
||||||
|
/**
|
||||||
|
* 商品条码
|
||||||
|
*/
|
||||||
|
barCode: '',
|
||||||
|
/**
|
||||||
|
* 图片地址
|
||||||
|
*/
|
||||||
|
picUrl: '',
|
||||||
|
/**
|
||||||
|
* 库存
|
||||||
|
*/
|
||||||
|
stock: 0,
|
||||||
|
/**
|
||||||
|
* 商品重量,单位:kg 千克
|
||||||
|
*/
|
||||||
|
weight: 0,
|
||||||
|
/**
|
||||||
|
* 商品体积,单位:m^3 平米
|
||||||
|
*/
|
||||||
|
volume: 0
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [required],
|
name: [required],
|
||||||
@ -148,7 +191,7 @@ watch(
|
|||||||
() => props.propFormData,
|
() => props.propFormData,
|
||||||
(data) => {
|
(data) => {
|
||||||
if (!data) return
|
if (!data) return
|
||||||
copyValueToTarget(formData.value, data)
|
copyValueToTarget(formData, data)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
@ -170,7 +213,7 @@ const validate = async () => {
|
|||||||
throw new Error('商品信息未完善!!')
|
throw new Error('商品信息未完善!!')
|
||||||
} else {
|
} else {
|
||||||
// 校验通过更新数据
|
// 校验通过更新数据
|
||||||
Object.assign(props.propFormData, formData.value)
|
Object.assign(props.propFormData, formData)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -180,13 +223,17 @@ const changeSpecType = (specType) => {
|
|||||||
console.log(specType)
|
console.log(specType)
|
||||||
}
|
}
|
||||||
// 分销类型
|
// 分销类型
|
||||||
const changeSubCommissionType = (subCommissionType) => {
|
const changeSubCommissionType = () => {
|
||||||
console.log(subCommissionType)
|
// 默认为零,类型切换后也要重置为零
|
||||||
|
for (const item of formData.skus) {
|
||||||
|
item.subCommissionFirstPrice = 0
|
||||||
|
item.subCommissionSecondPrice = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 选择属性确认
|
// 选择属性确认
|
||||||
const confirm = () => {}
|
// const confirm = () => {}
|
||||||
// 添加规格
|
// 添加规格
|
||||||
const addRule = () => {}
|
// const addRule = () => {}
|
||||||
const categoryList = ref() // 分类树
|
const categoryList = ref() // 分类树
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 获得分类树
|
// 获得分类树
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="DescriptionForm" setup>
|
<script lang="ts" name="DescriptionForm" setup>
|
||||||
import type { SpuType } from '@/api/mall/product/management/type'
|
import type { SpuType } from '@/api/mall/product/management/type/spuType'
|
||||||
import { Editor } from '@/components/Editor'
|
import { Editor } from '@/components/Editor'
|
||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
import { copyValueToTarget } from '@/utils/object'
|
import { copyValueToTarget } from '@/utils/object'
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="OtherSettingsForm" setup>
|
<script lang="ts" name="OtherSettingsForm" setup>
|
||||||
// 商品推荐
|
// 商品推荐
|
||||||
import type { SpuType } from '@/api/mall/product/management/type'
|
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'
|
||||||
|
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
<template>
|
||||||
|
<el-col v-for="(item, index) in attributeList" :key="index">
|
||||||
|
<div>
|
||||||
|
<el-text class="mx-1">属性名:</el-text>
|
||||||
|
<el-text class="mx-1">{{ item.name }}</el-text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-text class="mx-1">属性值:</el-text>
|
||||||
|
<el-tag
|
||||||
|
v-for="(value, valueIndex) in item.attributeValues"
|
||||||
|
:key="value.name"
|
||||||
|
:disable-transitions="false"
|
||||||
|
class="mx-1"
|
||||||
|
closable
|
||||||
|
@close="handleClose(index, valueIndex)"
|
||||||
|
>
|
||||||
|
{{ value.name }}
|
||||||
|
</el-tag>
|
||||||
|
<el-input
|
||||||
|
v-if="inputVisible"
|
||||||
|
ref="InputRef"
|
||||||
|
v-model="inputValue"
|
||||||
|
class="!w-20"
|
||||||
|
size="small"
|
||||||
|
@blur="handleInputConfirm(index)"
|
||||||
|
@keyup.enter="handleInputConfirm(index)"
|
||||||
|
/>
|
||||||
|
<el-button v-else class="button-new-tag ml-1" size="small" @click="showInput(index)">
|
||||||
|
+ 添加
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-divider class="my-10px" />
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="ProductAttributes" setup>
|
||||||
|
import { ElInput } from 'element-plus'
|
||||||
|
|
||||||
|
const inputValue = ref('') // 输入框值
|
||||||
|
const inputVisible = ref(false) // 输入框显隐控制
|
||||||
|
const InputRef = ref<InstanceType<typeof ElInput>>() //标签输入框Ref
|
||||||
|
const attributeList = ref([])
|
||||||
|
const props = defineProps({
|
||||||
|
attributeData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.attributeData,
|
||||||
|
(data) => {
|
||||||
|
if (!data) return
|
||||||
|
attributeList.value = data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
/** 删除标签 tagValue 标签值*/
|
||||||
|
const handleClose = (index, valueIndex) => {
|
||||||
|
const av = attributeList.value[index].attributeValues
|
||||||
|
av.splice(valueIndex, 1)
|
||||||
|
}
|
||||||
|
/** 显示输入框并获取焦点 */
|
||||||
|
const showInput = (index) => {
|
||||||
|
inputVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
InputRef.value[index]!.input!.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/** 输入框失去焦点或点击回车时触发 */
|
||||||
|
const handleInputConfirm = (index) => {
|
||||||
|
if (inputValue.value) {
|
||||||
|
// 因为ref再循环里,所以需要index获取对应的ref
|
||||||
|
attributeList.value[index].attributeValues.push({ name: inputValue.value })
|
||||||
|
}
|
||||||
|
inputVisible.value = false
|
||||||
|
inputValue.value = ''
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,82 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
v-loading="formLoading"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="80px"
|
||||||
|
>
|
||||||
|
<el-form-item label="名称" prop="name">
|
||||||
|
<el-input v-model="formData.name" placeholder="请输入名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="formData.remark" placeholder="请输入内容" type="textarea" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" name="ProductPropertyForm" setup>
|
||||||
|
import * as PropertyApi from '@/api/mall/product/property'
|
||||||
|
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
const dialogTitle = ref('添加商品属性') // 弹窗的标题
|
||||||
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
|
const formData = ref({
|
||||||
|
name: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
const formRules = reactive({
|
||||||
|
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = async () => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
|
/** 提交表单 */
|
||||||
|
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||||
|
const submitForm = async () => {
|
||||||
|
// 校验表单
|
||||||
|
if (!formRef) return
|
||||||
|
const valid = await formRef.value.validate()
|
||||||
|
if (!valid) return
|
||||||
|
// 提交请求
|
||||||
|
formLoading.value = true
|
||||||
|
try {
|
||||||
|
const data = formData.value as PropertyApi.PropertyVO
|
||||||
|
// 检查属性是否已存在,如果有则返回属性和其下属性值
|
||||||
|
const res = await PropertyApi.getPropertyListAndValue({ name: data.name })
|
||||||
|
if (res.length === 0) {
|
||||||
|
const propertyId = await PropertyApi.createProperty(data)
|
||||||
|
emit('success', { id: propertyId, ...formData.value, values: [] })
|
||||||
|
} else {
|
||||||
|
emit(res[0]) // 因为只用一个
|
||||||
|
}
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
dialogVisible.value = false
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置表单 */
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.value = {
|
||||||
|
name: '',
|
||||||
|
remark: ''
|
||||||
|
}
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<el-table :data="SkuData" border class="tabNumWidth" size="small">
|
||||||
|
<el-table-column align="center" fixed="left" label="图片" min-width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<UploadImg v-model="row.picUrl" height="80px" width="100%" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="商品条码" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.barCode" :min="0" class="w-100%" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="销售价(分)" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.price" :min="0" class="w-100%" type="number" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="市场价(分)" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.marketPrice" :min="0" class="w-100%" type="number" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="成本价(分)" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.costPrice" :min="0" class="w-100%" type="number" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="库存" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.stock" :min="0" class="w-100%" type="number" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="重量(kg)" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.weight" :min="0" class="w-100%" type="number" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="体积(m^3)" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.volume" :min="0" class="w-100%" type="number" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template v-if="subCommissionType">
|
||||||
|
<el-table-column align="center" label="一级返佣(分)" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.subCommissionFirstPrice" :min="0" class="w-100%" type="number" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="二级返佣(分)" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.subCommissionSecondPrice" :min="0" class="w-100%" type="number" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" name="index" setup>
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
import { UploadImg } from '@/components/UploadFile'
|
||||||
|
import { PropType } from 'vue'
|
||||||
|
import type { SkuType } from '@/api/mall/product/management/type/skuType'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
skuData: {
|
||||||
|
type: Array as PropType<SkuType>,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
subCommissionType: propTypes.bool.def(false) // 分销类型
|
||||||
|
})
|
||||||
|
const SkuData = ref<SkuType[]>([])
|
||||||
|
/**
|
||||||
|
* 将传进来的值赋值给SkuData
|
||||||
|
*/
|
||||||
|
watch(
|
||||||
|
() => props.skuData,
|
||||||
|
(data) => {
|
||||||
|
if (!data) return
|
||||||
|
SkuData.value = data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
@ -47,12 +47,7 @@
|
|||||||
<Icon class="mr-5px" icon="ep:refresh" />
|
<Icon class="mr-5px" icon="ep:refresh" />
|
||||||
重置
|
重置
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button v-hasPermi="['product:brand:create']" plain type="primary" @click="openForm">
|
||||||
v-hasPermi="['product:brand:create']"
|
|
||||||
plain
|
|
||||||
type="primary"
|
|
||||||
@click="openForm('create')"
|
|
||||||
>
|
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
<Icon class="mr-5px" icon="ep:plus" />
|
||||||
新增
|
新增
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -133,8 +128,8 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts" name="ProductManagement" setup>
|
<script lang="ts" name="ProductManagement" setup>
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime' // 业务api
|
||||||
|
import * as managementApi from '@/api/mall/product/management/spu' // const message = useMessage() // 消息弹窗
|
||||||
// const message = useMessage() // 消息弹窗
|
// const message = useMessage() // 消息弹窗
|
||||||
// const { t } = useI18n() // 国际化
|
// const { t } = useI18n() // 国际化
|
||||||
const { push } = useRouter() // 路由跳转
|
const { push } = useRouter() // 路由跳转
|
||||||
@ -182,9 +177,9 @@ const queryFormRef = ref() // 搜索的表单
|
|||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
// const data = await ProductBrandApi.getBrandParam(queryParams)
|
const data = await managementApi.getSkuList(queryParams)
|
||||||
// list.value = data.list
|
list.value = data.list
|
||||||
// total.value = data.total
|
total.value = data.total
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@ -201,7 +196,10 @@ const resetQuery = () => {
|
|||||||
handleQuery()
|
handleQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
const openForm = () => {
|
const openForm = (id?: number) => {
|
||||||
|
if (typeof id === 'number') {
|
||||||
|
push('/product/productManagementAdd?id=' + id)
|
||||||
|
}
|
||||||
push('/product/productManagementAdd')
|
push('/product/productManagementAdd')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,42 +2,49 @@
|
|||||||
<!-- 搜索工作栏 -->
|
<!-- 搜索工作栏 -->
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-form
|
<el-form
|
||||||
class="-mb-15px"
|
|
||||||
:model="queryParams"
|
|
||||||
ref="queryFormRef"
|
ref="queryFormRef"
|
||||||
:inline="true"
|
:inline="true"
|
||||||
|
:model="queryParams"
|
||||||
|
class="-mb-15px"
|
||||||
label-width="68px"
|
label-width="68px"
|
||||||
>
|
>
|
||||||
<el-form-item label="名称" prop="name">
|
<el-form-item label="名称" prop="name">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryParams.name"
|
v-model="queryParams.name"
|
||||||
placeholder="请输入名称"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入名称"
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="创建时间" prop="createTime">
|
<el-form-item label="创建时间" prop="createTime">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="queryParams.createTime"
|
v-model="queryParams.createTime"
|
||||||
value-format="YYYY-MM-DD HH:mm:ss"
|
|
||||||
type="daterange"
|
|
||||||
start-placeholder="开始日期"
|
|
||||||
end-placeholder="结束日期"
|
|
||||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
<el-button @click="handleQuery">
|
||||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
<Icon class="mr-5px" icon="ep:search" />
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="resetQuery">
|
||||||
|
<Icon class="mr-5px" icon="ep:refresh" />
|
||||||
|
重置
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
|
v-hasPermi="['product:property:create']"
|
||||||
plain
|
plain
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="openForm('create')"
|
@click="openForm('create')"
|
||||||
v-hasPermi="['product:property:create']"
|
|
||||||
>
|
>
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
<Icon class="mr-5px" icon="ep:plus" />
|
||||||
|
新增
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -46,23 +53,23 @@
|
|||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-table v-loading="loading" :data="list">
|
||||||
<el-table-column label="编号" align="center" prop="id" />
|
<el-table-column align="center" label="编号" prop="id" />
|
||||||
<el-table-column label="名称" align="center" />
|
<el-table-column align="center" label="名称" prop="name" />
|
||||||
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
|
<el-table-column :show-overflow-tooltip="true" align="center" label="备注" prop="remark" />
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="创建时间"
|
:formatter="dateFormatter"
|
||||||
align="center"
|
align="center"
|
||||||
|
label="创建时间"
|
||||||
prop="createTime"
|
prop="createTime"
|
||||||
width="180"
|
width="180"
|
||||||
:formatter="dateFormatter"
|
|
||||||
/>
|
/>
|
||||||
<el-table-column label="操作" align="center">
|
<el-table-column align="center" label="操作">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
|
v-hasPermi="['product:property:update']"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="openForm('update', scope.row.id)"
|
@click="openForm('update', scope.row.id)"
|
||||||
v-hasPermi="['product:property:update']"
|
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -70,10 +77,10 @@
|
|||||||
<router-link :to="'/property/value/' + scope.row.id">属性值</router-link>
|
<router-link :to="'/property/value/' + scope.row.id">属性值</router-link>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
|
v-hasPermi="['product:property:delete']"
|
||||||
link
|
link
|
||||||
type="danger"
|
type="danger"
|
||||||
@click="handleDelete(scope.row.id)"
|
@click="handleDelete(scope.row.id)"
|
||||||
v-hasPermi="['product:property:delete']"
|
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -82,9 +89,9 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<Pagination
|
<Pagination
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
v-model:limit="queryParams.pageSize"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
:total="total"
|
||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
@ -92,10 +99,11 @@
|
|||||||
<!-- 表单弹窗:添加/修改 -->
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
<PropertyForm ref="formRef" @success="getList" />
|
<PropertyForm ref="formRef" @success="getList" />
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts" name="ProductProperty">
|
<script lang="ts" name="ProductProperty" setup>
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import * as PropertyApi from '@/api/mall/product/property'
|
import * as PropertyApi from '@/api/mall/product/property'
|
||||||
import PropertyForm from './PropertyForm.vue'
|
import PropertyForm from './PropertyForm.vue'
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user