Files
Market-Front/src/pages/admin/ProductPage.vue

472 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<ManageLayout>
<div class="product-manage-page">
<!-- 搜索和操作按钮 -->
<div class="mb-6 flex justify-between items-center">
<div class="flex items-center">
<el-input
v-model="searchKeyword"
placeholder="搜索商品名称或分类"
style="width: 300px; margin-right: 16px"
@keyup.enter="handleSearch"
>
<template #prefix>
<i class="fa fa-search"></i>
</template>
</el-input>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</div>
<el-button type="success" @click="showAddProductDialog = true">
<i class="fa fa-plus mr-1"></i> 添加商品
</el-button>
</div>
<!-- 商品表格 -->
<el-table
:data="paginatedProducts"
style="width: 100%"
v-loading="loading"
border
>
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="商品名称" />
<el-table-column prop="categoryName" label="分类" />
<el-table-column prop="price" label="价格" width="100">
<template #default="{ row }"> ¥{{ row.price }} </template>
</el-table-column>
<el-table-column prop="stock" label="库存" width="100" />
<el-table-column prop="sales" label="销量" width="100" />
<el-table-column prop="userId" label="商家ID" width="100">
<template #default="{ row }">
{{ row.userId }}
</template>
</el-table-column>
<el-table-column label="商家名称" width="120">
<template #default="{ row }">
{{ getUserById(row.userId || 0)?.username || "未知商家" }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
{{ row.status === 1 ? "上架" : "下架" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="380">
<template #default="{ row }">
<el-button size="small" @click="editProduct(row)">编辑</el-button>
<el-button
size="small"
:type="row.status === 1 ? 'warning' : 'success'"
@click="toggleProductStatus(row)"
>
{{ row.status === 1 ? "下架" : "上架" }}
</el-button>
<el-button size="small" type="danger" @click="deleteProduct(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="mt-6 flex justify-center">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5, 10, 20, 50]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="filteredProducts.length"
/>
</div>
<!-- 添加/编辑商品对话框 -->
<el-dialog
:title="isEditing ? '编辑商品' : '添加商品'"
v-model="showAddProductDialog"
width="600px"
>
<el-form
:model="productForm"
:rules="productFormRules"
ref="productFormRef"
label-width="100px"
>
<el-form-item label="商品名称" prop="name">
<el-input v-model="productForm.name" />
</el-form-item>
<el-form-item label="分类" prop="categoryId">
<el-select
v-model="productForm.categoryId"
placeholder="请选择分类"
>
<el-option
v-for="category in categories"
:key="category.id"
:label="category.name"
:value="category.id"
/>
</el-select>
</el-form-item>
<el-form-item label="副标题" prop="subtitle">
<el-input v-model="productForm.subtitle" />
</el-form-item>
<el-form-item label="主图" prop="mainImage">
<el-input v-model="productForm.mainImage" placeholder="图片URL" />
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input-number
v-model="productForm.price"
:precision="2"
:min="0"
/>
</el-form-item>
<el-form-item label="库存" prop="stock">
<el-input-number v-model="productForm.stock" :min="0" />
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input
v-model="productForm.description"
type="textarea"
:rows="3"
placeholder="商品描述"
/>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="productForm.sort" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="showAddProductDialog = false">取消</el-button>
<el-button type="primary" @click="submitProductForm"
>确定</el-button
>
</div>
</template>
</el-dialog>
</div>
</ManageLayout>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {
getProductList,
getProductCategories,
saveProduct,
updateProduct,
updateProductStatus,
deleteProductAPI,
} from "@/apis/product";
import { getUserList } from "@/apis/user"; // 导入获取用户列表的API
import { ProductListItem, ProductCategory, ProductDTO } from "@/types/product";
import { UserDTO } from "@/types/user"; // 导入用户类型
import ManageLayout from "@/layouts/ManageLayout.vue";
import { useRouter } from "vue-router";
import { useUserStore } from "@/stores/UserStore";
// 获取用户存储和路由
const userStore = useUserStore();
const router = useRouter();
// 响应式数据
const loading = ref(false);
const searchKeyword = ref("");
const showAddProductDialog = ref(false);
const isEditing = ref(false);
const productFormRef = ref();
// 分页数据
const currentPage = ref(1);
const pageSize = ref(10);
// 商品表单
const productForm = ref<ProductDTO>({
id: undefined,
categoryId: 0, // 设置默认值为0
name: "",
subtitle: "",
mainImage: "",
description: "",
price: 0,
stock: 0,
sort: 0,
});
// 所有商品数据
const products = ref<ProductListItem[]>([]);
// 商品分类
const categories = ref<ProductCategory[]>([]);
// 所有用户数据(用于获取商家名称)
const users = ref<UserDTO[]>([]);
// 商品表单验证规则
const productFormRules = reactive({
name: [
{ required: true, message: "请输入商品名称", trigger: "blur" },
{
min: 2,
max: 50,
message: "商品名称长度应在2-50个字符之间",
trigger: "blur",
},
],
categoryId: [{ required: true, message: "请选择分类", trigger: "change" }],
price: [
{ required: true, message: "请输入价格", trigger: "blur" },
{ type: "number", min: 0, message: "价格不能小于0", trigger: "blur" },
],
stock: [
{ required: true, message: "请输入库存", trigger: "blur" },
{ type: "number", min: 0, message: "库存不能小于0", trigger: "blur" },
],
});
// 计算属性:根据搜索关键词过滤商品
const filteredProducts = computed(() => {
if (!searchKeyword.value) return products.value;
return products.value.filter(
(product) =>
product.name?.includes(searchKeyword.value) ||
product.categoryName?.includes(searchKeyword.value),
);
});
// 计算属性:当前页的商品数据
const paginatedProducts = computed(() => {
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
return filteredProducts.value.slice(start, end);
});
// 根据用户ID获取用户信息
const getUserById = (userId: number) => {
return users.value.find((user) => user.id === userId);
};
// 获取商品列表
const fetchProducts = async () => {
loading.value = true;
try {
const response = await getProductList({
page: 1,
size: 1000, // 获取所有商品
});
products.value = response.data.list || [];
} catch (error) {
console.error("获取商品列表错误:", error);
ElMessage.error("获取商品列表失败");
} finally {
loading.value = false;
}
};
// 获取商品分类
const fetchCategories = async () => {
try {
const response = await getProductCategories();
if (response.code === 200) {
categories.value = response.data || [];
}
} catch (error) {
console.error("获取商品分类错误:", error);
ElMessage.error("获取商品分类失败");
}
};
// 获取用户列表(用于显示商家名称)
const fetchUsers = async () => {
try {
const response = await getUserList();
if (response.code === 200) {
users.value = response.data || [];
}
} catch (error) {
console.error("获取用户列表错误:", error);
ElMessage.error("获取用户列表失败");
}
};
// 搜索处理
const handleSearch = () => {
currentPage.value = 1; // 重置到第一页
};
// 分页大小改变
const handleSizeChange = (size: number) => {
pageSize.value = size;
currentPage.value = 1;
};
// 当前页改变
const handleCurrentChange = (page: number) => {
currentPage.value = page;
};
// 编辑商品
const editProduct = (product: ProductListItem) => {
isEditing.value = true;
// 复制商品数据处理可能的undefined值
productForm.value = {
id: product.id,
categoryId: product.categoryId || 0, // 确保是number类型
name: product.name,
mainImage: product.mainImage,
price: product.price,
stock: product.stock || 0,
};
showAddProductDialog.value = true;
};
// 切换商品状态(上下架)
const toggleProductStatus = async (product: ProductListItem) => {
try {
await ElMessageBox.confirm(
`确定要${product.status === 1 ? "下架" : "上架"}商品 "${product.name}" 吗?`,
"确认操作",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
},
);
// 调用更新商品状态API
const response = await updateProductStatus(
product.id,
product.status === 1 ? 0 : 1,
);
if (response.code === 200) {
// 更新本地数据
const index = products.value.findIndex((p) => p.id === product.id);
if (index !== -1) {
products.value[index] = {
...product,
status: product.status === 1 ? 0 : 1,
};
}
ElMessage.success(`${product.status === 1 ? "下架" : "上架"}成功`);
} else {
ElMessage.error(response.message || "操作失败");
}
} catch (error) {
console.error("切换商品状态错误:", error);
if (error !== "cancel") {
ElMessage.error("操作失败");
}
}
};
// 删除商品
const deleteProduct = async (product: ProductListItem) => {
try {
await ElMessageBox.confirm(
`确定要删除商品 "${product.name}" 吗?此操作不可恢复!`,
"警告",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
},
);
// 调用删除商品API
const response = await deleteProductAPI(product.id);
if (response.code === 200) {
// 从本地数据中删除商品
products.value = products.value.filter((p) => p.id !== product.id);
ElMessage.success("删除成功");
} else {
ElMessage.error(response.message || "删除失败");
}
} catch (error) {
console.error("删除商品错误:", error);
if (error !== "cancel") {
ElMessage.error("操作失败");
}
}
};
// 提交商品表单
const submitProductForm = async () => {
try {
// 验证表单
const form = productFormRef.value;
if (!form) return;
await form.validate();
if (isEditing.value) {
// 更新商品
const response = await updateProduct(productForm.value);
if (response.code === 200) {
// 更新本地数据 - 重新获取商品列表以确保数据同步
fetchProducts();
ElMessage.success("更新成功");
} else {
ElMessage.error(response.message || "更新失败");
return;
}
} else {
// 添加新商品
const response = await saveProduct({
...productForm.value,
userId: userStore.userInfo?.id, // 假设当前用户为商家
});
if (response.code === 200) {
ElMessage.success("添加成功");
} else {
ElMessage.error(response.message || "添加失败");
return;
}
}
showAddProductDialog.value = false;
fetchProducts(); // 重新获取商品列表
} catch (error) {
console.error("提交商品表单错误:", error);
if (error) {
console.log(error);
} else {
ElMessage.error("提交失败");
}
}
};
// 初始化
onMounted(() => {
if (userStore.userInfo?.isAdmin !== 1) {
ElMessage.error("您没有管理员权限!");
router.push("/");
}
fetchProducts();
fetchCategories();
fetchUsers(); // 获取用户列表
});
</script>
<style scoped>
.product-manage-page {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.dialog-footer {
text-align: right;
}
</style>