2025-03-08 08:18:35 +08:00
|
|
|
<script setup lang="ts">
|
|
|
|
import { ref, reactive, watch } from "vue";
|
|
|
|
import { ElMessage, FormRules } from "element-plus";
|
|
|
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
2025-05-17 09:57:22 +08:00
|
|
|
import { GoodsDTO, updateGoodsApi, addGoodsApi, deleteGoodsApi } from "@/api/shop/goods";
|
2025-03-08 08:18:35 +08:00
|
|
|
import { CategoryDTO, getCategoryAllApi } from "@/api/shop/category";
|
|
|
|
import Confirm from "@iconify-icons/ep/check";
|
|
|
|
import Upload from "@iconify-icons/ep/upload";
|
2025-05-17 09:57:22 +08:00
|
|
|
import Delete from "@iconify-icons/ep/delete";
|
2025-03-08 08:18:35 +08:00
|
|
|
const { VITE_APP_BASE_API } = import.meta.env;
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
visible: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
row: {
|
|
|
|
type: Object as () => GoodsDTO,
|
|
|
|
default: null
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const emit = defineEmits(["update:visible", "refresh"]);
|
|
|
|
|
|
|
|
const formRef = ref();
|
|
|
|
const isEdit = ref(false);
|
|
|
|
const formData = reactive<GoodsDTO>({
|
|
|
|
goodsId: 0,
|
|
|
|
goodsName: "",
|
|
|
|
price: 0,
|
|
|
|
stock: 0,
|
|
|
|
status: 1,
|
2025-04-21 11:40:59 +08:00
|
|
|
autoApproval: 0,
|
2025-03-08 08:18:35 +08:00
|
|
|
categoryId: 0,
|
|
|
|
goodsDetail: "",
|
|
|
|
coverImg: ""
|
|
|
|
});
|
|
|
|
|
|
|
|
const rules = reactive<FormRules>({
|
|
|
|
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' }
|
|
|
|
],
|
|
|
|
coverImg: [
|
|
|
|
{ required: true, message: "请上传商品封面图", trigger: "change" }
|
|
|
|
]
|
|
|
|
});
|
|
|
|
|
|
|
|
const handleConfirm = async () => {
|
|
|
|
try {
|
|
|
|
await formRef.value.validate();
|
|
|
|
if (isEdit.value) {
|
|
|
|
await updateGoodsApi(formData.goodsId!, formData);
|
|
|
|
ElMessage.success("商品修改成功");
|
|
|
|
} else {
|
|
|
|
await addGoodsApi(formData);
|
|
|
|
ElMessage.success("商品添加成功");
|
|
|
|
}
|
|
|
|
emit("refresh");
|
|
|
|
closeDialog();
|
|
|
|
} catch (error) {
|
|
|
|
console.error("表单操作失败", error);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const closeDialog = () => {
|
|
|
|
formRef.value.resetFields();
|
|
|
|
emit("update:visible", false);
|
|
|
|
};
|
|
|
|
|
|
|
|
const categoryOptions = ref<Array<CategoryDTO>>([]);
|
|
|
|
|
|
|
|
// 初始化表单数据和分类数据
|
|
|
|
const initForm = async () => {
|
|
|
|
try {
|
|
|
|
// 加载分类数据
|
|
|
|
const { data } = await getCategoryAllApi();
|
|
|
|
categoryOptions.value = data;
|
|
|
|
|
|
|
|
// 初始化表单数据
|
|
|
|
if (props.row?.goodsId) {
|
|
|
|
isEdit.value = true;
|
|
|
|
Object.assign(formData, props.row);
|
|
|
|
// 确保数字类型
|
|
|
|
formData.price = Number(formData.price);
|
|
|
|
formData.stock = Number(formData.stock);
|
|
|
|
} else {
|
|
|
|
isEdit.value = false;
|
|
|
|
if (data.length > 0) {
|
|
|
|
formData.categoryId = data[0].categoryId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.error("数据加载失败", e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleAvatarSuccess = (response, uploadFile) => {
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
watch(
|
|
|
|
() => props.visible,
|
|
|
|
val => {
|
|
|
|
if (val) initForm();
|
|
|
|
}
|
|
|
|
);
|
2025-05-17 09:57:22 +08:00
|
|
|
const handleDelete = async () => {
|
|
|
|
try {
|
|
|
|
await deleteGoodsApi(formData.goodsId);
|
|
|
|
ElMessage.success("删除成功");
|
|
|
|
emit("refresh");
|
|
|
|
closeDialog();
|
|
|
|
} catch (error) {
|
|
|
|
console.error("删除失败", error);
|
|
|
|
}
|
|
|
|
};
|
2025-03-08 08:18:35 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2025-05-17 09:57:22 +08:00
|
|
|
<el-drawer :title="isEdit ? '编辑商品' : '新增商品'" :model-value="visible" size="50%" direction="rtl" @close="closeDialog">
|
2025-03-08 08:18:35 +08:00
|
|
|
<el-form ref="formRef" :model="formData" :rules="rules" label-width="80px" label-position="right">
|
|
|
|
<el-form-item label="商品名称" prop="goodsName">
|
|
|
|
<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>
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item label="价格" prop="price">
|
|
|
|
<el-input-number v-model="formData.price" :min="0" :precision="2" controls-position="right" />
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item label="库存" prop="stock">
|
|
|
|
<el-input-number v-model="formData.stock" :min="0" controls-position="right" />
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<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>
|
|
|
|
|
2025-04-21 11:40:59 +08:00
|
|
|
<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-08 08:18:35 +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>
|
2025-05-17 09:57:22 +08:00
|
|
|
<el-form-item label="操作">
|
|
|
|
<el-popconfirm :title="`确认删除【${row.goodsName}】?`" @confirm="handleDelete()">
|
|
|
|
<template #reference>
|
|
|
|
<el-button type="danger" link :icon="useRenderIcon(Delete)" class="right-btn">
|
|
|
|
删除该商品
|
|
|
|
</el-button>
|
|
|
|
</template>
|
|
|
|
</el-popconfirm>
|
|
|
|
</el-form-item>
|
2025-03-08 08:18:35 +08:00
|
|
|
</el-form>
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
<el-button @click="closeDialog">取消</el-button>
|
|
|
|
<el-button type="primary" :icon="useRenderIcon(Confirm)" @click="handleConfirm">
|
|
|
|
确认
|
|
|
|
</el-button>
|
|
|
|
</template>
|
2025-05-17 09:57:22 +08:00
|
|
|
</el-drawer>
|
2025-03-08 08:18:35 +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>
|