shop-front-end/src/views/shop/goods/goods-form-modal.vue

174 lines
5.5 KiB
Vue
Raw Normal View History

2025-03-04 09:11:33 +08:00
<script setup lang="ts">
2025-03-04 17:17:46 +08:00
import { ref, reactive, onMounted, watch } from "vue";
import { ElMessage, FormRules } from "element-plus";
2025-03-04 09:11:33 +08:00
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { addGoodsApi, GoodsDTO } from "@/api/shop/goods";
2025-03-04 17:17:46 +08:00
import { CategoryDTO, getCategoryAllApi } from "@/api/shop/category";
2025-03-04 09:11:33 +08:00
import Confirm from "@iconify-icons/ep/check";
2025-03-04 17:17:46 +08:00
import Upload from "@iconify-icons/ep/upload";
const { VITE_APP_BASE_API } = import.meta.env;
2025-03-04 09:11:33 +08:00
const props = defineProps({
visible: {
type: Boolean,
default: false
}
});
const emit = defineEmits(["update:visible", "refresh"]);
const formRef = ref();
const formData = reactive<GoodsDTO>({
2025-03-04 09:11:33 +08:00
goodsName: "",
price: 0,
stock: 0,
2025-03-04 17:17:46 +08:00
status: 1,
autoApproval: 0,
2025-03-04 17:17:46 +08:00
categoryId: 0,
goodsDetail: "",
coverImg: "",
usageInstruction: ""
2025-03-04 09:11:33 +08:00
});
2025-03-04 17:17:46 +08:00
const rules = reactive<FormRules>({
2025-03-04 09:11:33 +08:00
goodsName: [{ required: true, message: "商品名称必填", trigger: "blur" }],
price: [
{ required: true, message: "价格必填", trigger: "blur" },
{ type: 'number', min: 0, message: '价格不能小于0' }
],
stock: [
{ required: true, message: "库存必填", trigger: "blur" },
{ type: 'number', min: 0, message: '库存不能小于0' }
2025-03-04 17:17:46 +08:00
],
coverImg: [
{ required: true, message: "请上传商品封面图", trigger: "change" }
2025-03-04 09:11:33 +08:00
]
});
const handleConfirm = async () => {
try {
await formRef.value.validate();
await addGoodsApi(formData);
ElMessage.success("商品添加成功");
emit("refresh");
closeDialog();
} catch (error) {
console.error("表单验证失败", error);
}
};
const closeDialog = () => {
formRef.value.resetFields();
emit("update:visible", false);
};
2025-03-04 17:17:46 +08:00
// 在原有响应式数据后添加分类数据
const categoryOptions = ref<Array<CategoryDTO>>([]);
// 加载分类数据
const loadCategories = async () => {
try {
const { data } = await getCategoryAllApi();
categoryOptions.value = data;
if (data.length > 0) {
formData.categoryId = data[0].categoryId;
}
} catch (e) {
console.error("分类加载失败", e);
}
};
const handleAvatarSuccess = (response, uploadFile) => {
// 这里根据实际API响应结构调整
formData.coverImg = response.data.url;
};
const beforeAvatarUpload = (rawFile) => {
if (!['image/jpeg', 'image/png'].includes(rawFile.type)) {
ElMessage.error('封面图必须是 JPG/PNG 格式!')
return false
}
if (rawFile.size > 50 * 1024 * 1024) {
ElMessage.error('封面图大小不能超过 50MB!')
return false
}
return true
};
2025-03-04 09:11:33 +08:00
</script>
<template>
2025-03-04 17:17:46 +08:00
<el-dialog title="新增商品" :model-value="visible" width="600px" @close="closeDialog" @open="loadCategories">
<el-form ref="formRef" :model="formData" :rules="rules" label-width="80px" label-position="right">
2025-03-04 09:11:33 +08:00
<el-form-item label="商品名称" prop="goodsName">
2025-03-04 17:17:46 +08:00
<el-input v-model="formData.goodsName" placeholder="请输入商品名称" clearable />
</el-form-item>
<el-form-item label="封面图" prop="coverImg">
<el-upload class="avatar-uploader" :action="VITE_APP_BASE_API + '/file/upload'" :show-file-list="false"
:on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
<img v-if="formData.coverImg" :src="formData.coverImg" class="avatar" />
<el-icon v-else class="avatar-uploader-icon">
<Icon :icon="Upload" />
</el-icon>
</el-upload>
2025-03-04 09:11:33 +08:00
</el-form-item>
<el-form-item label="价格" prop="price">
2025-03-04 17:17:46 +08:00
<el-input-number v-model="formData.price" :min="0" :precision="2" controls-position="right" />
2025-03-04 09:11:33 +08:00
</el-form-item>
<el-form-item label="库存" prop="stock">
2025-03-04 17:17:46 +08:00
<el-input-number v-model="formData.stock" :min="0" controls-position="right" />
2025-03-04 09:11:33 +08:00
</el-form-item>
2025-03-04 17:17:46 +08:00
<!-- 在状态表单项前添加分类选择 -->
<el-form-item label="商品分类" prop="categoryId">
<el-select v-model="formData.categoryId" placeholder="请选择商品分类" clearable class="w-full">
<el-option v-for="category in categoryOptions" :key="category.categoryId" :label="category.categoryName"
:value="category.categoryId" />
</el-select>
</el-form-item>
<el-form-item label="自动审批" prop="autoApproval">
<el-switch v-model="formData.autoApproval" :active-value="1" :inactive-value="0" active-text="启用"
inactive-text="关闭" />
</el-form-item>
2025-03-04 09:11:33 +08:00
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">上架</el-radio>
<el-radio :label="2">下架</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="使用说明" prop="usageInstruction">
<el-input v-model="formData.usageInstruction" type="textarea" :rows="3" placeholder="请输入商品使用说明" />
</el-form-item>
2025-03-04 09:11:33 +08:00
</el-form>
2025-03-04 17:17:46 +08:00
2025-03-04 09:11:33 +08:00
<template #footer>
<el-button @click="closeDialog">取消</el-button>
2025-03-04 17:17:46 +08:00
<el-button type="primary" :icon="useRenderIcon(Confirm)" @click="handleConfirm">
2025-03-04 09:11:33 +08:00
确认
</el-button>
</template>
</el-dialog>
2025-03-04 17:17:46 +08:00
</template>
<style scoped>
.avatar-uploader {
:deep(.el-upload) {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
&:hover {
border-color: var(--el-color-primary);
}
}
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>