低代码可视化-商城管理系统-商品管理-代码生成器

产品管理是商城管理系统的核心模块之一,它直接关系到商品的流通效率、库存管理和销售策略。通过高效的产品管理,商城能够实时掌握商品信息,优化库存结构,提高销售业绩。

新增数据库表

拷贝shop_cate修改为shop_goods表

修改表结构

其中包括一个自增ID字段,及三个时间类型的字段,其余的字段按照需求增加,这里title,remark,cate_id,price,line_price,amount,sellamount,skus,sku_type等个字段。其中我们用sku_type来区分是单规格还是多规格字段。

生成API

输入你安装的PHP时的域名/super/index.html,登录进去找到代码生成器界面。

新增页面

可以直接复制分类页面然后修改页面页面标识、页面名称、字体图标.

修改API

结合表shop_goods表,那么我们在后台API属性设置即为shop/goods即可。

修改表格字段

 

表单编辑字段

由于商品信息比较多,我们采用选项卡的方式来设计表单,基本信息、规格库存、商品详情。基本基本信息拖入单行文本字段进编辑表单设计区,输入字段标识、字段标识标题。

产品分类

产品分类字段比较特符,数据来源产品分类表的数据。这时要采用API把分类数据加载出来。

规格库存

规格库存用来设计价格等。其中规格类型我们用单选框组件,然后用FLEX组件把所有单规格输入组件包裹起来,增加显示判断,只有但单规格的时候才显示。

点击保存源码至本地

保存源码至本地后打开页面即可访问商品分类管理界面。

进入页面

启动环境进进入产品管理,在右上角可以设计不同主题效果。

这时增删改查已经完成。

数据查询

输入单选文本,字段名称改为title_like支持模板查询。PHP更多扩展查询参照此地址:PHP查询用法扩展-可视化设计工具

至此我们的产品管理增删改查已经完成

切换源码类型

低代码快速可视化完成商品增删改查代码生成器,大家根据自己喜欢可以切换选项式或组合式代码。

vue选项式代码

<template>
	<div class="container">
		<div class="el-card is-always-shadow diygw-col-24">
			<div class="el-card__body">
				<div class="mb8">
					<el-form class="flex flex-direction-row flex-wrap" :model="queryParams" :rules="queryParamsRules" ref="queryForm" label-width="80px">
						<div class="flex diygw-col-0">
							<el-form-item class="diygw-el-rate" prop="title_like" label="标题">
								<el-input type="text" placeholder="请输入标题查询" v-model="queryParams.title_like"> </el-input>
							</el-form-item>
						</div>
						<div class="flex diygw-col-0">
							<el-button type="primary" @click="handleQuery"> <SvgIcon name="ele-Search" /> 搜索 </el-button> <el-button @click="resetQuery"> <SvgIcon name="ele-Refresh" /> 重置 </el-button>
						</div>
					</el-form>
				</div>
				<div class="mb8">
					<el-button type="primary" plain @click="onOpenAddModule"> <SvgIcon name="ele-Plus" />新增 </el-button> <el-button type="danger" plain :disabled="multiple" @click="onTabelRowDel"> <SvgIcon name="ele-Delete" />删除 </el-button>
				</div>
				<el-table @selection-change="handleSelectionChange" v-loading="loading" :data="tableData" stripe border current-row-key="id" empty-text="没有数据" style="width: 100%">
					<el-table-column type="selection" width="55" align="center"></el-table-column>
					<el-table-column label="置顶" prop="title" align="center">
						<template #default="scope"> <el-tag style="cursor: pointer" v-if="scope.row.sortnum > 0" type="error" @click="updatenumApi(scope.row)"> 取消置顶 </el-tag><el-tag style="cursor: pointer" @click="ordernumApi(scope.row)" v-else> 置顶 </el-tag> </template>
					</el-table-column>
					<el-table-column label="商品图片" prop="img" align="center">
						<template #default="scope"> <diy-img style="width: 80px; height: 80px" :src="scope.row.img" fit="contain"></diy-img> </template>
					</el-table-column>
					<el-table-column label="商品名称" prop="title" align="center"> </el-table-column>
					<el-table-column label="商品价格" prop="price" align="center"> </el-table-column>
					<el-table-column label="总销量" prop="sellamonut" align="center"> </el-table-column>
					<el-table-column label="库存量" prop="amount" align="center"> </el-table-column>
					<el-table-column label="备注" prop="remark" align="center"> </el-table-column>
					<el-table-column label="数据状态" prop="status" align="center">
						<template #default="scope"> <el-tag v-if="scope.row.status == 1"> 上架 </el-tag><el-tag v-else> 下架 </el-tag> </template>
					</el-table-column>
					<el-table-column label="操作" align="center" fixed="right" width="180">
						<template #default="scope">
							<el-button type="primary" plain size="small" @click="onOpenEditModule(scope.row)"> <SvgIcon name="ele-Edit" />修改 </el-button> <el-button v-if="scope.row.id != 0" type="danger" plain size="small" @click="onTabelRowDel(scope.row)"> <SvgIcon name="ele-Delete" />删除 </el-button>
						</template>
					</el-table-column>
				</el-table>
				<!-- 分页设置-->
				<div v-show="total > 0"><el-divider></el-divider> <el-pagination background :total="total" :current-page="queryParams.pageNum" :page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange" /></div>
			</div>
			<!-- 添加或修改参数配置对话框 -->
			<el-dialog :fullscreen="isFullscreen" width="1000px" v-model="isShowDialog" destroy-on-close title="商品" draggable center>
				<template #header="{ close, titleId, titleClass }">
					<h4 :id="titleId" :class="titleClass">商品</h4>
					<el-tooltip v-if="!isFullscreen" content="最大化" placement="bottom">
						<el-button class="diygw-max-icon el-dialog__headerbtn">
							<el-icon @click="isFullscreen = !isFullscreen">
								<FullScreen />
							</el-icon>
						</el-button>
					</el-tooltip>
					<el-tooltip v-if="isFullscreen" content="返回" placement="bottom">
						<el-button class="diygw-max-icon el-dialog__headerbtn">
							<el-icon @click="isFullscreen = !isFullscreen">
								<Remove />
							</el-icon>
						</el-button>
					</el-tooltip>
				</template>
				<el-form class="flex flex-direction-row flex-wrap" :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="90px">
					<div class="flex diygw-col-24">
						<el-tabs v-model="tabsTab" tab-position="top" @tab-click="handleClick">
							<el-tab-pane label="基本信息" name="1">
								<div class="flex flex-direction-row flex-wrap">
									<div class="flex diygw-col-24">
										<el-form-item
											:rules="[
												{
													required: true,
													trigger: 'blur',
													message: '商品名称不能为空'
												}
											]"
											class="diygw-el-rate"
											prop="title"
											label="商品名称"
										>
											<el-input type="text" placeholder="请输入商品名称" v-model="editForm.title"> </el-input>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item prop="cateId" class="diygw-el-rate" label="商品分类">
											<el-select clear-icon="CircleClose" suffix-icon="ArrowUp" placeholder="请选择" v-model="editForm.cateId" style="width: 100% !important">
												<el-option v-for="item in cates.rows" :key="item.id" :label="item.title" :value="item.id" />
											</el-select>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item
											:rules="[
												{
													required: true,
													trigger: 'blur',
													message: '商品主图不能为空'
												}
											]"
											prop="img"
											class="diygw-el-rate"
											label="商品主图"
										>
											<diy-uploadinput v-model="editForm.img" title="商品主图"></diy-uploadinput>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item prop="imgs" class="diygw-el-rate" label="商品轮播图">
											<diy-photo v-model="editForm.imgs"></diy-photo>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item prop="status" class="diygw-el-rate" label="商品状态">
											<el-radio-group v-model="editForm.status">
												<el-radio v-for="item in editFormData.statusDatas" :key="item.value" :label="item.value">
													{{ item.label }}
												</el-radio>
											</el-radio-group>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item prop="remark" class="diygw-el-rate" label="商品描述">
											<el-input type="textarea" rows="3" placeholder="请输入描述" v-model="editForm.remark"> </el-input>
										</el-form-item>
									</div>
								</div>
							</el-tab-pane>
							<el-tab-pane label="规格库存" name="2">
								<div class="flex flex-direction-row flex-wrap">
									<div class="flex diygw-col-24">
										<el-form-item prop="skuType" class="diygw-el-rate" label="规格类型">
											<el-radio-group v-model="editForm.skuType">
												<el-radio v-for="item in editFormData.skuTypeDatas" :key="item.value" :label="item.value">
													{{ item.label }}
												</el-radio>
											</el-radio-group>
										</el-form-item>
									</div>
									<div v-if="editForm.skuType == 1" class="flex flex-wrap diygw-col-24 flex-direction-column">
										<div class="flex diygw-col-24">
											<el-form-item
												:rules="[
													{
														type: 'number',
														required: true,
														trigger: 'blur',
														message: '请输入数字'
													}
												]"
												prop="price"
												class="diygw-el-rate"
												label="商品价格"
											>
												<el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入商品价格" v-model="editForm.price"> </el-input-number>
												<div class="diygw-help-text">商品的实际购买金额,最低0.01</div>
											</el-form-item>
										</div>
										<div class="flex diygw-col-24">
											<el-form-item
												:rules="[
													{
														type: 'number',
														required: true,
														trigger: 'blur',
														message: '请输入数字'
													}
												]"
												prop="linePrice"
												class="diygw-el-rate"
												label="切线价格"
											>
												<el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入切线价格" v-model="editForm.linePrice"> </el-input-number>
												<div class="diygw-help-text">划线价仅用于商品页展示</div>
											</el-form-item>
										</div>
										<div class="flex diygw-col-24">
											<el-form-item
												:rules="[
													{
														type: 'number',
														required: true,
														trigger: 'blur',
														message: '请输入数字'
													}
												]"
												prop="amount"
												class="diygw-el-rate"
												label="库存数量"
											>
												<el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入库存数量" v-model="editForm.amount"> </el-input-number>
												<div class="diygw-help-text">商品的实际库存数量,为0时用户无法下单</div>
											</el-form-item>
										</div>
									</div>
									<div v-else class="flex diygw-col-24">
										<el-form-item prop="skus" label="多规格">
											<diy-sku :columns="editFormData.skusColumns" v-model:skus="editForm.skus.skus" v-model:specs="editForm.skus.specs"></diy-sku>
										</el-form-item>
									</div>
								</div>
							</el-tab-pane>
							<el-tab-pane label="商品详情" name="3">
								<div class="flex flex-direction-row flex-wrap">
									<div class="flex diygw-col-24">
										<el-form-item
											:rules="[
												{
													required: true,
													trigger: 'blur',
													message: '商品详情不能为空哟'
												}
											]"
											prop="content"
											class="diygw-el-rate"
											label="商品详情"
										>
											<diy-editor v-model="editForm.content"></diy-editor>
										</el-form-item>
									</div>
								</div>
							</el-tab-pane>
						</el-tabs>
					</div>
				</el-form>
				<template #footer>
					<div class="dialog-footer flex justify-center"><el-button @click="closeDialog"> 取 消 </el-button> <el-button type="primary" @click="onSubmit" :loading="saveloading"> 保 存 </el-button></div>
				</template>
			</el-dialog>
		</div>
		<div class="clearfix"></div>
	</div>
</template>

<script>
	import { useRouter, useRoute } from 'vue-router';
	import { storeToRefs } from 'pinia';
	import { useUserInfo } from '@/stores/userInfo';

	import { ElMessageBox, ElMessage } from 'element-plus';
	import { deepClone, changeRowToForm } from '@/utils/other';
	import { addData, updateData, delData, listData } from '@/api';

	import DiyUploadinput from '@/components/upload/uploadinput.vue';
	import DiyPhoto from '@/components/upload/photo.vue';
	import DiySku from '@/components/sku/index.vue';
	import DiyEditor from '@/components/editor/index.vue';

	export default {
		components: {
			DiyUploadinput,
			DiyPhoto,
			DiySku,
			DiyEditor
		},
		data() {
			return {
				globalOption: {},
				userInfo: {},
				ordernum: {
					code: 200,
					msg: '设置成功'
				},
				updatenum: {
					code: 200,
					msg: '修改成功',
					data: {
						id: '1',
						sortnum: '0'
					}
				},
				cates: {
					rows: [
						{
							id: 0,
							title: '',
							remark: null,
							img: '',
							sortnum: null,
							status: '',
							userId: 0,
							createTime: '',
							updateTime: '',
							deleteTime: null
						}
					],
					total: 0,
					code: 0,
					msg: ''
				},
				// 遮罩层
				loading: true,
				// 选中数组
				ids: [],
				// 非单个禁用
				single: true,
				// 非多个禁用
				multiple: true,
				// 弹出层标题
				title: '',
				// 总条数
				total: 0,
				tableData: [],
				// 是否显示弹出层
				isFullscreen: false,
				isShowDialog: false,
				saveloading: false,
				tabsTab: '1',
				editFormInit: {},
				queryParamsInit: {},

				editFormData: {
					statusDatas: [
						{ value: '1', label: '上架' },
						{ value: '2', label: '下架' }
					],
					skuTypeDatas: [
						{ value: '1', label: '单规格' },
						{ value: '2', label: '多规格' }
					],
					skusColumns: [
						{ title: '图片地址', id: 'thumb', type: 'img' },
						{ title: '价格', id: 'price', type: 'number' },
						{ title: '划线价格', id: 'linePrice', type: 'number' },
						{ title: '库存', id: 'amount', type: 'number' },
						{ title: '备注', id: 'sku', type: 'text' }
					]
				},

				queryParams: {
					pageNum: 1,
					pageSize: 10,
					title_like: ''
				},

				queryParamsRules: {},

				editForm: {
					id: undefined,
					title: '',
					cateId: '',
					img: '',
					imgs: [],
					status: '1',
					remark: '',
					skuType: '1',
					price: '',
					linePrice: '',
					amount: '',
					skus: {
						skus: [],
						specs: []
					},
					content: ''
				},

				editFormRules: {}
			};
		},
		methods: {
			getParamData(e, row) {
				row = row || {};
				let dataset = e.currentTarget ? e.currentTarget.dataset : e;
				if (row) {
					dataset = Object.assign(dataset, row);
				}
				return dataset;
			},
			navigateTo(e, row) {
				let dataset = this.getParamData(e, row);
				let { type } = dataset;
				if (type == 'page' || type == 'inner' || type == 'href') {
					this.router.push({
						path: dataset.url,
						query: dataset
					});
				} else if (typeof this[type] == 'function') {
					this[type](dataset);
				} else {
					ElMessage.error('待实现点击事件');
				}
			},
			async init() {
				await this.catesApi();
			},
			// 置顶 API请求方法
			async ordernumApi(param) {
				param = param || {};
				param = this.getParamData(param);
				let http_url = '/shop/goods/sortnum';
				let http_data = {};
				let http_header = {};

				http_data.id = param.id;
				let flag = await this.confirmFunction({ title: '确定置顶吗' });
				if (!flag) {
					ElMessage.error('你已取消');
					return;
				}
				let ordernum = await this.$http.post(http_url, http_data, http_header, 'json');
				this.ordernum = ordernum;
				this.resetQuery();
			},
			// 设置为0 API请求方法
			async updatenumApi(param) {
				param = param || {};
				param = this.getParamData(param);
				let http_url = '/shop/goods/update';
				let http_data = {};
				let http_header = {};

				http_data.id = param.id;
				http_data.sortnum = 0;
				let flag = await this.confirmFunction({ title: '确定取消置顶吗' });
				if (!flag) {
					ElMessage.error('你已取消');
					return;
				}
				let updatenum = await this.$http.post(http_url, http_data, http_header, 'json');
				this.updatenum = updatenum;
				this.resetQuery();
			},
			// 分类 API请求方法
			async catesApi(param) {
				param = param || {};
				param = this.getParamData(param);
				let http_url = '/shop/cate/all';
				let http_data = {};
				let http_header = {};

				let cates = await this.$http.post(http_url, http_data, http_header, 'json');
				this.cates = cates;
			},

			// confirm 自定义方法
			async confirmFunction(param) {
				param = param || {};
				let thiz = this;
				let title = param && (param.title || param.title == 0) ? param.title : thiz.title || '';
				return new Promise((resolve) => {
					ElMessageBox({
						message: title,
						title: '警告',
						showCancelButton: true,
						confirmButtonText: '确定',
						cancelButtonText: '取消',
						type: 'warning'
					})
						.then(() => {
							resolve(true);
						})
						.catch(() => {
							resolve(false);
						});
				});
			},
			// 打开弹窗
			openDialog(row) {
				if (row.id && row.id != undefined && row.id != 0) {
					this.editForm = changeRowToForm(row, this.editForm);
				} else {
					this.initForm();
				}
				this.isShowDialog = true;
				this.saveloading = false;
			},

			// 关闭弹窗
			closeDialog(row) {
				this.mittBus.emit('onEditAdmintableModule', row);
				this.isShowDialog = false;
			},

			// 保存
			onSubmit() {
				const formWrap = this.$refs.editFormRef;
				if (!formWrap) return;
				formWrap.validate((valid, fields) => {
					if (valid) {
						this.saveloading = true;
						if (this.editForm.skuType == 2 && this.editForm.skus && this.editForm.skus.skus.length == 0) {
							ElMessage.error('请设置多规格');
							return;
						}
						if (this.editForm.id != undefined && this.editForm.id != 0) {
							updateData('/shop/goods', this.editForm)
								.then(() => {
									ElMessage.success('修改成功');
									this.saveloading = false;
									this.closeDialog(this.editForm); // 关闭弹窗
								})
								.finally(() => {
									this.saveloading = false;
								});
						} else {
							addData('/shop/goods', this.editForm)
								.then(() => {
									ElMessage.success('新增成功');
									this.saveloading = false;
									this.closeDialog(this.editForm); // 关闭弹窗
								})
								.finally(() => {
									this.saveloading = false;
								});
						}
					} else {
						let message = '';
						if (fields && Object.keys(fields).length > 0) {
							let field = fields[Object.keys(fields)[0]];
							let messages = field.map((item) => {
								return item.message;
							});
							message = messages.join(',');
						}
						ElMessage.error('验证未通过!' + message);
					}
				});
			},
			// 表单初始化,方法:`resetFields()` 无法使用
			initForm() {
				this.editForm = deepClone(this.editFormInit);
			},
			/** 查询商品列表 */
			handleQuery() {
				this.loading = true;
				listData('/shop/goods', this.queryParams).then((response) => {
					this.tableData = response.rows;
					this.total = response.total;
					this.loading = false;
				});
			},
			/** 重置按钮操作 */
			resetQuery() {
				this.queryParams = deepClone(this.queryParamsInit);
				this.handleQuery();
			},

			// 打开新增商品弹窗
			onOpenAddModule(row) {
				row = [];
				this.title = '添加商品';
				this.openDialog(row);
			},
			// 打开编辑商品弹窗
			onOpenEditModule(row) {
				this.title = '修改商品';
				this.initForm();
				this.openDialog(row);
			},
			/** 删除按钮操作 */
			onTabelRowDel(row) {
				let thiz = this;
				const id = row.id || this.ids;
				ElMessageBox({
					message: '是否确认删除选中的商品?',
					title: '警告',
					showCancelButton: true,
					confirmButtonText: '确定',
					cancelButtonText: '取消',
					type: 'warning'
				}).then(function () {
					return delData('/shop/goods', id).then(() => {
						thiz.handleQuery();
						ElMessage.success('删除成功');
					});
				});
			},
			// 多选框选中数据
			handleSelectionChange(selection) {
				this.ids = selection.map((item) => item.id);
				this.single = selection.length != 1;
				this.multiple = !selection.length;
			},

			//分页页面大小发生变化
			handleSizeChange(val) {
				this.queryParams.pageSize = val;
				this.handleQuery();
			},
			//当前页码发生变化
			handleCurrentChange(val) {
				this.queryParams.pageNum = val;
				this.handleQuery();
			}
		},
		async mounted() {
			this.router = useRouter();
			this.globalOption = useRoute().query;
			const stores = useUserInfo();
			const { userInfos } = storeToRefs(stores);
			this.userInfo = userInfos;
			await this.init();
			this.editFormInit = deepClone(this.editForm);
			this.queryParamsInit = deepClone(this.queryParams);
			this.handleQuery();
			this.mittBus.on('onEditAdmintableModule', () => {
				this.handleQuery();
			});
		},
		beforeUnmount() {
			this.mittBus.off('onEditAdmintableModule');
		}
	};
</script>

<style lang="scss" scoped>
	.container {
		font-size: 12px;
	}
</style>

vue组合式代码

<template>
	<div class="container">
		<div class="el-card is-always-shadow diygw-col-24">
			<div class="el-card__body">
				<div class="mb8">
					<el-form class="flex flex-direction-row flex-wrap" :model="state.queryParams" :rules="queryParamsRules" ref="queryForm" label-width="80px">
						<div class="flex diygw-col-0">
							<el-form-item class="diygw-el-rate" prop="title_like" label="标题">
								<el-input type="text" placeholder="请输入标题查询" v-model="queryParams.title_like"> </el-input>
							</el-form-item>
						</div>
						<div class="flex diygw-col-0">
							<el-button type="primary" @click="handleQuery"> <SvgIcon name="ele-Search" /> 搜索 </el-button> <el-button @click="resetQuery"> <SvgIcon name="ele-Refresh" /> 重置 </el-button>
						</div>
					</el-form>
				</div>
				<div class="mb8">
					<el-button type="primary" plain @click="onOpenAddModule"> <SvgIcon name="ele-Plus" />新增 </el-button> <el-button type="danger" plain :disabled="state.multiple" @click="onTabelRowDel"> <SvgIcon name="ele-Delete" />删除 </el-button>
				</div>
				<el-table @selection-change="handleSelectionChange" v-loading="state.loading" :data="state.tableData" stripe border current-row-key="id" empty-text="没有数据" style="width: 100%">
					<el-table-column type="selection" width="55" align="center"></el-table-column>
					<el-table-column label="置顶" prop="title" align="center">
						<template #default="scope"> <el-tag style="cursor: pointer" v-if="scope.row.sortnum > 0" type="error" @click="updatenumApi(scope.row)"> 取消置顶 </el-tag><el-tag style="cursor: pointer" @click="ordernumApi(scope.row)" v-else> 置顶 </el-tag> </template>
					</el-table-column>
					<el-table-column label="商品图片" prop="img" align="center">
						<template #default="scope"> <diy-img style="width: 80px; height: 80px" :src="scope.row.img" fit="contain"></diy-img> </template>
					</el-table-column>
					<el-table-column label="商品名称" prop="title" align="center"> </el-table-column>
					<el-table-column label="商品价格" prop="price" align="center"> </el-table-column>
					<el-table-column label="总销量" prop="sellamonut" align="center"> </el-table-column>
					<el-table-column label="库存量" prop="amount" align="center"> </el-table-column>
					<el-table-column label="备注" prop="remark" align="center"> </el-table-column>
					<el-table-column label="数据状态" prop="status" align="center">
						<template #default="scope"> <el-tag v-if="scope.row.status == 1"> 上架 </el-tag><el-tag v-else> 下架 </el-tag> </template>
					</el-table-column>
					<el-table-column label="操作" align="center" fixed="right" width="180">
						<template #default="scope">
							<el-button type="primary" plain size="small" @click="onOpenEditModule(scope.row)"> <SvgIcon name="ele-Edit" />修改 </el-button> <el-button v-if="scope.row.id != 0" type="danger" plain size="small" @click="onTabelRowDel(scope.row)"> <SvgIcon name="ele-Delete" />删除 </el-button>
						</template>
					</el-table-column>
				</el-table>
				<!-- 分页设置-->
				<div v-show="state.total > 0"><el-divider></el-divider> <el-pagination background :total="state.total" :current-page="state.queryParams.pageNum" :page-size="state.queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange" /></div>
			</div>
			<!-- 添加或修改参数配置对话框 -->
			<el-dialog :fullscreen="isFullscreen" width="1000px" v-model="isShowDialog" destroy-on-close title="商品" draggable center>
				<template #header="{ close, titleId, titleClass }">
					<h4 :id="titleId" :class="titleClass">商品</h4>
					<el-tooltip v-if="!isFullscreen" content="最大化" placement="bottom">
						<el-button class="diygw-max-icon el-dialog__headerbtn">
							<el-icon @click="isFullscreen = !isFullscreen">
								<FullScreen />
							</el-icon>
						</el-button>
					</el-tooltip>
					<el-tooltip v-if="isFullscreen" content="返回" placement="bottom">
						<el-button class="diygw-max-icon el-dialog__headerbtn">
							<el-icon @click="isFullscreen = !isFullscreen">
								<Remove />
							</el-icon>
						</el-button>
					</el-tooltip>
				</template>
				<el-form class="flex flex-direction-row flex-wrap" :model="state.editForm" :rules="state.editFormRules" ref="editFormRef" label-width="90px">
					<div class="flex diygw-col-24">
						<el-tabs v-model="tabsTab" tab-position="top" @tab-click="handleClick">
							<el-tab-pane label="基本信息" name="1">
								<div class="flex flex-direction-row flex-wrap">
									<div class="flex diygw-col-24">
										<el-form-item
											:rules="[
												{
													required: true,
													trigger: 'blur',
													message: '商品名称不能为空'
												}
											]"
											class="diygw-el-rate"
											prop="title"
											label="商品名称"
										>
											<el-input type="text" placeholder="请输入商品名称" v-model="editForm.title"> </el-input>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item prop="cateId" class="diygw-el-rate" label="商品分类">
											<el-select clear-icon="CircleClose" suffix-icon="ArrowUp" placeholder="请选择" v-model="editForm.cateId" style="width: 100% !important">
												<el-option v-for="item in cates.rows" :key="item.id" :label="item.title" :value="item.id" />
											</el-select>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item
											:rules="[
												{
													required: true,
													trigger: 'blur',
													message: '商品主图不能为空'
												}
											]"
											prop="img"
											class="diygw-el-rate"
											label="商品主图"
										>
											<diy-uploadinput v-model="editForm.img" title="商品主图"></diy-uploadinput>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item prop="imgs" class="diygw-el-rate" label="商品轮播图">
											<diy-photo v-model="editForm.imgs"></diy-photo>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item prop="status" class="diygw-el-rate" label="商品状态">
											<el-radio-group v-model="editForm.status">
												<el-radio v-for="item in editFormData.statusDatas" :key="item.value" :label="item.value">
													{{ item.label }}
												</el-radio>
											</el-radio-group>
										</el-form-item>
									</div>
									<div class="flex diygw-col-24">
										<el-form-item prop="remark" class="diygw-el-rate" label="商品描述">
											<el-input type="textarea" rows="3" placeholder="请输入描述" v-model="editForm.remark"> </el-input>
										</el-form-item>
									</div>
								</div>
							</el-tab-pane>
							<el-tab-pane label="规格库存" name="2">
								<div class="flex flex-direction-row flex-wrap">
									<div class="flex diygw-col-24">
										<el-form-item prop="skuType" class="diygw-el-rate" label="规格类型">
											<el-radio-group v-model="editForm.skuType">
												<el-radio v-for="item in editFormData.skuTypeDatas" :key="item.value" :label="item.value">
													{{ item.label }}
												</el-radio>
											</el-radio-group>
										</el-form-item>
									</div>
									<div v-if="editForm.skuType == 1" class="flex flex-wrap diygw-col-24 flex-direction-column">
										<div class="flex diygw-col-24">
											<el-form-item
												:rules="[
													{
														type: 'number',
														required: true,
														trigger: 'blur',
														message: '请输入数字'
													}
												]"
												prop="price"
												class="diygw-el-rate"
												label="商品价格"
											>
												<el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入商品价格" v-model="editForm.price"> </el-input-number>
												<div class="diygw-help-text">商品的实际购买金额,最低0.01</div>
											</el-form-item>
										</div>
										<div class="flex diygw-col-24">
											<el-form-item
												:rules="[
													{
														type: 'number',
														required: true,
														trigger: 'blur',
														message: '请输入数字'
													}
												]"
												prop="linePrice"
												class="diygw-el-rate"
												label="切线价格"
											>
												<el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入切线价格" v-model="editForm.linePrice"> </el-input-number>
												<div class="diygw-help-text">划线价仅用于商品页展示</div>
											</el-form-item>
										</div>
										<div class="flex diygw-col-24">
											<el-form-item
												:rules="[
													{
														type: 'number',
														required: true,
														trigger: 'blur',
														message: '请输入数字'
													}
												]"
												prop="amount"
												class="diygw-el-rate"
												label="库存数量"
											>
												<el-input-number min="0" max="100" step="1" controls controls-position="right" placeholder="输入库存数量" v-model="editForm.amount"> </el-input-number>
												<div class="diygw-help-text">商品的实际库存数量,为0时用户无法下单</div>
											</el-form-item>
										</div>
									</div>
									<div v-else class="flex diygw-col-24">
										<el-form-item prop="skus" label="多规格">
											<diy-sku :columns="editFormData.skusColumns" v-model:skus="editForm.skus.skus" v-model:specs="editForm.skus.specs"></diy-sku>
										</el-form-item>
									</div>
								</div>
							</el-tab-pane>
							<el-tab-pane label="商品详情" name="3">
								<div class="flex flex-direction-row flex-wrap">
									<div class="flex diygw-col-24">
										<el-form-item
											:rules="[
												{
													required: true,
													trigger: 'blur',
													message: '商品详情不能为空哟'
												}
											]"
											prop="content"
											class="diygw-el-rate"
											label="商品详情"
										>
											<diy-editor v-model="editForm.content"></diy-editor>
										</el-form-item>
									</div>
								</div>
							</el-tab-pane>
						</el-tabs>
					</div>
				</el-form>
				<template #footer>
					<div class="dialog-footer flex justify-center"><el-button @click="closeDialog"> 取 消 </el-button> <el-button type="primary" @click="onSubmit" :loading="state.saveloading"> 保 存 </el-button></div>
				</template>
			</el-dialog>
		</div>
		<div class="clearfix"></div>
	</div>
</template>

<script setup name="goodsgoods">
	import { ElMessageBox, ElMessage } from 'element-plus';
	import { ref, toRefs, reactive, onMounted, getCurrentInstance, onUnmounted, unref } from 'vue';
	import { deepClone, changeRowToForm } from '@/utils/other';
	import { addData, updateData, delData, listData } from '@/api';

	import { useRouter, useRoute } from 'vue-router';
	import { storeToRefs } from 'pinia';
	import { useUserInfo } from '@/stores/userInfo';

	import DiyUploadinput from '@/components/upload/uploadinput.vue';
	import DiyPhoto from '@/components/upload/photo.vue';
	import DiySku from '@/components/sku/index.vue';
	import DiyEditor from '@/components/editor/index.vue';

	const stores = useUserInfo();
	const { userInfos } = storeToRefs(stores);
	const { proxy } = getCurrentInstance();
	const router = useRouter();
	const route = useRoute();
	const globalOption = ref(route.query);
	const getParamData = (e, row) => {
		row = row || {};
		let dataset = e.currentTarget ? e.currentTarget.dataset : e;
		if (row) {
			dataset = Object.assign(dataset, row);
		}
		return dataset;
	};

	const navigateTo = (e, row) => {
		let dataset = getParamData(e, row);
		let { type } = dataset;
		if ((type == 'page' || type == 'inner' || type == 'href') && dataset.url) {
			router.push({
				path: (dataset.url.startsWith('/') ? '' : '/') + dataset.url,
				query: dataset
			});
		} else {
			ElMessage.error('待实现点击事件');
		}
	};

	const state = reactive({
		userInfo: userInfos.value,
		ordernum: {
			code: 200,
			msg: '设置成功'
		},
		updatenum: {
			code: 200,
			msg: '修改成功',
			data: {
				id: '1',
				sortnum: '0'
			}
		},
		cates: {
			rows: [
				{
					id: 0,
					title: '',
					remark: null,
					img: '',
					sortnum: null,
					status: '',
					userId: 0,
					createTime: '',
					updateTime: '',
					deleteTime: null
				}
			],
			total: 0,
			code: 0,
			msg: ''
		},
		// 遮罩层
		loading: true,
		// 选中数组
		ids: [],
		// 非单个禁用
		single: true,
		// 非多个禁用
		multiple: true,
		// 弹出层标题
		title: '',
		// 总条数
		total: 0,
		tableData: [],
		// 是否显示弹出层
		isFullscreen: false,
		isShowDialog: false,
		saveloading: false,
		tabsTab: '1',

		editFormData: {
			statusDatas: [
				{ value: '1', label: '上架' },
				{ value: '2', label: '下架' }
			],
			skuTypeDatas: [
				{ value: '1', label: '单规格' },
				{ value: '2', label: '多规格' }
			],
			skusColumns: [
				{ title: '图片地址', id: 'thumb', type: 'img' },
				{ title: '价格', id: 'price', type: 'number' },
				{ title: '划线价格', id: 'linePrice', type: 'number' },
				{ title: '库存', id: 'amount', type: 'number' },
				{ title: '备注', id: 'sku', type: 'text' }
			]
		},

		queryParams: {
			pageNum: 1,
			pageSize: 10,
			title_like: ''
		},

		queryParamsRules: {},

		editForm: {
			id: undefined,
			title: '',
			cateId: '',
			img: '',
			imgs: [],
			status: '1',
			remark: '',
			skuType: '1',
			price: '',
			linePrice: '',
			amount: '',
			skus: {
				skus: [],
				specs: []
			},
			content: ''
		},

		editFormRules: {}
	});
	const { userInfo, cates, tabsTab, editFormData, queryParams, multiple, ordernum, tableData, updatenum, loading, title, single, total, isShowDialog, editForm, ids, saveloading, isFullscreen } = toRefs(state);
	// 置顶 API请求方法
	const ordernumApi = async (param) => {
		param = param || {};
		param = getParamData(param);
		let http_url = '/shop/goods/sortnum';
		let http_data = {};

		let http_header = {};

		http_data.id = param.id;
		let flag = await confirmFunction({ title: '确定置顶吗' });
		if (!flag) {
			ElMessage.error('你已取消');
			return;
		}
		let ordernum = await proxy.$http.post(http_url, http_data, http_header, 'json');

		state.ordernum = ordernum;
		state.resetQuery();
	};
	// 设置为0 API请求方法
	const updatenumApi = async (param) => {
		param = param || {};
		param = getParamData(param);
		let http_url = '/shop/goods/update';
		let http_data = {};

		let http_header = {};

		http_data.id = param.id;
		http_data.sortnum = 0;
		let flag = await confirmFunction({ title: '确定取消置顶吗' });
		if (!flag) {
			ElMessage.error('你已取消');
			return;
		}
		let updatenum = await proxy.$http.post(http_url, http_data, http_header, 'json');

		state.updatenum = updatenum;
		state.resetQuery();
	};
	// 分类 API请求方法
	const catesApi = async (param) => {
		param = param || {};
		param = getParamData(param);
		let http_url = '/shop/cate/all';
		let http_data = {};

		let http_header = {};

		let cates = await proxy.$http.post(http_url, http_data, http_header, 'json');

		state.cates = cates;
	};

	//confirm 自定义方法
	const confirmFunction = async (param) => {
		let title = param && (param.title || param.title == 0) ? param.title : state.title || '';
		return new Promise((resolve) => {
			ElMessageBox({
				message: title,
				title: '警告',
				showCancelButton: true,
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				type: 'warning'
			})
				.then(() => {
					resolve(true);
				})
				.catch(() => {
					resolve(false);
				});
		});
	};
	const editFormRef = ref(null);

	const editFormInit = deepClone(state.editForm);

	// 打开弹窗
	const openDialog = (row) => {
		if (row.id && row.id != undefined && row.id != 0) {
			state.editForm = changeRowToForm(row, state.editForm);
		} else {
			initForm();
		}
		state.isShowDialog = true;
		state.saveloading = false;
	};

	// 关闭弹窗
	const closeDialog = (row) => {
		proxy.mittBus.emit('onEditAdmintableModule', row);
		state.isShowDialog = false;
	};

	// 保存
	const onSubmit = () => {
		const formWrap = unref(editFormRef);
		if (!formWrap) return;
		formWrap.validate((valid, fields) => {
			if (valid) {
				state.saveloading = true;
				if (editForm.skuType == 2 && editForm.skus && editForm.skus.skus.length == 0) {
					ElMessage.error('请设置多规格');
					return;
				}
				if (state.editForm.id != undefined && state.editForm.id != 0) {
					updateData('/shop/goods', state.editForm)
						.then(() => {
							ElMessage.success('修改成功');
							state.saveloading = false;
							closeDialog(state.editForm); // 关闭弹窗
						})
						.finally(() => {
							state.saveloading = false;
						});
				} else {
					addData('/shop/goods', state.editForm)
						.then(() => {
							ElMessage.success('新增成功');
							state.saveloading = false;
							closeDialog(state.editForm); // 关闭弹窗
						})
						.finally(() => {
							state.saveloading = false;
						});
				}
			} else {
				let message = '';
				if (fields && Object.keys(fields).length > 0) {
					let field = fields[Object.keys(fields)[0]];
					let messages = field.map((item) => {
						return item.message;
					});
					message = messages.join(',');
				}
				ElMessage.error('验证未通过!' + message);
			}
		});
	};
	// 表单初始化,方法:`resetFields()` 无法使用
	const initForm = () => {
		state.editForm = deepClone(editFormInit);
	};
	const queryParamsInit = deepClone(state.queryParams);
	/** 查询商品列表 */
	const handleQuery = () => {
		state.loading = true;
		listData('/shop/goods', state.queryParams).then((response) => {
			state.tableData = response.rows;
			state.total = response.total;
			state.loading = false;
		});
	};
	/** 重置按钮操作 */
	const resetQuery = () => {
		state.queryParams = deepClone(queryParamsInit);
		handleQuery();
	};

	// 打开新增商品弹窗
	const onOpenAddModule = (row) => {
		row = [];
		state.title = '添加商品';
		initForm();
		openDialog(row);
	};
	// 打开编辑商品弹窗
	const onOpenEditModule = (row) => {
		state.title = '修改商品';
		openDialog(row);
	};
	/** 删除按钮操作 */
	const onTabelRowDel = (row) => {
		const id = row.id || state.ids;

		ElMessageBox({
			message: '是否确认删除选中的商品?',
			title: '警告',
			showCancelButton: true,
			confirmButtonText: '确定',
			cancelButtonText: '取消',
			type: 'warning'
		}).then(function () {
			return delData('/shop/goods', id).then(() => {
				handleQuery();
				ElMessage.success('删除成功');
			});
		});
	};
	// 多选框选中数据
	const handleSelectionChange = (selection) => {
		state.ids = selection.map((item) => item.id);
		state.single = selection.length != 1;
		state.multiple = !selection.length;
	};

	//分页页面大小发生变化
	const handleSizeChange = (val) => {
		state.queryParams.pageSize = val;
		handleQuery();
	};
	//当前页码发生变化
	const handleCurrentChange = (val) => {
		state.queryParams.pageNum = val;
		handleQuery();
	};
	const init = async () => {
		await catesApi();
	};
	// 页面加载时
	onMounted(async () => {
		await init();
		handleQuery();
		proxy.mittBus.on('onEditAdmintableModule', () => {
			handleQuery();
		});
	});

	// 页面卸载时
	onUnmounted(() => {
		proxy.mittBus.off('onEditAdmintableModule');
	});
</script>

<style lang="scss" scoped>
	.container {
		font-size: 12px;
	}
</style>

扩展阅读

  1. 产品录入:

    • 管理员可以通过产品管理模块录入新的产品信息,包括商品名称、价格、描述、分类、品牌、图片等。
    • 录入时需要进行信息的校验和合法性验证,确保数据的准确性和完整性。
  2. 产品编辑:

    • 管理员可以对已有产品的信息进行编辑和更新,如修改商品名称、价格、描述等。
    • 编辑界面应友好且操作便捷,以便管理员轻松完成更新。
  3. 产品上架与下架:

    • 管理员可以灵活地将产品上架或下架。
    • 上架后,用户可以在商城中浏览和购买该产品;下架后,用户将无法再找到该产品进行购买。
    • 上架和下架操作应提供明确的提示和确认机制,避免误操作。
  4. 库存管理:

    • 产品管理模块应提供库存管理功能,管理员可以查看和管理各个产品的库存情况。
    • 包括库存数量的增加、减少、库存警戒值的设置等。
    • 当库存数量低于警戒值时,系统应自动提醒管理员进行补货或下架操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值