完善商品提交信息

This commit is contained in:
wangxiaowei
2025-05-21 17:57:38 +08:00
parent 5b77884d90
commit d9ffaa6b4e
6 changed files with 707 additions and 426 deletions

View File

@ -26,6 +26,6 @@ export function apiGoodsDetail(params: any) {
}
// 商品分类列表
export function checkCategory(params: any) {
return request.post({ url: '/goodsCategory/checkCategory', params })
export function checkCategory() {
return request.post({ url: '/goodsCategory/checkCategory' })
}

View File

@ -1,33 +1,18 @@
<template>
<div class="material-select">
<popup
ref="popupRef"
width="1050px"
custom-class="body-padding"
:title="`选择${tipsText}`"
@confirm="handleConfirm"
@close="handleClose"
>
<popup ref="popupRef" width="1050px" custom-class="body-padding" :title="`选择${tipsText}`"
@confirm="handleConfirm" @close="handleClose">
<template v-if="!hiddenUpload" #trigger>
<div class="material-select__trigger clearfix" @click.stop>
<draggable class="draggable" v-model="fileList" animation="300" item-key="id">
<template v-slot:item="{ element, index }">
<div
class="material-preview"
:class="{
'is-disabled': disabled,
'is-one': limit == 1
}"
@click="showPopup(index)"
>
<div class="material-preview" :class="{
'is-disabled': disabled,
'is-one': limit == 1
}" @click="showPopup(index)">
<del-wrap @close="deleteImg(index)">
<file-item
:uri="excludeDomain ? getImageUrl(element) : element"
:file-size="size"
:width="width"
:height="height"
:type="type"
></file-item>
<file-item :uri="excludeDomain ? getImageUrl(element) : element" :file-size="size"
:width="width" :height="height" :type="type"></file-item>
</del-wrap>
<div class="operation-btns text-xs text-center">
<span>修改</span>
@ -37,24 +22,16 @@
</div>
</template>
</draggable>
<div
class="material-upload"
@click="showPopup(-1)"
v-show="showUpload"
:class="{
'is-disabled': disabled,
'is-one': limit == 1,
[uploadClass]: true
}"
>
<div class="material-upload" @click="showPopup(-1)" v-show="showUpload" :class="{
'is-disabled': disabled,
'is-one': limit == 1,
[uploadClass]: true
}">
<slot name="upload">
<div
class="upload-btn"
:style="{
width: width || size,
height: height || size
}"
>
<div class="upload-btn" :style="{
width: width || size,
height: height || size
}">
<icon :size="25" name="el-icon-Plus" />
<span>添加</span>
</div>
@ -64,13 +41,8 @@
</template>
<el-scrollbar>
<div class="material-wrap">
<material
ref="materialRef"
:type="type"
:file-size="fileSize"
:limit="meterialLimit"
@change="selectChange"
/>
<material ref="materialRef" :type="type" :file-size="fileSize" :limit="meterialLimit"
@change="selectChange" />
</div>
</el-scrollbar>
</popup>
@ -272,6 +244,7 @@ export default defineComponent({
<style scoped lang="scss">
.material-select {
.material-upload,
.material-preview {
position: relative;
@ -281,17 +254,21 @@ export default defineComponent({
margin-bottom: 8px;
box-sizing: border-box;
float: left;
&.is-disabled {
cursor: not-allowed;
}
&.is-one {
margin-bottom: 0;
}
&:hover {
.operation-btns {
display: block;
}
}
.operation-btns {
display: none;
position: absolute;
@ -303,12 +280,14 @@ export default defineComponent({
background-color: rgba(0, 0, 0, 0.3);
}
}
.material-upload {
:deep(.upload-btn) {
@apply text-tx-secondary box-border rounded border-br border-dashed border flex flex-col justify-center items-center;
}
}
}
.material-wrap {
min-width: 720px;
height: 560px;

View File

@ -3,12 +3,127 @@
<popup ref="popupRef" :title="popupTitle" :async="true" width="550px" @confirm="handleSubmit"
@close="handleClose">
<el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules">
<el-form-item label="商品名称" prop="name">
<el-input v-model="formData.name" clearable placeholder="请输入商品名称" />
</el-form-item>
<el-form-item label="商品编码" prop="code">
<el-input v-model="formData.code" clearable placeholder="请输入商品编码" />
</el-form-item>
<el-tabs type="border-card">
<el-tab-pane label="基础设置">
<el-form-item label="商品名称" prop="name" required>
<el-input v-model="formData.name" clearable placeholder="请输入商品名称" />
</el-form-item>
<el-form-item label="商品编码" prop="code">
<el-input v-model="formData.code" clearable placeholder="请输入商品编码" />
</el-form-item>
<el-form-item label="商品分类" prop="first_category_id" required>
<div class="flex w-full">
<div class="w-4/12">
<el-select v-model="formData.first_category_id" placeholder="请选择分类"
@change="selectFirstCategory">
<el-option :label="item.name" :value="item.id"
v-for="(item, index) in firstCategory" />
</el-select>
</div>
<div class="w-4/12">
<el-select v-model="formData.second_category_id" placeholder="请选择分类"
@change="selectSecondCategory">
<el-option :label="item.name" :value="item.id"
v-for="(item, index) in secondCategory" />
</el-select>
</div>
<div class="w-4/12">
<el-select v-model="formData.third_category_id" placeholder="请选择分类">
<el-option :label="item.name" :value="item.id"
v-for="(item, index) in thirdCategory" />
</el-select>
</div>
</div>
</el-form-item>
<el-form-item label="商品卖点" prop="remark">
<el-input v-model="formData.remark" clearable placeholder="请输入商品卖点" />
</el-form-item>
<el-form-item label="商品主图" prop="image" required>
<material-picker v-model="formData.image" :limit="1" />
</el-form-item>
<el-form-item label="商品轮播图" prop="goods_image" required>
<material-picker v-model="formData.goods_image" :limit="8" />
</el-form-item>
</el-tab-pane>
<el-tab-pane label="价格库存">
<el-radio-group v-model="goodsSpec">
<el-radio :value="1">统一规格</el-radio>
<el-radio :value="2">多规格</el-radio>
</el-radio-group>
<!-- 单规格 -->
<div v-if="goodsSpec === 1">
<el-table :data="[{}]" style="width: 100%">
<el-table-column label="价格(元)" prop="one_price">
<template #default="scope">
<el-input v-model="formData.one_price" clearable placeholder="请输入价格(元)" />
</template>
</el-table-column>
<el-table-column label="成本价(元)" prop="one_cost_price">
<template #default="scope">
<el-input v-model="formData.one_cost_price" clearable placeholder="请输入成本价(元)" />
</template>
</el-table-column>
<el-table-column label="请输入库存" prop="one_stock">
<template #default="scope">
<el-input v-model="formData.one_stock" clearable placeholder="请输入库存" />
</template>
</el-table-column>
</el-table>
</div>
<!-- 单规格 -->
<div v-if="goodsSpec === 2">
<div>
<el-form-item label="规格项" prop="code">
<el-input v-model="formData.code" clearable placeholder="请填写规格名" />
<div>
<el-input v-model="specValue" clearable />
</div>
<div @click="addSpecValue" style="cursor: pointer;"> + 添加规格值</div>
</el-form-item>
</div>
<el-button type="primary">添加规格项目</el-button>
<!-- <el-table :data="[{}]" style="width: 100%">
<el-table-column label="*价格(元)" prop="one_price">
<template #default="scope">
<el-input v-model="formData.one_price" clearable placeholder="请输入价格(元)" />
</template>
</el-table-column>
<el-table-column label="*成本价(元)" prop="one_cost_price">
<template #default="scope">
<el-input v-model="formData.one_cost_price" clearable placeholder="请输入成本价(元)" />
</template>
</el-table-column>
<el-table-column label="*请输入库存" prop="one_stock">
<template #default="scope">
<el-input v-model="formData.one_stock" clearable placeholder="请输入库存" />
</template>
</el-table-column>
</el-table> -->
</div>
</el-tab-pane>
<el-tab-pane label="商品详情"></el-tab-pane>
<el-tab-pane label="销售设置"></el-tab-pane>
</el-tabs>
</el-form>
<el-dialog v-model="showSpec" title="输入规格值,多个请换行" width="500">
<el-input v-model="specValue" type="textarea" clearable :rows="3" />
<div class="mt-4">
<el-button type="default" @click="closeSpecPopup">取消</el-button>
<el-button type="primary" @click="confirmSpec">确定</el-button>
</div>
</el-dialog>
<!-- <el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules">
<el-form-item label="一级分类id" prop="first_category_id">
<el-input v-model="formData.first_category_id" clearable placeholder="请输入一级分类id" />
</el-form-item>
@ -143,7 +258,7 @@
<el-form-item label="是否开启上门自提:1-是;0-否;" prop="is_selffetch">
<el-input v-model="formData.is_selffetch" clearable placeholder="请输入是否开启上门自提:1-是;0-否;" />
</el-form-item>
</el-form>
</el-form> -->
</popup>
</div>
</template>
@ -151,7 +266,7 @@
<script lang="ts" setup name="goodsEdit">
import type { FormInstance } from 'element-plus'
import Popup from '@/components/popup/index.vue'
import { apiGoodsAdd, apiGoodsEdit, apiGoodsDetail } from '@/api/goods'
import { apiGoodsAdd, apiGoodsEdit, apiGoodsDetail, checkCategory } from '@/api/goods'
import { timeFormat } from '@/utils/util'
import type { PropType } from 'vue'
defineProps({
@ -164,13 +279,16 @@ const emit = defineEmits(['success', 'close'])
const formRef = shallowRef<FormInstance>()
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
const goodsSpec = ref(1)
let showSpec = ref(false)
let specValue = ref("")
// 弹窗标题
const popupTitle = computed(() => {
return mode.value == 'edit' ? '编辑商品主表' : '新增商品主表'
})
// 表单数据
const formData = reactive({
id: '',
@ -179,13 +297,20 @@ const formData = reactive({
first_category_id: '',
second_category_id: '',
third_category_id: '',
remark: '',
image: '',
goods_image: '',
one_price: '',
one_cost_price: '',
one_stock: '',
brand_id: '',
supplier_id: '',
status: '',
image: '',
video: '',
one_spec_image: '',
poster: '',
remark: '',
content: '',
sort: '',
sales_sum: '',
@ -232,71 +357,64 @@ const formRules = reactive<any>({
}],
first_category_id: [{
required: true,
message: '请输入一级分类id',
message: '请选择商品分类',
trigger: ['blur']
}],
second_category_id: [{
required: true,
message: '请输入二级分类id',
trigger: ['blur']
}],
third_category_id: [{
required: true,
message: '请输入三级分类id',
trigger: ['blur']
}],
status: [{
required: true,
message: '请输入商品状态:-1-回收站0-下架1-上架',
trigger: ['blur']
}],
image: [{
required: true,
message: '请输入商品主图',
trigger: ['blur']
}],
is_show_stock: [{
required: true,
message: '请输入是否显示库存1-0-否',
trigger: ['blur']
}],
free_shipping_type: [{
required: true,
message: '请输入运费类型1-包邮2-统一运费3-运费模板',
trigger: ['blur']
}],
is_commission: [{
required: true,
message: '请输入分销佣金1-开启0-不开启',
trigger: ['blur']
}],
is_share_bouns: [{
required: true,
message: '请输入区域股东分红1-开启0-不开启',
trigger: ['blur']
}],
is_team: [{
required: true,
message: '请输入是否开启拼团[0=否, 1=是]',
trigger: ['blur']
}],
is_integral: [{
required: true,
message: '请输入积分抵扣1-开启0-不开启',
trigger: ['blur']
}],
is_member: [{
required: true,
message: '请输入会员价1-开启0-不开启',
trigger: ['blur']
}],
give_integral_type: [{
required: true,
message: '请输入赠送积分类型0-不赠送1-赠送固定积分2-按比例赠送积分',
trigger: ['blur']
}]
// status: [{
// required: true,
// message: '请输入商品状态:-1-回收站0-下架1-上架',
// trigger: ['blur']
// }],
// image: [{
// required: true,
// message: '请输入商品主图',
// trigger: ['blur']
// }],
// is_show_stock: [{
// required: true,
// message: '请输入是否显示库存1-是0-否',
// trigger: ['blur']
// }],
// free_shipping_type: [{
// required: true,
// message: '请输入运费类型1-包邮2-统一运费3-运费模板',
// trigger: ['blur']
// }],
// is_commission: [{
// required: true,
// message: '请输入分销佣金1-开启0-不开启',
// trigger: ['blur']
// }],
// is_share_bouns: [{
// required: true,
// message: '请输入区域股东分红1-开启0-不开启',
// trigger: ['blur']
// }],
// is_team: [{
// required: true,
// message: '请输入是否开启拼团[0=否, 1=是]',
// trigger: ['blur']
// }],
// is_integral: [{
// required: true,
// message: '请输入积分抵扣1-开启0-不开启',
// trigger: ['blur']
// }],
// is_member: [{
// required: true,
// message: '请输入会员价1-开启0-不开启',
// trigger: ['blur']
// }],
// give_integral_type: [{
// required: true,
// message: '请输入赠送积分类型0-不赠送1-赠送固定积分2-按比例赠送积分',
// trigger: ['blur']
// }]
})
onMounted(() => {
category()
});
// 获取详情
const setFormData = async (data: Record<any, any>) => {
@ -314,12 +432,55 @@ const getDetail = async (row: Record<string, any>) => {
})
setFormData(data)
}
//
// 创建分类列表
let goodsCategory = reactive<any[]>([])
let firstCategory = reactive<any[]>([])
let secondCategory = reactive<any[]>([])
let thirdCategory = reactive<any[]>([])
const category = async () => {
const res = await checkCategory()
goodsCategory = res
firstCategory = res.filter((item: { pid: number }) => {
if (item.pid === 0) {
return item
}
})
}
// 选择第一个分类组,渲染第二个分组
const selectFirstCategory = (id: number) => {
secondCategory = goodsCategory.filter((item: { pid: number }) => {
if (item.pid === id) {
return item
}
})
if (secondCategory.length === 0) {
formData.second_category_id = ''
formData.third_category_id = ''
}
}
// 选择第二个分类组,渲染第三个分组
const selectSecondCategory = (id: number) => {
thirdCategory = goodsCategory.filter((item: { pid: number }) => {
if (item.pid === id) {
return item
}
})
if (thirdCategory.length === 0) {
formData.third_category_id = ''
}
}
// 提交按钮
const handleSubmit = async () => {
await formRef.value?.validate()
// await formRef.value?.validate()
const data = { ...formData, }
console.log("data>>>", data);
return false;
mode.value == 'edit'
? await apiGoodsEdit(data)
: await apiGoodsAdd(data)
@ -339,10 +500,32 @@ const handleClose = () => {
}
// 添加规格值
const addSpecValue = () => {
showSpec.value = true
}
// 关闭规格值弹窗
const closeSpecPopup = () => {
specValue.value = ''
showSpec.value = false
}
const confirmSpec = () => {
if (specValue.value) {
let specs = specValue.value.split('\n');
for (let i in specs) {
specs[i] = specs[i].trim();
}
}
}
defineExpose({
open,
setFormData,
getDetail
getDetail,
selectFirstCategory,
selectSecondCategory
})
</script>

View File

@ -2,184 +2,133 @@
<div class="edit-popup">
<popup ref="popupRef" :title="popupTitle" :async="true" width="550px" @confirm="handleSubmit"
@close="handleClose">
<el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules"></el-form>
<el-tabs type="border-card">
<el-tab-pane label="基础设置">
<el-form-item label="商品名称" prop="name">
<el-input v-model="formData.name" clearable placeholder="请输入商品名称" />
</el-form-item>
<el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules">
<el-tabs type="border-card">
<el-tab-pane label="基础设置">
<el-form-item label="商品名称" prop="name" required>
<el-input v-model="formData.name" clearable placeholder="请输入商品名称" />
</el-form-item>
<el-form-item label="商品编码" prop="code">
<el-input v-model="formData.code" clearable placeholder="请输入商品编码" />
</el-form-item>
<el-form-item label="商品编码" prop="code">
<el-input v-model="formData.code" clearable placeholder="请输入商品编码" />
</el-form-item>
<el-form-item label="商品分类" prop="code">
<div>
<el-select v-model="formData.region" placeholder="please select your zone">
<el-option label="请选择分类" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
<el-form-item label="商品分类" prop="first_category_id" required>
<div class="flex w-full">
<div class="w-4/12">
<el-select v-model="formData.first_category_id" placeholder="请选择分类"
@change="selectFirstCategory">
<el-option :label="item.name" :value="item.id"
v-for="(item, index) in firstCategory" />
</el-select>
</div>
<div class="w-4/12">
<el-select v-model="formData.second_category_id" placeholder="请选择分类"
@change="selectSecondCategory">
<el-option :label="item.name" :value="item.id"
v-for="(item, index) in secondCategory" />
</el-select>
</div>
<div class="w-4/12">
<el-select v-model="formData.third_category_id" placeholder="请选择分类">
<el-option :label="item.name" :value="item.id"
v-for="(item, index) in thirdCategory" />
</el-select>
</div>
</div>
</el-form-item>
<el-form-item label="商品卖点" prop="remark">
<el-input v-model="formData.remark" clearable placeholder="请输入商品卖点" />
</el-form-item>
<el-form-item label="商品主图" prop="image" required>
<material-picker v-model="formData.image" :limit="1" />
</el-form-item>
<el-form-item label="商品轮播图" prop="goods_image" required>
<material-picker v-model="formData.goods_image" :limit="8" />
</el-form-item>
</el-tab-pane>
<el-tab-pane label="价格库存">
<el-radio-group v-model="formData.spec_type">
<el-radio :value="1">统一规格</el-radio>
<el-radio :value="2">多规格</el-radio>
</el-radio-group>
<!-- 单规格 -->
<div v-if="formData.spec_type === 1">
<el-table :data="[{}]" style="width: 100%">
<el-table-column label="价格(元)" prop="one_price">
<template #default="scope">
<el-input v-model="formData.one_price" clearable placeholder="请输入价格(元)" />
</template>
</el-table-column>
<el-table-column label="成本价(元)" prop="one_cost_price">
<template #default="scope">
<el-input v-model="formData.one_cost_price" clearable placeholder="请输入成本价(元)" />
</template>
</el-table-column>
<el-table-column label="请输入库存" prop="one_stock">
<template #default="scope">
<el-input v-model="formData.one_stock" clearable placeholder="请输入库存" />
</template>
</el-table-column>
</el-table>
</div>
<!-- 多规格 -->
<div v-if="formData.spec_type === 2">
<div v-for="(spec, specIdx) in specs" :key="specIdx" class="mb-2">
<el-form-item :label="'规格名'">
<el-input v-model="spec.name" placeholder="如口味/尺寸" style="width: 100px;" />
</el-form-item>
<el-form-item :label="'规格值'" style="flex:1;">
<el-input v-model="spec.valuesStr" type="textarea" :rows="2" placeholder="每行一个规格值"
@change="onSpecValueChange(specIdx)" />
</el-form-item>
<div class="flex justify-end">
<el-button type="danger" @click="removeSpec(specIdx)">删除</el-button>
</div>
</div>
<el-button type="primary" @click="addSpec" :disabled="specs.length >= 3"
class="mb-2">添加规格项</el-button>
<div>
<el-select v-model="formData.region" placeholder="please select your zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
<el-table :data="skuTable" style="width: 100%; margin-top: 10px;">
<el-table-column v-for="(spec, idx) in specs" :key="idx"
:label="spec.name || `规格${idx + 1}`" :prop="'spec' + idx" />
<el-table-column label="价格(元)">
<template #default="scope">
<el-input v-model="scope.row.price" placeholder="价格" />
</template>
</el-table-column>
<el-table-column label="成本价(元)">
<template #default="scope">
<el-input v-model="scope.row.cost_price" placeholder="成本价" />
</template>
</el-table-column>
<el-table-column label="库存">
<template #default="scope">
<el-input v-model="scope.row.stock" placeholder="库存" />
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="商品详情">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef"
:defaultConfig="toolbarConfig" :mode="mode" />
<Editor style="height: 500px; overflow-y: hidden;" v-model="formData.content"
:defaultConfig="editorConfig" :mode="mode" @onCreated="handleCreated" />
</el-tab-pane>
<el-tab-pane label="销售设置"></el-tab-pane>
</el-tabs>
</el-form>
<div>
<el-select v-model="formData.region" placeholder="please select your zone">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</div>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="价格库存"></el-tab-pane>
<el-tab-pane label="商品详情"></el-tab-pane>
<el-tab-pane label="销售设置"></el-tab-pane>
</el-tabs>
<!-- <el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules">
<el-form-item label="一级分类id" prop="first_category_id">
<el-input v-model="formData.first_category_id" clearable placeholder="请输入一级分类id" />
</el-form-item>
<el-form-item label="二级分类id" prop="second_category_id">
<el-input v-model="formData.second_category_id" clearable placeholder="请输入二级分类id" />
</el-form-item>
<el-form-item label="三级分类id" prop="third_category_id">
<el-input v-model="formData.third_category_id" clearable placeholder="请输入三级分类id" />
</el-form-item>
<el-form-item label="品牌id" prop="brand_id">
<el-input v-model="formData.brand_id" clearable placeholder="请输入品牌id" />
</el-form-item>
<el-form-item label="供应商id" prop="supplier_id">
<el-input v-model="formData.supplier_id" clearable placeholder="请输入供应商id" />
</el-form-item>
<el-form-item label="商品状态:-1-回收站0-下架1-上架" prop="status">
<el-input v-model="formData.status" clearable placeholder="请输入商品状态:-1-回收站0-下架1-上架" />
</el-form-item>
<el-form-item label="商品主图" prop="image">
<el-input v-model="formData.image" clearable placeholder="请输入商品主图" />
</el-form-item>
<el-form-item label="商品视频" prop="video">
<el-input v-model="formData.video" clearable placeholder="请输入商品视频" />
</el-form-item>
<el-form-item label="商品自定义海报" prop="poster">
<el-input v-model="formData.poster" clearable placeholder="请输入商品自定义海报" />
</el-form-item>
<el-form-item label="商品简介" prop="remark">
<el-input v-model="formData.remark" clearable placeholder="请输入商品简介" />
</el-form-item>
<el-form-item label="商品详细描述" prop="content">
<el-input v-model="formData.content" clearable placeholder="请输入商品详细描述" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="formData.sort" clearable placeholder="请输入排序" />
</el-form-item>
<el-form-item label="商品销量" prop="sales_sum">
<el-input v-model="formData.sales_sum" clearable placeholder="请输入商品销量" />
</el-form-item>
<el-form-item label="虚拟销量" prop="virtual_sales_sum">
<el-input v-model="formData.virtual_sales_sum" clearable placeholder="请输入虚拟销量" />
</el-form-item>
<el-form-item label="商品点击量" prop="click_count">
<el-input v-model="formData.click_count" clearable placeholder="请输入商品点击量" />
</el-form-item>
<el-form-item label="虚拟点击量" prop="virtual_click">
<el-input v-model="formData.virtual_click" clearable placeholder="请输入虚拟点击量" />
</el-form-item>
<el-form-item label="商品规格:1-统一规格2-多规格;" prop="spec_type">
<el-input v-model="formData.spec_type" clearable placeholder="请输入商品规格:1-统一规格2-多规格;" />
</el-form-item>
<el-form-item label="最高价格" prop="max_price">
<el-input v-model="formData.max_price" clearable placeholder="请输入最高价格" />
</el-form-item>
<el-form-item label="最低价格" prop="min_price">
<el-input v-model="formData.min_price" clearable placeholder="请输入最低价格" />
</el-form-item>
<el-form-item label="市场价sku中最高的市场价" prop="market_price">
<el-input v-model="formData.market_price" clearable placeholder="请输入市场价sku中最高的市场价" />
</el-form-item>
<el-form-item label="总库存" prop="stock">
<el-input v-model="formData.stock" clearable placeholder="请输入总库存" />
</el-form-item>
<el-form-item label="库存预警" prop="stock_warn">
<el-input v-model="formData.stock_warn" clearable placeholder="请输入库存预警" />
</el-form-item>
<el-form-item label="是否显示库存1-是0-否" prop="is_show_stock">
<el-input v-model="formData.is_show_stock" clearable placeholder="请输入是否显示库存1-是0-否" />
</el-form-item>
<el-form-item label="运费类型1-包邮2-统一运费3-运费模板" prop="free_shipping_type">
<el-input v-model="formData.free_shipping_type" clearable
placeholder="请输入运费类型1-包邮2-统一运费3-运费模板" />
</el-form-item>
<el-form-item label="统一运费金额" prop="free_shipping">
<el-input v-model="formData.free_shipping" clearable placeholder="请输入统一运费金额" />
</el-form-item>
<el-form-item label="运费模板" prop="free_shipping_template_id">
<el-input v-model="formData.free_shipping_template_id" clearable placeholder="请输入运费模板" />
</el-form-item>
<el-form-item label="分销佣金1-开启0-不开启" prop="is_commission">
<el-input v-model="formData.is_commission" clearable placeholder="请输入分销佣金1-开启0-不开启" />
</el-form-item>
<el-form-item label="一级分销比例" prop="first_ratio">
<el-input v-model="formData.first_ratio" clearable placeholder="请输入一级分销比例" />
</el-form-item>
<el-form-item label="二级分销比例" prop="second_ratio">
<el-input v-model="formData.second_ratio" clearable placeholder="请输入二级分销比例" />
</el-form-item>
<el-form-item label="三级分销比例" prop="three_ratio">
<el-input v-model="formData.three_ratio" clearable placeholder="请输入三级分销比例" />
</el-form-item>
<el-form-item label="区域股东分红1-开启0-不开启" prop="is_share_bouns">
<el-input v-model="formData.is_share_bouns" clearable placeholder="请输入区域股东分红1-开启0-不开启" />
</el-form-item>
<el-form-item label="区域分红比例" prop="region_ratio">
<el-input v-model="formData.region_ratio" clearable placeholder="请输入区域分红比例" />
</el-form-item>
<el-form-item label="股东分红比例" prop="shareholder_ratio">
<el-input v-model="formData.shareholder_ratio" clearable placeholder="请输入股东分红比例" />
</el-form-item>
<el-form-item label="新品推荐1-是0-否" prop="is_new">
<el-input v-model="formData.is_new" clearable placeholder="请输入新品推荐1-是0-否" />
</el-form-item>
<el-form-item label="好物优选1-是0-否" prop="is_best">
<el-input v-model="formData.is_best" clearable placeholder="请输入好物优选1-是0-否" />
</el-form-item>
<el-form-item label="猜你喜欢1-是0-否" prop="is_like">
<el-input v-model="formData.is_like" clearable placeholder="请输入猜你喜欢1-是0-否" />
</el-form-item>
<el-form-item label="是否开启拼团[0=否, 1=是]" prop="is_team">
<el-input v-model="formData.is_team" clearable placeholder="请输入是否开启拼团[0=否, 1=是]" />
</el-form-item>
<el-form-item label="积分抵扣1-开启0-不开启" prop="is_integral">
<el-input v-model="formData.is_integral" clearable placeholder="请输入积分抵扣1-开启0-不开启" />
</el-form-item>
<el-form-item label="会员价1-开启0-不开启" prop="is_member">
<el-input v-model="formData.is_member" clearable placeholder="请输入会员价1-开启0-不开启" />
</el-form-item>
<el-form-item label="赠送积分类型0-不赠送1-赠送固定积分2-按比例赠送积分" prop="give_integral_type">
<el-input v-model="formData.give_integral_type" clearable
placeholder="请输入赠送积分类型0-不赠送1-赠送固定积分2-按比例赠送积分" />
</el-form-item>
<el-form-item label="赠送积分;" prop="give_integral">
<el-input v-model="formData.give_integral" clearable placeholder="请输入赠送积分;" />
</el-form-item>
<el-form-item label="是否删除1-是0-否" prop="del">
<el-input v-model="formData.del" clearable placeholder="请输入是否删除1-是0-否" />
</el-form-item>
<el-form-item label="是否开启快递配送:1-是;0-否;" prop="is_express">
<el-input v-model="formData.is_express" clearable placeholder="请输入是否开启快递配送:1-是;0-否;" />
</el-form-item>
<el-form-item label="是否开启上门自提:1-是;0-否;" prop="is_selffetch">
<el-input v-model="formData.is_selffetch" clearable placeholder="请输入是否开启上门自提:1-是;0-否;" />
</el-form-item>
</el-form> -->
<!-- <el-dialog v-model="showImagePicker" title="选择图片" width="800px" @close="showImagePicker = false">
<material-picker :limit="10" @change="onImageSelected" />
</el-dialog> -->
<!-- <material-picker ref="materialPickerRef" :limit="10" @change="onImageSelected" style="display: none;" /> -->
</popup>
</div>
</template>
@ -188,8 +137,10 @@
import type { FormInstance } from 'element-plus'
import Popup from '@/components/popup/index.vue'
import { apiGoodsAdd, apiGoodsEdit, apiGoodsDetail, checkCategory } from '@/api/goods'
import { timeFormat } from '@/utils/util'
import type { PropType } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import '@wangeditor/editor/dist/css/style.css' // 引入 css
defineProps({
dictData: {
type: Object as PropType<Record<string, any[]>>,
@ -200,13 +151,14 @@ const emit = defineEmits(['success', 'close'])
const formRef = shallowRef<FormInstance>()
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
const goodsSpec = ref(2)
// 弹窗标题
const popupTitle = computed(() => {
return mode.value == 'edit' ? '编辑商品主表' : '新增商品主表'
})
// 表单数据
const formData = reactive({
id: '',
@ -215,20 +167,28 @@ const formData = reactive({
first_category_id: '',
second_category_id: '',
third_category_id: '',
remark: '',
image: '',
goods_image: '',
one_price: '',
one_cost_price: '',
one_stock: '',
spec_name: '',
spec_type: 1,
content: '',
brand_id: '',
supplier_id: '',
status: '',
image: '',
video: '',
one_spec_image: '',
poster: '',
remark: '',
content: '',
sort: '',
sales_sum: '',
virtual_sales_sum: '',
click_count: '',
virtual_click: '',
spec_type: '',
max_price: '',
min_price: '',
market_price: '',
@ -268,69 +228,59 @@ const formRules = reactive<any>({
}],
first_category_id: [{
required: true,
message: '请输入一级分类id',
message: '请选择商品分类',
trigger: ['blur']
}],
second_category_id: [{
required: true,
message: '请输入二级分类id',
trigger: ['blur']
}],
third_category_id: [{
required: true,
message: '请输入三级分类id',
trigger: ['blur']
}],
status: [{
required: true,
message: '请输入商品状态:-1-回收站0-下架1-上架',
trigger: ['blur']
}],
image: [{
required: true,
message: '请输入商品主图',
trigger: ['blur']
}],
is_show_stock: [{
required: true,
message: '请输入是否显示库存1-0-否',
trigger: ['blur']
}],
free_shipping_type: [{
required: true,
message: '请输入运费类型1-包邮2-统一运费3-运费模板',
trigger: ['blur']
}],
is_commission: [{
required: true,
message: '请输入分销佣金1-开启0-不开启',
trigger: ['blur']
}],
is_share_bouns: [{
required: true,
message: '请输入区域股东分红1-开启0-不开启',
trigger: ['blur']
}],
is_team: [{
required: true,
message: '请输入是否开启拼团[0=否, 1=是]',
trigger: ['blur']
}],
is_integral: [{
required: true,
message: '请输入积分抵扣1-开启0-不开启',
trigger: ['blur']
}],
is_member: [{
required: true,
message: '请输入会员价1-开启0-不开启',
trigger: ['blur']
}],
give_integral_type: [{
required: true,
message: '请输入赠送积分类型0-不赠送1-赠送固定积分2-按比例赠送积分',
trigger: ['blur']
}]
// status: [{
// required: true,
// message: '请输入商品状态:-1-回收站0-下架1-上架',
// trigger: ['blur']
// }],
// image: [{
// required: true,
// message: '请输入商品主图',
// trigger: ['blur']
// }],
// is_show_stock: [{
// required: true,
// message: '请输入是否显示库存1-是0-否',
// trigger: ['blur']
// }],
// free_shipping_type: [{
// required: true,
// message: '请输入运费类型1-包邮2-统一运费3-运费模板',
// trigger: ['blur']
// }],
// is_commission: [{
// required: true,
// message: '请输入分销佣金1-开启0-不开启',
// trigger: ['blur']
// }],
// is_share_bouns: [{
// required: true,
// message: '请输入区域股东分红1-开启0-不开启',
// trigger: ['blur']
// }],
// is_team: [{
// required: true,
// message: '请输入是否开启拼团[0=否, 1=是]',
// trigger: ['blur']
// }],
// is_integral: [{
// required: true,
// message: '请输入积分抵扣1-开启0-不开启',
// trigger: ['blur']
// }],
// is_member: [{
// required: true,
// message: '请输入会员价1-开启0-不开启',
// trigger: ['blur']
// }],
// give_integral_type: [{
// required: true,
// message: '请输入赠送积分类型0-不赠送1-赠送固定积分2-按比例赠送积分',
// trigger: ['blur']
// }]
})
onMounted(() => {
@ -353,18 +303,71 @@ const getDetail = async (row: Record<string, any>) => {
})
setFormData(data)
}
//
// 创建分类列表
let goodsCategory = reactive<any[]>([])
let firstCategory = reactive<any[]>([])
let secondCategory = reactive<any[]>([])
let thirdCategory = reactive<any[]>([])
const category = async () => {
const { data, code } = await checkCategory({})
console.log("data,code>>>", data, code);
return false;
const res = await checkCategory()
goodsCategory = res
firstCategory = res.filter((item: { pid: number }) => {
if (item.pid === 0) {
return item
}
})
}
// 选择第一个分类组,渲染第二个分组
const selectFirstCategory = (id: number) => {
secondCategory = goodsCategory.filter((item: { pid: number }) => {
if (item.pid === id) {
return item
}
})
if (secondCategory.length === 0) {
formData.second_category_id = ''
formData.third_category_id = ''
}
}
// 选择第二个分类组,渲染第三个分组
const selectSecondCategory = (id: number) => {
thirdCategory = goodsCategory.filter((item: { pid: number }) => {
if (item.pid === id) {
return item
}
})
if (thirdCategory.length === 0) {
formData.third_category_id = ''
}
}
// 提交按钮
const handleSubmit = async () => {
await formRef.value?.validate()
// await formRef.value?.validate()
const data = { ...formData, }
data.price = skuTable.value.map(item => item.price)
data.cost_price = skuTable.value.map(item => item.cost_price)
data.stock = skuTable.value.map(item => item.stock)
data.spec_value_str = skuTable.value.map(item => {
const arr = []
if ('spec0' in item) arr.push(item.spec0)
if ('spec1' in item) arr.push(item.spec1)
if ('spec2' in item) arr.push(item.spec2)
return arr.join(',')
})
// data.spec_name = specs.value.map(item => item.name);
// data.spec_values = specs.value.map(item => item.valuesStr.replace(/\n/g, ','));
console.log("data>>>", data);
// console.log("specs>>>", specs);
// console.log("specs>>>", skuTable);
return false;
mode.value == 'edit'
? await apiGoodsEdit(data)
: await apiGoodsAdd(data)
@ -383,11 +386,151 @@ const handleClose = () => {
emit('close')
}
// 多规格相关
// const specs = ref([
// { name: '', valuesStr: '', values: [] }
// ])
const specs = ref([])
const skuTable = ref<any[]>([])
// 添加规格项最多3个
const addSpec = () => {
if (specs.value.length < 3) {
specs.value.push({ name: '', valuesStr: '', values: [] })
syncSpecToFormData()
}
}
// 删除规格项
const removeSpec = (idx: number) => {
specs.value.splice(idx, 1)
updateSkuTable()
syncSpecToFormData()
}
// 规格值变更
const onSpecValueChange = (idx: number) => {
const spec = specs.value[idx]
spec.values = spec.valuesStr
.split('\n')
.map(v => v.trim())
.filter(v => v)
updateSkuTable()
syncSpecToFormData()
}
// 计算所有规格组合(笛卡尔积)
function cartesianProduct(arr: any[][]) {
if (arr.length === 0) return []
return arr.reduce((a, b) =>
a.flatMap(d => b.map(e => [].concat(d, e)))
)
}
// 同步规格数据到formData
const syncSpecToFormData = () => {
formData.spec_name = specs.value.map(item => item.name)
formData.spec_values = specs.value.map(item => item.valuesStr.replace(/\n/g, ','))
}
// 更新SKU表格
const updateSkuTable = () => {
const valueArr = specs.value.map(s => s.values)
if (valueArr.some(arr => arr.length === 0)) {
skuTable.value = []
return
}
let result: any[] = []
if (valueArr.length === 1) {
result = valueArr[0].map(v1 => ({
spec0: v1,
price: '',
cost_price: '',
stock: ''
}))
} else if (valueArr.length === 2) {
result = cartesianProduct(valueArr).map(([v1, v2]) => ({
spec0: v1,
spec1: v2,
price: '',
cost_price: '',
stock: ''
}))
} else if (valueArr.length === 3) {
result = cartesianProduct(valueArr).map(([v1, v2, v3]) => ({
spec0: v1,
spec1: v2,
spec2: v3,
price: '',
cost_price: '',
stock: ''
}))
}
skuTable.value = result
}
// 编辑器
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
const toolbarConfig = {}
const materialPickerRef = ref()
const showImagePicker = ref(false)
let imageInsertFn: ((url: string) => void) | null = null
const editorConfig = {
placeholder: '请输入内容...',
MENU_CONF: {
uploadImage: {
// 自定义上传图片
customBrowseAndUpload(insertFn: (url: string, alt: string, href: string) => void) {
showImagePicker.value = true
imageInsertFn = (url: string) => {
insertFn(url, '', '')
// 直接调用 material-picker 的打开方法
materialPickerRef.value?.open(-1)
}
}
}
}
}
// 图片选择后回调
function onImageSelected(val: string | string[]) {
const urls = Array.isArray(val) ? val : [val]
if (imageInsertFn && urls.length) {
urls.forEach(url => {
imageInsertFn!(url)
})
imageInsertFn = null
}
showImagePicker.value = false
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
// 监听规格变化自动生成表格
watch(specs, updateSkuTable, { deep: true })
defineExpose({
open,
setFormData,
getDetail
getDetail,
selectFirstCategory,
selectSecondCategory,
editorRef,
mode: 'default', // 或 'simple'
toolbarConfig,
editorConfig,
handleCreated,
})
</script>

View File

@ -1,18 +1,12 @@
<template>
<div class="edit-popup">
<popup
ref="popupRef"
:title="popupTitle"
:async="true"
width="550px"
@confirm="handleSubmit"
@close="handleClose"
>
<popup ref="popupRef" :title="popupTitle" :async="true" width="550px" @confirm="handleSubmit"
@close="handleClose">
<el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules">
<el-form-item label="分类名称" prop="name">
<el-input v-model="formData.name" clearable placeholder="请输入分类名称" />
</el-form-item>
<el-form-item label="父级id" prop="pid">
<el-form-item label="父级分类" prop="pid">
<el-input v-model="formData.pid" clearable placeholder="请输入父级id" />
</el-form-item>
<el-form-item label="等级" prop="level">
@ -102,8 +96,8 @@ const setFormData = async (data: Record<any, any>) => {
formData[key] = data[key]
}
}
}
const getDetail = async (row: Record<string, any>) => {
@ -117,9 +111,9 @@ const getDetail = async (row: Record<string, any>) => {
// 提交按钮
const handleSubmit = async () => {
await formRef.value?.validate()
const data = { ...formData, }
mode.value == 'edit'
? await apiGoodsCategoryEdit(data)
const data = { ...formData, }
mode.value == 'edit'
? await apiGoodsCategoryEdit(data)
: await apiGoodsCategoryAdd(data)
popupRef.value?.close()
emit('success')

View File

@ -1,38 +1,10 @@
<template>
<div>
<el-card class="!border-none mb-4" shadow="never">
<el-form
class="mb-[-16px]"
:model="queryParams"
inline
>
<el-form class="mb-[-16px]" :model="queryParams" inline>
<el-form-item label="分类名称" prop="name">
<el-input class="w-[280px]" v-model="queryParams.name" clearable placeholder="请输入分类名称" />
</el-form-item>
<el-form-item label="父级id" prop="pid">
<el-input class="w-[280px]" v-model="queryParams.pid" clearable placeholder="请输入父级id" />
</el-form-item>
<el-form-item label="等级" prop="level">
<el-input class="w-[280px]" v-model="queryParams.level" clearable placeholder="请输入等级" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input class="w-[280px]" v-model="queryParams.sort" clearable placeholder="请输入排序" />
</el-form-item>
<el-form-item label="是否显示:1-是;0-否" prop="is_show">
<el-input class="w-[280px]" v-model="queryParams.is_show" clearable placeholder="请输入是否显示:1-是;0-否" />
</el-form-item>
<el-form-item label="是否首页推荐1-是0-否" prop="is_recommend">
<el-input class="w-[280px]" v-model="queryParams.is_recommend" clearable placeholder="请输入是否首页推荐1-是0-否" />
</el-form-item>
<el-form-item label="分类图片" prop="image">
<el-input class="w-[280px]" v-model="queryParams.image" clearable placeholder="请输入分类图片" />
</el-form-item>
<el-form-item label="分类描述" prop="remark">
<el-input class="w-[280px]" v-model="queryParams.remark" clearable placeholder="请输入分类描述" />
</el-form-item>
<el-form-item label="删除标志:1-是0-否" prop="del">
<el-input class="w-[280px]" v-model="queryParams.del" clearable placeholder="请输入删除标志:1-是0-否" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
@ -46,15 +18,12 @@
</template>
新增
</el-button>
<el-button
v-perms="['goods_category/delete']"
:disabled="!selectData.length"
@click="handleDelete(selectData)"
>
<el-button v-perms="['goods_category/delete']" :disabled="!selectData.length"
@click="handleDelete(selectData)">
删除
</el-button>
<div class="mt-4">
<el-table :data="pager.lists" @selection-change="handleSelectionChange">
<!-- <el-table :data="pager.lists" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="分类名称" prop="name" show-overflow-tooltip />
<el-table-column label="父级id" prop="pid" show-overflow-tooltip />
@ -85,7 +54,28 @@
</el-button>
</template>
</el-table-column>
</el-table> -->
<el-table :data="pager.lists" row-key="id" border default-expand-all>
<el-table-column prop="name" label="分类名称" />
<el-table-column prop="id" label="分类图片">
<template #default="{ row }">
<el-image style="width: 100px; height: 100px" :src="row.image" />
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button v-perms="['goods_category/edit']" type="primary" link @click="handleEdit(row)">
编辑
</el-button>
<el-button v-perms="['goods_category/delete']" type="danger" link
@click="handleDelete(row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex mt-4 justify-end">
<pagination v-model="pager" @change="getLists" />
@ -111,14 +101,6 @@ const showEdit = ref(false)
// 查询条件
const queryParams = reactive({
name: '',
pid: '',
level: '',
sort: '',
is_show: '',
is_recommend: '',
image: '',
remark: '',
del: ''
})
// 选中数据
@ -138,6 +120,7 @@ const { pager, getLists, resetParams, resetPage } = usePaging({
params: queryParams
})
// 添加
const handleAdd = async () => {
showEdit.value = true
@ -162,4 +145,3 @@ const handleDelete = async (id: number | any[]) => {
getLists()
</script>