feat: 商品规格功能调整

This commit is contained in:
duan 2025-02-21 14:41:43 +08:00
parent dc5f664143
commit 43ae9f371d
17 changed files with 954 additions and 140 deletions

View File

@ -80,5 +80,8 @@
"scss.lint.unknownAtRules": "ignore",
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"[html]": {
"editor.defaultFormatter": "vscode.html-language-features"
}
}

View File

@ -3,6 +3,14 @@ const baseURL = "/product/admin/prod/spec";
// 商品管理-商品规格
const AuthAPI = {
// 新快捷新增
quickAdd(data: any) {
return request<any, Responseres>({
url: `${baseURL}/quickAdd`,
method: "post",
data,
});
},
/** 列表*/
getPage(params: any) {
return request<any, Responseres>({

View File

@ -798,6 +798,8 @@ function fetchPageData(formData: IObject = {}, isRestart = false) {
if (isRestart) {
pagination.currentPage = 1;
}
console.log(props, 'debug1')
console.log(props.contentConfig, 'debug2')
props.contentConfig
.indexAction(
showPagination
@ -809,6 +811,7 @@ function fetchPageData(formData: IObject = {}, isRestart = false) {
: formData
)
.then((data) => {
if (showPagination) {
if (props.contentConfig.parseData) {
data = props.contentConfig.parseData(data);

View File

@ -3,7 +3,9 @@ import type { IObject, PageContentInstance, PageModalInstance, PageSearchInstanc
function usePage() {
const searchRef = ref<PageSearchInstance>();
const searchRefs = ref<PageSearchInstance>();
const contentRef = ref<PageContentInstance>();
const contentRefs = ref<PageContentInstance>();
const addModalRef = ref<PageModalInstance>();
const editModalRef = ref<PageModalInstance>();
// 搜索
@ -11,6 +13,11 @@ function usePage() {
const filterParams = contentRef.value?.getFilterParams();
contentRef.value?.fetchPageData({ ...queryParams, ...filterParams }, true);
}
// 搜索2
function searchs(queryParams: IObject) {
const filterParams = contentRefs.value?.getFilterParams();
contentRefs.value?.fetchPageData({ ...queryParams, ...filterParams }, true);
}
// 重置
function handleResetClick(queryParams: IObject) {
const filterParams = contentRef.value?.getFilterParams();
@ -50,10 +57,13 @@ function usePage() {
return {
searchRef,
searchRefs,
contentRef,
contentRefs,
addModalRef,
editModalRef,
handleQueryClick,
searchs,
handleResetClick,
handleAddClick,
handleEditClick,

View File

@ -1,18 +1,11 @@
<!-- 图片上传组件 -->
<template>
<el-upload
v-model:file-list="fileList"
list-type="picture-card"
:before-upload="handleBeforeUpload"
:http-request="handleUpload"
:on-success="handleSuccess"
:on-error="handleError"
:on-exceed="handleExceed"
:accept="props.accept"
:limit="props.limit"
multiple
>
<el-icon><Plus /></el-icon>
<el-upload v-model:file-list="fileList" list-type="picture-card" :before-upload="handleBeforeUpload"
:http-request="handleUpload" :on-success="handleSuccess" :on-error="handleError" :on-exceed="handleExceed"
:accept="props.accept" :limit="props.limit" multiple>
<el-icon>
<Plus />
</el-icon>
<template #file="{ file }">
<div style="width: 100%">
<img class="el-upload-list__item-thumbnail" :src="file.url" />
@ -23,24 +16,21 @@
</span>
<!-- 删除 -->
<span @click="handleRemove(file.url!)">
<el-icon><Delete /></el-icon>
<el-icon>
<Delete />
</el-icon>
</span>
</span>
</div>
</template>
</el-upload>
<el-image-viewer
v-if="previewVisible"
:zoom-rate="1.2"
:initial-index="previewImageIndex"
:url-list="modelValue"
@close="handlePreviewClose"
/>
<el-image-viewer v-if="previewVisible" :zoom-rate="1.2" :initial-index="previewImageIndex" :url-list="modelValue"
@close="handlePreviewClose" />
</template>
<script setup lang="ts">
import { UploadRawFile, UploadRequestOptions, UploadUserFile } from "element-plus";
import FileAPI, { FileInfo } from "@/api/file";
import FileAPI, { FileInfo } from "@/api/account/common";
const props = defineProps({
/**
@ -180,9 +170,9 @@ const handleSuccess = (fileInfo: FileInfo, uploadFile: UploadUserFile) => {
ElMessage.success("上传成功");
const index = fileList.value.findIndex((file) => file.uid === uploadFile.uid);
if (index !== -1) {
fileList.value[index].url = fileInfo.url;
fileList.value[index].url = fileInfo;
fileList.value[index].status = "success";
modelValue.value[index] = fileInfo.url;
modelValue.value[index] = fileInfo;
}
};

View File

@ -0,0 +1,55 @@
<template>
<!-- 新增添加商品 -->
<myDialog title="选择商品" width="50%" ref="myDialogRef" @Confirm="subitgood">
<page-search ref="searchRef" :search-config="searchConfig" @query-click="handleQueryClick"
@reset-click="handleResetClick" />
<page-content ref="contentRef" :content-config="contentConfig" @filter-change="handleFilterChange">
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template>
<template #gender="scope">
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
</template>
<template #timemanagement="scope">
{{ scope.row.startTime }}-{{ scope.row.endTime }}
</template>
<template #mobile="scope">
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
</page-content>
</myDialog>
</template>
<script setup lang="ts">
import myDialog from '@/components/mycomponents/myDialog.vue'
import usePage from "@/components/CURD/usePage";
import searchConfig from "./shopListconfig/search"
import contentConfig from "./shopListconfig/content2";
const {
searchRef,
contentRef,
addModalRef,
editModalRef,
handleQueryClick,
handleResetClick,
// handleAddClick,
// handleEditClick,
handleSubmitClick,
handleExportClick,
handleSearchClick,
handleFilterChange,
} = usePage();
let myDialogRef: any = ref(null)
function opens() {
myDialogRef.value.open()
}
//
function subitgood() {
// selectData.value = contentRefs.value.getselectTable()
// myDialogRef.value.close()
}
defineExpose({ opens })
</script>

View File

@ -0,0 +1,40 @@
import type { IContentConfig } from "@/components/CURD/types";
import UserAPI from "@/api/product/index";
const contentConfig: IContentConfig = {
pageName: "sys:user",
table: {
showOverflowTooltip: true,
},
toolbar: [],
pagination: {
background: true,
layout: "prev,pager,next,jumper,total,sizes",
pageSize: 10,
pageSizes: [10, 20, 30, 50],
},
indexAction: function (params) {
return UserAPI.getPage(params);
},
modifyAction(data) {
// 模拟发起网络请求修改字段
// console.log("modifyAction:", data);
ElMessage.success(JSON.stringify(data));
return Promise.resolve(null);
},
cols: [
{ type: "selection", width: 50, align: "center" },
{ label: "封面图", align: "center", prop: "coverImg", templet: "image" },
{ label: "商品信息", align: "center", prop: "name" },
{ label: "规格", align: "center", prop: "specSnap", },
{ label: "是否售罄", align: "center", prop: "isPauseSale", },
{ label: "售价", align: "center", prop: "lowPrice", },
{ label: "库存", align: "center", prop: "realSalesNumber", },
{ label: "销量", align: "center", prop: "stockNumber", },
{ label: "分类名称", align: "center", prop: "categoryName", },
],
};
export default contentConfig;

View File

@ -0,0 +1,41 @@
import DeptAPI from "@/api/product/specificationsconfig";
import UserAPI from "@/api/onlineShop/goodsGroupconfig";
import type { ISearchConfig } from "@/components/CURD/types";
const searchConfig: ISearchConfig = {
pageName: "sys:user",
formItems: [
{
type: "input",
label: "商品名称",
prop: "name",
attrs: {
placeholder: "请输入商品名称",
clearable: true,
style: {
width: "200px",
},
},
},
{
type: "select",
label: "商品分类",
prop: "categoryId",
attrs: {
placeholder: "请选择商品分类",
clearable: true,
style: {
width: "200px",
},
},
options: [],
async initFn(formItem) {
formItem.options = await UserAPI.getList();
},
},
],
};
export default searchConfig;

View File

@ -86,7 +86,7 @@
</template>
<!-- 新增添加商品 -->
<myDialog title="选择商品" width="50%" ref="myDialogRef" @Confirm="subitgood">
<page-search ref="searchRef" :search-config="searchConfig2" @query-click="handleQueryClick"
<page-search ref="searchRefs" :search-config="searchConfig2" @query-click="searchs"
@reset-click="handleResetClick" />
<page-content ref="contentRefs" :content-config="contentConfig2" @add-click="handleAddClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
@ -168,10 +168,13 @@ import myDialog from '@/components/mycomponents/myDialog.vue'
import selectGoodslist from "./goodsGroupconfig/selectGoodslist.vue"
const {
searchRef,
searchRefs,
contentRef,
contentRefs,
addModalRef,
editModalRef,
handleQueryClick,
searchs,
handleResetClick,
// handleAddClick,
// handleEditClick,
@ -181,6 +184,7 @@ const {
handleFilterChange,
} = usePage();
//
async function handleAddClick() {
addModalRef.value?.setModalVisible();
@ -191,7 +195,6 @@ async function handleAddClick() {
// addModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
}
const myDialogRef = ref(null)
const contentRefs = ref(null)
const myDialogRefAdd = ref(null)
const elFormref = ref(null)
let title = ref("新增分组")
@ -205,6 +208,7 @@ let forms = reactive({
sort: 1,
time: ""
})
//
function addgoods() {
myDialogRef.value.open()

View File

@ -7,7 +7,12 @@ const contentConfig: IContentConfig = {
showOverflowTooltip: true,
},
toolbar: [],
pagination: {
background: true,
layout: "prev,pager,next,jumper,total,sizes",
pageSize: 10,
pageSizes: [10, 20, 30, 50],
},
indexAction: function (params) {
return UserAPI.getPage(params);
},

View File

@ -16,6 +16,9 @@
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template>
<template #type="scope">
{{ typeFilter(scope.row[scope.prop]) }}
</template>
<template #gender="scope">
<DictLabel v-model="scope.row[scope.prop]" code="gender" />
</template>
@ -125,6 +128,14 @@ function handleToolbarClick(name: string) {
function confirm() {
console.log(form, 'debug')
}
//
function typeFilter(item: any) {
if (item == 'single') { return '单规格' }
else if (item == 'sku') { return '多规格' }
else if (item == 'package') { return '套餐商品' }
else if (item == 'weight') { return '称重商品' }
else if (item == 'coupon') { return '团购券' }
}
//
async function handleOperatClick(data: IOperatData) {
console.log(data);

View File

@ -1,50 +1,149 @@
<template>
<el-form ref="ruleFormRef" :model="ruleForm" label-width="150px" class="demo-ruleForm" status-icon>
<el-form ref="ruleFormRef" label-width="150px" class="demo-ruleForm" status-icon>
<el-form-item label="规格属性">
<el-table :data="datas.skuList" border>
<el-table-column label="售价" prop="salePrice">
<el-table :data="skuList" border>
<el-table-column :label="item.label" :prop="item.value" v-for="(item, index) in props.specTableHeaders"
:key="index">
</el-table-column>
<el-table-column prop="originPrice">
<template #header>
<span>原价</span>
<el-icon style="margin-left: 10px;" color="#409EFC" class="no-inherit" @click="batchNumber('originPrice')">
<EditPen />
</el-icon>
</template>
<template v-slot="scope">
<el-input-number size="mini" @change="priceFormat(scope.row, 'salePrice')"
@blur="priceFormat(scope.row, 'salePrice')" v-model="scope.row.salePrice"
controls-position="right"></el-input-number>
<el-input-number @change="priceFormat(scope.row, 'originPrice')" v-model="scope.row.originPrice"
@blur="priceFormat(scope.row, 'originPrice')" controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="会员价" prop="memberPrice">
<el-table-column prop="costPrice">
<template #header>
<span>成本价</span>
<el-icon style="margin-left: 10px;" color="#409EFC" class="no-inherit" @click="batchNumber('costPrice')">
<EditPen />
</el-icon>
</template>
<template v-slot="scope">
<el-input-number @change="priceFormat(scope.row, 'memberPrice')"
@blur="priceFormat(scope.row, 'memberPrice')" v-model="scope.row.memberPrice"
controls-position="right"></el-input-number>
<el-input-number @change="priceFormat(scope.row, 'costPrice')" v-model="scope.row.costPrice"
@blur="priceFormat(scope.row, 'costPrice')" controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="起售数量" prop="suit">
<template slot-scope="scope">
<el-input-number @change="priceFormat(scope.row, 'suit')" @blur="priceFormat(scope.row, 'suit')" :min="0"
v-model="scope.row.suit" controls-position="right" v-if="form.type == 'weigh'"></el-input-number>
<el-input-number @change="priceFormat(scope.row, 'suit')" @blur="priceFormat(scope.row, 'suit')" :min="1"
v-model="scope.row.suit" controls-position="right" v-else></el-input-number>
<el-table-column prop="salePrice">
<template #header>
<span>售价</span>
<el-icon style="margin-left: 10px;" color="#409EFC" class="no-inherit" @click="batchNumber('salePrice')">
<EditPen />
</el-icon>
</template>
<template v-slot="scope">
<el-input-number size="default" v-model="scope.row.salePrice" @change="priceFormat(scope.row, 'salePrice')"
@blur="priceFormat(scope.row, 'salePrice')" controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="分销金额" prop="firstShared">
<el-table-column prop="memberPrice">
<template #header>
<span>会员价</span>
<el-icon style="margin-left: 10px;" color="#409EFC" class="no-inherit" @click="batchNumber('memberPrice')">
<EditPen />
</el-icon>
</template>
<template v-slot="scope">
<el-input-number @change="priceFormat(scope.row, 'firstShared')"
@blur="priceFormat(scope.row, 'firstShared')" v-model="scope.row.firstShared"
controls-position="right"></el-input-number>
<el-input-number @change="priceFormat(scope.row, 'memberPrice')" v-model="scope.row.memberPrice"
@blur="priceFormat(scope.row, 'memberPrice')" controls-position="right"></el-input-number>
</template>
</el-table-column>
<el-table-column label="起售数量" prop="suitNum">
<template #header>
<span>起售数量</span>
<el-icon style="margin-left: 10px;" color="#409EFC" class="no-inherit" @click="batchNumber('suitNum')">
<EditPen />
</el-icon>
</template>
<template v-slot="scope">
<el-input-number @change="priceFormat(scope.row, 'suitNum')" v-model="scope.row.suitNum"
@blur="priceFormat(scope.row, 'suitNum')" :min="0" controls-position="right"
v-if="props.info.type == 'weigh'"></el-input-number>
<el-input-number @change="priceFormat(scope.row, 'suitNum')" v-model="scope.row.suitNum"
@blur="priceFormat(scope.row, 'suitNum')" :min="1" controls-position="right" v-else></el-input-number>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<el-dialog title="批量修改" width="400px" v-model="showBatchModal">
<el-form :model="batchNumberForm">
<el-form-item>
<el-input-number @change="priceFormat(batchNumberForm, 'batchNumber')"
@blur="priceFormat(batchNumberForm, 'batchNumber')" v-model="batchNumberForm.batchNumber"
controls-position="right" style="width: 100%"></el-input-number>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="batchNumberFormConfirm">
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
const ruleFormRef = ref(null)
let datas = reactive({
skuList: [],
let batchNumberKey = ref(null)
let batchNumberForm = reactive({
batchNumber: 0,
})
const ruleForm = ref({
let skuList = ref([])
let showBatchModal = ref(false)
const props = defineProps({
info: {
type: Object,
default: () => {
return {}
}
},
list: {
type: Array,
default: () => {
return []
}
},
specTableHeaders: {
type: Array,
default: () => {
return []
}
}
})
onMounted(() => {
skuList.value = props.list
})
watch(() => props.list, (newval) => {
skuList.value = newval
}, { deep: true })
//
function batchNumber(key) {
batchNumberKey.value = key;
showBatchModal.value = true;
}
//
function batchNumberFormConfirm() {
skuList.value.map((item, index) => {
let newitem = { ...item };
newitem[batchNumberKey.value] = batchNumberForm.batchNumber;
skuList.value[index] = { ...newitem }
});
showBatchModal.value = false;
batchNumberForm.batchNumber = 0;
}
function getdata() {
return skuList.value
}
function priceFormat(item, key) {
const messageheight = 48;
const offset = window.innerHeight / 2 - messageheight / 2 - 100;
@ -52,15 +151,34 @@ function priceFormat(item, key) {
const min = 0;
const max = 100000000;
const newval = formatPrice(item[key], min, max, true);
console.log(newval);
if (typeof newval !== "number") {
item[key] = newval.value;
// this.$message({
// offset,
// message: `${min}${max}`,
// })
ElMessage.warning(`请输入${min}${max}范围内的数字`)
}
})
}
//
const formatPrice = (price, min = -Infinity, max = 100000000, returnIsArea = false) => {
if (price === undefined || price === null || price === '') {
return 0
}
//
const newval = parseFloat((Math.floor(price * 100) / 100).toFixed(2))
//
if (newval > max) {
return returnIsArea ? { value: max, error: true } : max
}
//
if (newval < min) {
return returnIsArea ? { value: min, error: true } : min
}
//
if (newval < min) {
return min
}
//
return newval
}
defineExpose({ getdata })
</script>

View File

@ -33,8 +33,8 @@
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="商品图片" required prop="coverImg">
<SingleImageUpload v-model="ruleForm.coverImg" />
<el-form-item label="商品图片" required prop="images">
<MultiImageUpload v-model="ruleForm.images" />
</el-form-item>
<el-form-item>
<div style="color: #999;">第一张图为商品封面图图片尺寸为750×750</div>
@ -48,12 +48,12 @@
</el-checkbox-group>
</el-form-item> -->
<el-form-item label="商品类型">
<el-radio-group v-model="ruleForm.type">
<el-radio-group v-model="ruleForm.type" @change="changeTypeEnum(ruleForm.type)">
<el-radio label="single">单规格商品</el-radio>
<el-radio label="sku">多规格商品</el-radio>
<el-radio label="package">套餐商品</el-radio>
<el-radio label="weight">称重商品</el-radio>
<el-radio label="coupon">团购券</el-radio>
<!-- <el-radio label="coupon">团购券</el-radio> -->
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="规格类型">
@ -62,52 +62,24 @@
<el-radio label="sku">多规格</el-radio>
</el-radio-group>
</el-form-item> -->
<el-form-item label="套餐类型">
<!-- <el-form-item label="套餐类型">
<el-radio-group v-model="ruleForm.groupType">
<el-radio label="0">固定套餐</el-radio>
<el-radio label="1">可选套餐</el-radio>
</el-radio-group>
</el-form-item>
<template v-if="ruleForm.groupType == 0">
<el-table border :data="item.goods" v-for="(item, index) in ruleForm.proGroupVo" :key="index">
<el-table-column label="名称" prop="proName"></el-table-column>
<el-table-column label="规格" prop="skuName"> </el-table-column>
<el-table-column label="价格" prop="price"></el-table-column>
<el-table-column label="数量" prop="number">
<template v-slot="scope">
<el-input-number v-model="scope.row.number" :min="0" />
</template>
</el-table-column>
<el-table-column width="150">
<template slot="header" slot-scope="scope">
<el-button type="primary"
@click="$refs.shopListRef.show([...ruleForm.proGroupVo[index].goods])">添加商品</el-button>
</template>
<template slot-scope="scope">
<el-button type="text" :disabled="scope.row.typeEnum != '多规格'"
@click="showSelectSkuHandle(scope.row, scope.$index, index)">设置规格</el-button>
<el-button type="text" @click="ruleForm.proGroupVo[index].goods.splice(scope.$index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<template v-if="ruleForm.groupType == 1">
<div class="group_wrap" v-for="(item, index) in form.proGroupVo" :key="index">
<el-form inline :model="item">
<el-form-item label="规格组名">
<el-input v-model="item.title" />
</el-form-item>
<el-form-item :label="`本组菜品${item.goods.length}选`">
<el-input v-model="item.number" />
</el-form-item>
<el-form-item>
<el-button @click="form.proGroupVo.splice(index, 1)">删除</el-button>
</el-form-item>
</el-form>
<div>
<el-table border :data="item.goods">
</el-form-item> -->
<el-form-item label="套餐商品" v-if="ruleForm.type == 'package'">
<div style="display: block;width: 100%;">
<div class="head-container">
<el-radio-group v-model="ruleForm.groupType" @change="typeChange">
<el-radio-button label="0">固定套餐</el-radio-button>
<el-radio-button label="1">可选套餐</el-radio-button>
</el-radio-group>
</div>
<div v-if="ruleForm.groupType == '0'">
<el-table border :data="item.goods" v-for="(item, index) in ruleForm.proGroupVo" :key="index">
<el-table-column label="名称" prop="proName"></el-table-column>
<el-table-column label="规格" prop="skuName"></el-table-column>
<el-table-column label="规格" prop="skuName"> </el-table-column>
<el-table-column label="价格" prop="price"></el-table-column>
<el-table-column label="数量" prop="number">
<template v-slot="scope">
@ -115,23 +87,85 @@
</template>
</el-table-column>
<el-table-column width="150">
<template slot="header" slot-scope="scope">
<el-button type="primary"
@click="$refs.shopListRef.show([...form.proGroupVo[index].goods]); addGroupIndex = index">添加商品</el-button>
<template #header>
<el-button type="primary" @click="addgoods">添加商品</el-button>
</template>
<template slot-scope="scope">
<!-- <template slot="header" v-slot="scope"> -->
<!-- <el-button type="primary" @click="">添加商品</el-button> -->
<!-- </template> -->
<!-- <template v-slot="scope">
<el-button type="text" :disabled="scope.row.typeEnum != '多规格'"
@click="showSelectSkuHandle(scope.row, scope.$index, index)">设置规格</el-button>
<el-button type="text" @click="form.proGroupVo[index].goods.splice(scope.$index, 1)">删除</el-button>
</template>
</template> -->
</el-table-column>
</el-table>
</div>
</div>
<el-button type="primary" @click="$refs.shopListRef.show(); addGroupIndex = false">添加套餐组</el-button>
</template>
<!--
<div v-if="ruleForm.groupType == 1">
<div class="group_wrap" v-for="(item, index) in form.proGroupVo" :key="index">
<el-form inline :model="item">
<el-form-item label="规格组名">
<el-input v-model="item.title" />
</el-form-item>
<el-form-item :label="`本组菜品${item.goods.length}选`">
<el-input v-model="item.number" />
</el-form-item>
<el-form-item>
<el-button @click="form.proGroupVo.splice(index, 1)">删除</el-button>
</el-form-item>
</el-form>
<div>
<el-table border :data="item.goods">
<el-table-column label="名称" prop="proName"></el-table-column>
<el-table-column label="规格" prop="skuName"></el-table-column>
<el-table-column label="价格" prop="price"></el-table-column>
<el-table-column label="数量" prop="number">
<template v-slot="scope">
<el-input-number v-model="scope.row.number" :min="0" />
</template>
</el-table-column>
<el-table-column width="150">
<template slot="header" slot-scope="scope">
<el-button type="primary"
@click="$refs.shopListRef.show([...form.proGroupVo[index].goods]); addGroupIndex = index">添加商品</el-button>
</template>
<template slot-scope="scope">
<el-button type="text" :disabled="scope.row.typeEnum != '多规格'"
@click="showSelectSkuHandle(scope.row, scope.$index, index)">设置规格</el-button>
<el-button type="text" @click="form.proGroupVo[index].goods.splice(scope.$index, 1)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-button type="primary" @click="$refs.shopListRef.show(); addGroupIndex = false">添加套餐组</el-button>
</div> -->
</el-form-item>
<el-form-item label="选择规格" v-if="ruleForm.type == 'sku'">
<el-select v-model="ruleForm.specId" placeholder="请选择规格" style="width: 500px" @change="selectSpecHandle">
<el-option :label="item.name" :value="item.id" v-for="item in datas.specificationsconfig"
:key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="item.name" v-if="datas.selectSpeclist.length" v-for="item in datas.selectSpeclist"
:key="item.name">
<el-checkbox-group v-model="item.selectSpecResult" @change="selectSpecResultChange">
<el-checkbox :value="item.name" v-for="(item, index) in item.children" :key="index">{{ item.name
}}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- 规格属性 -->
<SpecificationAttribute></SpecificationAttribute>
<!-- 单规格称重 -->
<SpecificationAttribute v-if="ruleForm.type != 'sku'" :info="ruleForm" :list="list"
ref="specificationAttributeRef">
</SpecificationAttribute>
<SpecificationAttribute v-if="ruleForm.type == 'sku' && list.length" :specTableHeaders="datas.specTableHeaders"
:info="ruleForm" :list="list" ref="specificationAttributeRef"></SpecificationAttribute>
<el-form-item label="重量">
<el-col :span="12">
<div style="display: block;">
@ -159,7 +193,7 @@
</el-form-item> -->
<el-form-item label="定时上下架">
<el-checkbox-group v-model="ruleForm.days">
<el-checkbox v-for="(item, index) in datas.cycle" :key="item.value" :label="item.value">
<el-checkbox v-for="(item, index) in datas.cycle" :key="item.value" :value="item.value">
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
@ -199,6 +233,8 @@
<el-button @click="resetForm(ruleFormRef)">取消</el-button>
</el-form-item>
</el-form>
<!-- 选择商品 -->
<shopList ref="shopListRef" @success="selectShopRes" />
</div>
</template>
@ -209,7 +245,23 @@ import type { FormInstance, FormRules } from 'element-plus'
import SpecificationAttribute from './SpecificationAttribute.vue'
import UserAPI from "@/api/product/productclassification";
import UserAPI2 from "@/api/product/commonUnits";
import UserAPI3 from "@/api/product/index";
import UserAPI4 from "@/api/product/specificationsconfig";
import shopList from "@/components/mycomponents/shopList.vue";
import { useRouter } from 'vue-router';
const router = useRouter();
let list = ref<any[]>([{
"originPrice": 0,
"costPrice": 0,
"salePrice": 0,
"memberPrice": 0,
"suitNum": 1,
"coverImg": "",
"weight": 0,
"barCode": "88888888888888888888"
}]);
let datas = reactive({
cycle: [
{ label: "周一", value: "Monday" },
@ -222,8 +274,23 @@ let datas = reactive({
],
Company: [],
classification: [],
})
//
specificationsconfig: [],
selectSpeclist: [],
defaultSku: {
"originPrice": 0,
"costPrice": 0,
"salePrice": 0,
"memberPrice": 0,
"suitNum": 1,
"coverImg": "",
"weight": 0,
"barCode": "88888888888888888888"
},
specTableHeaders: [],
})
let shopListRef = ref(null)
interface RuleForm {
name: string,
shortTitle: string,
@ -239,6 +306,7 @@ interface RuleForm {
weight: Number,
isAllowTempModifyPrice: Number,
days: string[],
useTime: string[],
startTime: string,
endTime: string,
isSale: string,
@ -248,6 +316,46 @@ interface RuleForm {
packFee: Number,
sort: Number,
}
//
function addgoods() {
shopListRef.value.opens()
}
//
function selectShopRes(res) {
// let newres = res.map(item => {
// item.proId = item.id
// item.proName = item.name
// item.price = item.lowPrice
// item.skuId = ''
// item.skuName = ''
// item.number = 1
// return item
// })
// if (this.form.groupType == 0) {
// let obj = {
// title: '',
// count: newres.length,
// number: '',
// goods: newres
// }
// this.form.proGroupVo = [{ ...obj }]
// } else {
// if (this.addGroupIndex !== false) {
// this.form.proGroupVo[this.addGroupIndex].count = newres.length
// this.form.proGroupVo[this.addGroupIndex].goods = newres
// } else {
// let arr = [...this.form.proGroupVo]
// arr.push({
// title: '',
// count: newres.length,
// number: '',
// goods: newres
// })
// this.form.proGroupVo = [...arr]
// }
// }
}
const ruleFormRef = ref<FormInstance>()
const ruleForm = reactive<RuleForm>({
//
@ -265,11 +373,16 @@ const ruleForm = reactive<RuleForm>({
//
type: 'single',
// id
specId: 'normal',
specId: '',
//
groupType: '0',
//
proGroupVo: [],
proGroupVo: [{
title: '',
count: '',
number: '',
goods: []
}],
// sku
skuList: [],
//
@ -280,13 +393,13 @@ const ruleForm = reactive<RuleForm>({
days: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
useTime: ['00:00:00', '23:59:59'],
//
startTime: '00:00:00',
startTime: '',
//
endTime: "23:59:59",
endTime: "",
//
isSale: "1",
//
isStock: "0",
isStock: "1",
//
isHot: "1",
//
@ -314,7 +427,7 @@ const rules = reactive<FormRules<RuleForm>>({
trigger: 'change',
},
],
coverImg: [
images: [
{
required: true,
message: '请选择图片',
@ -331,18 +444,187 @@ async function getList() {
datas.Company = await UserAPI2.getList()
datas.classification = await UserAPI.getList()
}
const submitForm = async (formEl: FormInstance | undefined) => {
console.log(ruleForm, '提交数据')
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
console.log('submit!')
//
function selectSpecResultChange() {
createSkuHeader();
createSkuBody();
}
//
function createSkuHeader() {
const headers = [];
for (let item of datas.selectSpeclist) {
if (item.selectSpecResult.length) {
headers.push({
label: item.name,
value: item.name,
});
}
}
datas.specTableHeaders = headers;
}
//
function createSkuBody() {
let bodys = [];
for (let item of datas.selectSpeclist) {
if (item.selectSpecResult.length) {
let arr = [];
for (let val of item.selectSpecResult) {
arr.push({
[item.name]: val,
});
}
bodys.push(arr);
}
}
let arr = cartesian(bodys);
const m = {
coverImg: "",
...datas.defaultSku,
};
let newarr = [];
for (let item of arr) {
if (Array.isArray(item)) {
let obj = {};
let specSnap = [];
for (let v of item) {
for (let key in v) {
obj[`${key}`] = v[key];
specSnap.push(v[key]);
}
}
let specSnapStr = specSnap.join(",");
newarr.push({
specSnap: specSnapStr,
specInfo: specSnapStr,
...m,
...obj,
// barCode: `${dayjs().valueOf()}${RandomNumBoth(1, 9999)}`,
});
} else {
let specSnap = [];
for (let key in item) {
specSnap.push(item[key]);
}
let specSnapStr = specSnap.join(",");
newarr.push({
specSnap: specSnapStr,
specInfo: specSnapStr,
...m,
...item,
// barCode: `${dayjs().valueOf()}${RandomNumBoth(1, 9999)}`,
});
}
}
console.log(newarr, '提交')
list.value = newarr;
}
//
function changeTypeEnum(item) {
// single- sku- package- weight- coupon-
list.value = []
if (item == 'sku') {
tbProductSpecGet();
} else {
ruleForm.specId = ""
datas.selectSpeclist = []
list.value = [datas.defaultSku]
}
}
//
function cartesian(arr) {
if (arr.length < 2) return arr[0] || [];
return [].reduce.call(arr, (col, set) => {
let res = [];
col.forEach((c) => {
set.forEach((s) => {
let t = [].concat(Array.isArray(c) ? c : [c]);
t.push(s);
res.push(t);
});
});
return res;
});
}
//
function typeChange() {
ruleForm.typeEnum = 'normal'
if (ruleForm.groupType == 0) {
// this.$set(this.form.proGroupVo, 0, {
// title: '',
// count: '',
// number: 1,
// goods: []
// })
} else {
// this.form.proGroupVo = []
}
// this.changeTypeEnum()
}
//
async function tbProductSpecGet() {
datas.specificationsconfig = await UserAPI4.getPage()
}
//
function selectSpecHandle(e) {
const selectSpec = JSON.parse(JSON.stringify(datas.specificationsconfig.find((item) => item.id == e).children));
for (let item in selectSpec) {
selectSpec[item].selectSpecResult = [];
}
datas.selectSpeclist = selectSpec;
// this.form.skuList = [ ];
}
const specificationAttributeRef = ref(null)
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid, fields) => {
if (valid) {
//
ruleForm.coverImg = ruleForm.images[0]
// id
ruleForm.specId = specIdFunction(ruleForm.type)
//
ruleForm.startTime = ruleForm.useTime[0]
ruleForm.endTime = ruleForm.useTime[1]
// sku
ruleForm.skuList = specificationAttributeRef.value.getdata()
// selectSpecInfo
if (ruleForm.type == 'sku') {
let obj = {}
datas.selectSpeclist.forEach((item: any) => {
obj[item.name] = item.selectSpecResult
})
ruleForm.selectSpecInfo = obj
}
console.log(ruleForm, '提交数据')
let res = await UserAPI3.addunit(ruleForm)
if (res.code == 200) {
ElMessage.success("添加成功");
router.push({ name: 'productIndex' });
}
} else {
ElMessage.error("请填写完整信息");
console.log('error submit!', fields)
}
})
}
// id
const specIdFunction = (type: string) => {
if (type === 'single') {
return null
} else if (type === '2') {
return 2
} else if (type === '3') {
return 3
} else if (type === '4') {
return 4
} else if (type === '5') {
return 5
}
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()

View File

@ -66,9 +66,14 @@ const contentConfig: IContentConfig<UserPageQuery> = {
cols: [
// { type: "selection", width: 50, align: "center" },
{ label: "封面图", align: "center", prop: "coverImg", templet: "image" },
{ label: "分类名称", align: "center", prop: "categoryName", },
{ label: "商品名称", align: "center", prop: "name", },
{ label: "售价", align: "center", prop: "id" },
{ label: "规格名称", align: "center", prop: "specName" },
{
label: "商品规格", align: "center", prop: "type", templet: "custom",
slotName: "type",
},
{ label: "库存", align: "center", prop: "stockNumber" },
{ label: "耗材信息", align: "center", prop: "id" },
{

View File

@ -22,15 +22,71 @@
<el-text>{{ scope.row[scope.prop] }}</el-text>
<copy-button v-if="scope.row[scope.prop]" :text="scope.row[scope.prop]" style="margin-left: 2px" />
</template>
<template #operates="scope">
<el-button type="text" size="small" v-if="scope.row.level < 3"
@click="addlowerLevel(scope.row)">添加下一级</el-button>
<el-button type="text" size="small" @click="handleEditClick(scope.row)">编辑</el-button>
<el-button type="text" size="small" @click="deleteClick(scope.row)">删除</el-button>
</template>
</page-content>
<!-- 添加下一级-编辑 -->
<myDialog title="添加模板" width="50%" ref="myDialogRefs" @Confirm="subitgoods">
<el-form ref="forms" :model="datas.contentForm" :rules="datas.rules" label-width="80px" label-position="left">
<el-form-item label="规格名称" prop="name">
<el-input v-model="datas.contentForm.name" placeholder="请输入下一级名称"></el-input>
</el-form-item>
</el-form>
</myDialog>
<!-- 新增 -->
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<!-- <page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
<Dict v-model="scope.formData[scope.prop]" code="gender" />
</template>
</page-modal>
</page-modal> -->
<myDialog title="新增模板" width="50%" ref="myDialogRef" @Confirm="subitgood">
<el-form ref="form" :model="datas.contentForm" :rules="datas.rules" label-width="80px" label-position="left">
<el-form-item label="名称" prop="name">
<el-input v-model="datas.contentForm.name" placeholder="模板名称,如:衣服"></el-input>
</el-form-item>
<el-form-item :label="item.name" v-for="(item, index) in datas.contentForm.children" :key="index">
<div class="tag_wrap">
<el-tag v-for="(val, i) in item.children" :key="i" closable size="medium" :disable-transitions="true"
@close="handleClose(index, i)">
{{ val.name }}
</el-tag>
<el-input class="input-new-tag" v-show="item.inputVisible" v-model="item.inputValue" ref="saveTagInput"
size="small" placeholder="请输入规格值,回车添加" @keyup.enter.native="handleInputConfirm(index)"
@blur="handleInputConfirm(index)">
</el-input>
<el-button v-show="!item.inputVisible" size="small" icon="Plus" plain @click="showInput(index)">
添加
</el-button>
<el-button size="small" icon="Delete" plain @click="deleteTag(index)">删除</el-button>
</div>
</el-form-item>
</el-form>
<el-form ref="skuForm" :model="datas.skuForm" :rules="datas.skuRules" label-width="80px" label-position="left">
<el-row :gutter="20" v-if="datas.showSkuForm">
<el-col :span="10">
<el-form-item label="规格" prop="skuValidate1">
<el-input v-model="datas.skuForm.label" placeholder="规格,如:尺码"></el-input>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="规格值" prop="skuValidate2">
<el-input v-model="datas.skuForm.value" placeholder="规格值S、M"></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-button type="primary" @click="addSkuSubmit">添加</el-button>
<el-button @click="datas.showSkuForm = false">取消</el-button>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" icon="Plus" @click="datas.showSkuForm = true">添加规格</el-button>
</el-form-item>
</el-form>
</myDialog>
<!-- 编辑 -->
<page-modal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
<template #gender="scope">
@ -60,7 +116,21 @@ import contentConfig from "./specificationsconfig/content";
import contentConfig2 from "./specificationsconfig/content2";
import editModalConfig from "./specificationsconfig/edit";
import searchConfig from "./specificationsconfig/search";
import { pid } from "process";
const validateSku1 = (rule, value, callback) => {
if (!datas.skuForm.label) {
callback(new Error(' '))
} else {
callback()
}
}
const validateSku2 = (rule, value, callback) => {
if (!datas.skuForm.value) {
callback(new Error(' '))
} else {
callback()
}
}
const {
searchRef,
contentRef,
@ -75,9 +145,147 @@ const {
handleSearchClick,
handleFilterChange,
} = usePage();
// refs
const skuForm = ref(null)
const myDialogRefs = ref(null)
const forms = ref(null)
let datas = reactive({
contentForm: {
name: '',
children: [],
},
skuForm: {
label: '', value: ''
},
rules: {
name: [
{
required: true,
message: ' ',
trigger: 'blur'
}
]
},
skuRules: {
skuValidate1: {
required: true,
validator: validateSku1,
trigger: 'blur'
},
skuValidate2: {
required: true,
validator: validateSku2,
trigger: 'blur'
}
},
showSkuForm: false,
addchilderinfo: {}
})
let myDialogRef = ref(null)
function subitgood() {
skuForm.value.validate(async valid => {
if (valid) {
let res = await UserAPI.quickAdd(datas.contentForm)
if (res.code == 200) {
ElMessage.success("添加成功");
handleQueryClick();
datas.contentForm.name = ''
datas.contentForm.children = []
myDialogRef.value?.close();
}
}
})
}
function subitgoods() {
forms.value.validate(async valid => {
if (valid) {
let obj = {
name: datas.contentForm.name,
level: datas.addchilderinfo.level < 3 ? (datas.addchilderinfo.level + 1) : 3,
pid: datas.addchilderinfo.id
}
let res = await UserAPI.addunit(obj)
if (res.code == 200) {
ElMessage.success("添加成功");
myDialogRefs.value.close()
handleQueryClick();
datas.contentForm.name = ''
}
}
})
}
function handleClose(index: any, i: any) {
datas.contentForm.children[index].value.splice(i, 1);
}
function showInput(index: any) {
datas.contentForm.children[index].inputVisible = true;
}
function addSkuSubmit() {
skuForm.value.validate(async valid => {
if (valid) {
datas.contentForm.children.push({
name: datas.skuForm.label,
children: [{ name: datas.skuForm.value }],
inputVisible: false,
inputValue: ''
})
datas.skuForm.label = ''
datas.skuForm.value = ''
datas.showSkuForm = false
}
})
}
//
function handleInputConfirm(index: number) {
let inputValue = datas.contentForm.children[index].inputValue;
if (inputValue) {
datas.contentForm.children[index].children.push({ name: inputValue });
}
datas.contentForm.children[index].inputVisible = false;
datas.contentForm.children[index].inputValue = '';
}
//
//
function deleteTag(index: number) {
datas.contentForm.children.splice(index, 1);
}
//
async function addlowerLevel(item: any) {
datas.addchilderinfo = item
myDialogRefs.value.open()
// addModalRef.value?.setModalVisible();
// addModalConfig.formItems[2]!.attrs!.data = await UserAPI.getPage({ name: "" });
}
// function handleEditClicks(item: any) {
// console.log(item)
// }
function deleteClick(item: any) {
const ids = [item.id].join(",");
if (!ids) {
ElMessage.warning("请勾选删除项");
return;
}
ElMessageBox.confirm("确认删除?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(function () {
contentConfig.deleteAction(ids).then(() => {
ElMessage.success("删除成功");
handleQueryClick();
});
});
}
//
async function handleAddClick() {
myDialogRef.value?.open();
addModalRef.value?.setModalVisible();
//
addModalConfig.formItems[2]!.attrs!.data = await UserAPI.getPage({ name: "" });
@ -88,8 +296,19 @@ async function handleAddClick() {
async function handleEditClick(row: IObject) {
editModalRef.value?.handleDisabled(false);
//
editModalConfig.formItems[2]!.attrs!.data = await UserAPI.getPage({ name: "" });
// editModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
editModalConfig.formItems[2]!.attrs!.data = [{
createTime: "",
fullName: "顶级",
id: "0", level: 1,
name: "顶级",
pid: "0",
pids: "0,",
shopId: "1",
sort: 1,
status: 1,
updateTime: "",
children: await UserAPI.getPage({ name: "" })
}]
//
// editModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
// id
@ -143,3 +362,17 @@ async function handleOperatClick(data: IOperatData) {
//
const isA = ref(true);
</script>
<style scoped lang="scss">
.tag_wrap {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.input-new-tag {
width: 180px;
margin-left: 10px;
vertical-align: bottom;
}
</style>

View File

@ -47,13 +47,18 @@ const contentConfig: IContentConfig<UserPageQuery> = {
// { type: "selection", width: 50, align: "center" },
{ label: "模板名称", align: "center", prop: "name" },
{ label: "完整名称", align: "center", prop: "fullName" },
// {
// label: "操作",
// align: "center",
// fixed: "right",
// width: 280,
// templet: "operates",
// },
{
label: "操作",
slotName: "operates",
templet: "custom",
align: "center",
fixed: "right",
width: 280,
templet: "tool",
operat: ["edit", "delete"],
},
],
};

View File

@ -33,14 +33,15 @@ const modalConfig: IModalConfig<UserForm> = {
{
label: "规格级别",
prop: "level",
rules: [{ required: true, message: "用户角色不能为空", trigger: "blur" }],
type: "select",
attrs: {
placeholder: "请选择",
},
options: [{ label: "一级", value: 1 }, { label: "二级", value: 2 }, { label: "三级", value: 3 }],
initialValue: [],
}, {
hidden: true
},
{
label: "上级规格",
prop: "pid",
type: "tree-select",