2024-10-23 09:55:22 +00:00
|
|
|
|
<template>
|
2024-12-17 06:59:05 +00:00
|
|
|
|
<div class="totalBox">
|
2024-11-03 04:40:56 +00:00
|
|
|
|
<!-- 表单 ref 和 prop绑定 用于重置表单 -->
|
2024-12-17 06:59:05 +00:00
|
|
|
|
<div class="fromBox">
|
|
|
|
|
<el-form ref="resetFormData" :model="form" label-width="auto" size="large" :rules="rules">
|
|
|
|
|
<div>
|
|
|
|
|
<el-form-item label="添加商品图片">
|
|
|
|
|
<!-- 下面的event的作用,传入当前事件对象 -->
|
|
|
|
|
<el-upload ref="uploadProductImg" action="#" list-type="picture-card" :auto-upload="false" multiple="true"
|
|
|
|
|
:on-remove="handleRemove" @change="(event: any) => handleChange(event, 0)" :on-exceed="Exceed_ProductImg"
|
|
|
|
|
limit="7">
|
|
|
|
|
<el-icon>
|
|
|
|
|
<Plus />
|
|
|
|
|
</el-icon>
|
|
|
|
|
</el-upload>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 添加图文描述-->
|
|
|
|
|
<div>
|
|
|
|
|
<el-form-item label="添加图文描述">
|
|
|
|
|
<el-upload ref="uploadProductDetail" action="#" list-type="picture-card" :auto-upload="false"
|
|
|
|
|
multiple="true" :on-change="(event: any) => handleChange(event, 1)" :on-exceed="Exceed_ProductDetail"
|
|
|
|
|
limit="1" :on-remove="handleRemove">
|
|
|
|
|
<el-icon>
|
|
|
|
|
<Plus />
|
|
|
|
|
</el-icon>
|
|
|
|
|
</el-upload>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="NamePrice">
|
|
|
|
|
<el-form-item label="产品名称" prop="name">
|
|
|
|
|
<el-input v-model="form.name" maxlength="12" minlength="2" show-word-limit style="width: 260px;" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="产品价格" prop="price">
|
|
|
|
|
<el-input-number v-model="form.price" min="0.01" :precision="2" :step="0.5" />
|
|
|
|
|
<p>元</p>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="IntroInven">
|
|
|
|
|
<el-form-item label="产品简介" prop="intro">
|
|
|
|
|
<el-input v-model="form.intro" type="textarea" placeholder="产品尺寸,服务等" maxlength="30" show-word-limit
|
|
|
|
|
style="width: 300px;" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<div>
|
|
|
|
|
<el-form-item label="库存" prop="inventory">
|
|
|
|
|
<el-input-number v-model="form.inventory" min="0" :precision="0" :step="1" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="festivalBox">
|
|
|
|
|
<el-form-item label="节日名称" prop="festivalName" style="width: 300px;">
|
|
|
|
|
<el-input v-model="form.festivalName" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="产品类别" prop="type" style="width: 240px;">
|
|
|
|
|
<el-select v-model="typeList.value" placeholder="请选择" @change="(event: any) => loadForm(event, 1)">
|
|
|
|
|
<el-option v-for="item in typeList" :key="item.value" :label="item.label" :value="item.value1" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</div>
|
|
|
|
|
<el-form-item label="商品标签" prop="label">
|
|
|
|
|
<div v-for="(item, index) in labelList" :key="index">
|
|
|
|
|
<el-input v-model="labelList[index]" type="text" style="width: 150px;" maxlength="5" show-word-limit
|
|
|
|
|
@blur="addLabelList" />
|
|
|
|
|
</div>
|
|
|
|
|
<el-button type="primary" @click="addLabel">添加</el-button>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="商品详细描述" prop="introDetail">
|
|
|
|
|
<el-input v-model="form.introDetail" type="textarea" placeholder="产品尺寸,服务等" maxlength="100" show-word-limit/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<div class="btnBox">
|
|
|
|
|
<el-form-item>
|
|
|
|
|
<el-button type="primary" @click="onSubmit" :disabled="saveBtn">保存</el-button>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item>
|
|
|
|
|
<el-button type="primary" @click="resetForm">重置</el-button>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</div>
|
|
|
|
|
</el-form>
|
|
|
|
|
</div>
|
2024-10-23 09:55:22 +00:00
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
2024-12-17 06:59:05 +00:00
|
|
|
|
import { ref, onMounted, inject, reactive } from 'vue';
|
|
|
|
|
import { Plus } from '@element-plus/icons-vue';
|
|
|
|
|
import { type UploadFile, type UploadProps, genFileId, type UploadRawFile, type FormInstance, type FormRules } from 'element-plus';
|
|
|
|
|
import { SuccessInfo, WarnInfo, CommInfo } from '@/utils/messageInfo';
|
2024-10-27 10:08:43 +00:00
|
|
|
|
import myAxios from "@/api/myAxios";
|
2024-11-03 04:40:56 +00:00
|
|
|
|
const fileSimple = ref() //单个文件
|
2024-12-17 06:59:05 +00:00
|
|
|
|
const productImgMap = new Map() //商品图片数组
|
|
|
|
|
// const uploadedFiles = ref<UploadFile[]>([]);//商品图片数组
|
|
|
|
|
// const uploadedDescription = ref<UploadFile[]>([]);//商品图文描述数组
|
|
|
|
|
const productImgArr : any = ref([])
|
2024-11-03 04:40:56 +00:00
|
|
|
|
const resetFormData = ref()
|
2024-11-01 05:04:02 +00:00
|
|
|
|
const form = ref({
|
2024-12-17 06:59:05 +00:00
|
|
|
|
name: '', //商品名称
|
2024-11-03 04:40:56 +00:00
|
|
|
|
price: '', //商品价格
|
|
|
|
|
intro: '',//产品简介
|
2024-12-17 06:59:05 +00:00
|
|
|
|
festivalName: '', //节日名称
|
2024-11-03 04:40:56 +00:00
|
|
|
|
type: '',//类别
|
|
|
|
|
label: '',//商品标签
|
|
|
|
|
introDetail: '',//详情描述
|
|
|
|
|
goodImg: '', //商品图片url
|
|
|
|
|
detailImg: '', //图文详情url
|
|
|
|
|
inventory: '' //库存
|
2024-10-23 09:55:22 +00:00
|
|
|
|
})
|
2024-11-05 12:19:45 +00:00
|
|
|
|
//导入组件刷新
|
2024-12-17 06:59:05 +00:00
|
|
|
|
const reload: any = inject("reload")
|
2024-11-03 04:40:56 +00:00
|
|
|
|
const typeList: any = ref([
|
|
|
|
|
{
|
|
|
|
|
value1: '',
|
|
|
|
|
label: ''
|
|
|
|
|
}
|
|
|
|
|
])
|
2024-12-17 06:59:05 +00:00
|
|
|
|
const uploadProductImg: any = ref() //图片上传的ref绑定
|
|
|
|
|
const uploadProductDetail: any = ref() //图片上传的ref绑定
|
|
|
|
|
const saveBtn = ref(false) //按钮状态
|
|
|
|
|
const labelList = ref([''])
|
2024-11-03 04:40:56 +00:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
getTypeList() //加载类别列表,渲染在产品列表的select选项里面
|
2024-11-01 05:04:02 +00:00
|
|
|
|
})
|
2024-11-03 04:40:56 +00:00
|
|
|
|
const getTypeList = async () => { //获取类别列表作为可选项
|
|
|
|
|
const res = await myAxios.post('/category/list', {}) //获取商品列表
|
|
|
|
|
for (let key in res.data.data) { //循环赋值
|
|
|
|
|
typeList.value[key] = {
|
|
|
|
|
value1: res.data.data[key].typeName,
|
|
|
|
|
label: res.data.data[key].typeName
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-01 05:04:02 +00:00
|
|
|
|
}
|
2024-12-17 06:59:05 +00:00
|
|
|
|
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => { //uploadFile表示当前删除的图片,uploadFiles是还剩余的图片信息
|
|
|
|
|
productImgMap.delete(uploadFile.uid)
|
|
|
|
|
productImgArr.value.splice(0,productImgArr.value.length) //一并删除数组中存放的图片Url地址
|
2024-11-01 05:04:02 +00:00
|
|
|
|
}
|
2024-11-03 04:40:56 +00:00
|
|
|
|
//提交表单
|
|
|
|
|
const onSubmit = async () => {
|
|
|
|
|
const values = Object.values(form.value);
|
|
|
|
|
// 使用some()方法来检查是否有任何值为空
|
2024-12-17 06:59:05 +00:00
|
|
|
|
console.log('表单--->',values);
|
|
|
|
|
if (values.some((value: any)=> value === null || value === undefined || value === '' || value === 0)) {
|
|
|
|
|
WarnInfo('请检查表单数据是否完整填写')
|
2024-11-03 04:40:56 +00:00
|
|
|
|
return; //空返回结束函数
|
|
|
|
|
}
|
|
|
|
|
const res = await myAxios.post('/goods/add', { ...form.value })
|
2024-12-17 06:59:05 +00:00
|
|
|
|
console.log(res.data);
|
2024-11-03 04:40:56 +00:00
|
|
|
|
if (res.data.code === 1) {
|
2024-12-17 06:59:05 +00:00
|
|
|
|
SuccessInfo('提交成功')
|
2024-11-05 12:19:45 +00:00
|
|
|
|
reload() //上传完后重置表单
|
2024-11-03 04:40:56 +00:00
|
|
|
|
} else {
|
2024-12-17 06:59:05 +00:00
|
|
|
|
WarnInfo('服务错误')
|
2024-11-05 12:19:45 +00:00
|
|
|
|
return;
|
2024-11-03 04:40:56 +00:00
|
|
|
|
}
|
2024-11-01 05:04:02 +00:00
|
|
|
|
}
|
2024-11-03 04:40:56 +00:00
|
|
|
|
//图片上传请求 此请求只要选择了图片,就会默认上传
|
|
|
|
|
const handleChange = async (file: any, flag: number) => {
|
|
|
|
|
fileSimple.value = file
|
|
|
|
|
let formData = new FormData() //这一步很重要 创建一个FormData对象
|
|
|
|
|
formData.append("file", fileSimple.value.raw) //fileSimple.value.raw 才是文件主体 将其以文件的格式插入formData
|
|
|
|
|
const res = await myAxios({ //编写请求,与以前的请求不同,这一次要指定好头部类型和文件类型
|
|
|
|
|
url: '/file/upload/server/not_login',
|
|
|
|
|
method: 'post',
|
|
|
|
|
headers: {
|
|
|
|
|
'content-Type': 'multipart/form-data'
|
|
|
|
|
},
|
|
|
|
|
data: {
|
|
|
|
|
biz: "test",
|
|
|
|
|
// 取出formData对象中的file
|
|
|
|
|
file: formData.get("file")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
if (res.data.code === 1) {
|
|
|
|
|
//三元运算 当flag为0时,赋值给商品图片 flag为1时,赋值给详情图片
|
2024-12-17 06:59:05 +00:00
|
|
|
|
// flag ? form.value.detailImg = res.data.data : 0
|
|
|
|
|
if(flag === 0) {
|
|
|
|
|
productImgMap.set(fileSimple.value.uid,res.data.data)
|
|
|
|
|
productImgMap.forEach(loopMap) //将图片url插入到数组中
|
|
|
|
|
form.value.goodImg = formatString()
|
|
|
|
|
} else {
|
|
|
|
|
form.value.detailImg = res.data.data
|
|
|
|
|
}
|
|
|
|
|
// console.log('str--->',formatString());
|
2024-11-03 04:40:56 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//将 商品类别 赋值到表格里
|
|
|
|
|
const loadForm = (value: any, flag: number) => {
|
2024-12-17 06:59:05 +00:00
|
|
|
|
flag ? form.value.type = value : form.value.festivalName = value //三元运算符 flag=1 是关于"商品类别"选项的值 flag=0 是关于“是否为节日限定”选项的值
|
2024-11-03 04:40:56 +00:00
|
|
|
|
}
|
|
|
|
|
//清除表单
|
2024-12-17 06:59:05 +00:00
|
|
|
|
const resetForm = () => {
|
2024-11-03 04:40:56 +00:00
|
|
|
|
resetFormData.value.resetFields()
|
2024-10-31 11:23:27 +00:00
|
|
|
|
}
|
2024-12-17 06:59:05 +00:00
|
|
|
|
const Exceed_ProductImg: UploadProps['onExceed'] = (files) => { //覆盖商品照片 'onExceed'当文件个数超过限制时,做出的判断
|
|
|
|
|
uploadProductImg.value!.clearFiles() //清空已上传的文件列表
|
2024-11-05 12:19:45 +00:00
|
|
|
|
const file = files[0] as UploadRawFile
|
|
|
|
|
file.uid = genFileId()
|
2024-12-17 06:59:05 +00:00
|
|
|
|
uploadProductImg.value!.handleStart(file) //手动选择文件
|
2024-11-05 12:19:45 +00:00
|
|
|
|
}
|
|
|
|
|
const Exceed_ProductDetail: UploadProps['onExceed'] = (files) => { //覆盖商品详情图片
|
2024-12-17 06:59:05 +00:00
|
|
|
|
uploadProductDetail.value!.clearFiles() //清空已上传的文件列表
|
2024-11-05 12:19:45 +00:00
|
|
|
|
const file = files[0] as UploadRawFile
|
|
|
|
|
file.uid = genFileId()
|
2024-12-17 06:59:05 +00:00
|
|
|
|
uploadProductDetail.value!.handleStart(file) //手动选择文件
|
|
|
|
|
}
|
|
|
|
|
const validateName = (rule: any, value: any, callback: any) => { //商品名称的校验规则
|
|
|
|
|
if (value === '') {
|
|
|
|
|
callback(new Error('请输入商品名称'))
|
|
|
|
|
} else if (value.length < 2) {
|
|
|
|
|
callback(new Error('商品名称不能小于两位'))
|
|
|
|
|
saveBtn.value = true
|
|
|
|
|
} else { saveBtn.value = false }
|
|
|
|
|
}
|
|
|
|
|
const validateIntro = (rule: any, value: any, callback: any) => { //商品简介的校验规则
|
|
|
|
|
if (value === '') {
|
|
|
|
|
callback(new Error('请输入商品简介'))
|
|
|
|
|
} else if (value.length < 20) {
|
|
|
|
|
callback(new Error('商品简介不能少于20字'))
|
|
|
|
|
saveBtn.value = true
|
|
|
|
|
} else { saveBtn.value = false }
|
|
|
|
|
}
|
|
|
|
|
const validateDetail = (rule: any, value: any, callback: any) => {
|
|
|
|
|
if(value === '') {
|
|
|
|
|
callback(new Error('请输入商品详情'))
|
|
|
|
|
} else if(value.length < 80) {
|
|
|
|
|
callback(new Error('商品详情不能少于80字'))
|
|
|
|
|
saveBtn.value = true
|
|
|
|
|
} else { saveBtn.value = false }
|
|
|
|
|
}
|
|
|
|
|
const rules = reactive<FormRules<typeof form>>({ //表单校验规则
|
|
|
|
|
name: [{ validator: validateName, trigger: 'blur' }],
|
|
|
|
|
intro: [{ validator: validateIntro, trigger: 'blur' }],
|
|
|
|
|
introDetail: [{ validator: validateDetail, trigger: 'blur' }]
|
|
|
|
|
})
|
|
|
|
|
const addLabel = () => { //添加商品标签按钮的方法
|
|
|
|
|
if (labelList.value[labelList.value.length - 1] === '') {
|
|
|
|
|
WarnInfo('请填写完')
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (labelList.value.length < 4) {
|
|
|
|
|
labelList.value.push('')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const addLabelList = () => { //格式化商品标签
|
|
|
|
|
var string = ''
|
|
|
|
|
if (!labelList.value.some((item: any) => item === '')) {
|
|
|
|
|
string = labelList.value.join(';')
|
|
|
|
|
} else return;
|
|
|
|
|
form.value.label = string
|
|
|
|
|
}
|
|
|
|
|
const loopMap = (value :any , key :any ,map :any) => { //循环商品图片Map集合函数,用于给请求的字段赋值
|
|
|
|
|
productImgArr.value.push(value)
|
|
|
|
|
}
|
|
|
|
|
const formatString =()=>{
|
|
|
|
|
var str = ''
|
|
|
|
|
str = productImgArr.value.join(';')
|
|
|
|
|
// console.log(str);
|
|
|
|
|
return str
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.totalBox {
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
}
|
|
|
|
|
.fromBox {
|
|
|
|
|
width: 700px;
|
|
|
|
|
height: 750px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
}
|
|
|
|
|
.NamePrice {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
.IntroInven {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
.festivalBox {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
.btnBox {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-around;
|
2024-11-05 12:19:45 +00:00
|
|
|
|
}
|
2024-12-17 06:59:05 +00:00
|
|
|
|
</style>
|