测试暂未发现问题
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { ref, onMounted, reactive, watch } from 'vue'
|
||||
import { ref, onMounted, reactive, watch, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
getProductsList,
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
updateProducts,
|
||||
deleteProductsAPI
|
||||
} from '../api/products'
|
||||
import { getSupplierList } from '../api/suppliers'
|
||||
import { hasPermission } from '../store/user'
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
@@ -32,6 +34,11 @@ const currentProductId = ref(null)
|
||||
const loading = ref(false)
|
||||
const showDetailDialog = ref(false)
|
||||
const currentDetail = ref(null)
|
||||
const supplierList = ref([])
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(10)
|
||||
|
||||
// 产品类型映射
|
||||
const productTypeMap = {
|
||||
@@ -44,31 +51,67 @@ const productTypeMap = {
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
fetchSuppliers()
|
||||
})
|
||||
|
||||
const fetchSuppliers = async () => {
|
||||
try {
|
||||
const res = await getSupplierList(null, { quiet: true })
|
||||
supplierList.value = res.data?.list || res.data || []
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// 监听筛选变化
|
||||
watch(filterType, () => {
|
||||
currentPage.value = 1
|
||||
handleSearch()
|
||||
})
|
||||
|
||||
// 监听搜索结果变化,重置页码
|
||||
watch(searchResults, () => {
|
||||
currentPage.value = 1
|
||||
})
|
||||
|
||||
// 分页后的数据
|
||||
const paginatedData = computed(() => {
|
||||
const start = (currentPage.value - 1) * pageSize.value
|
||||
const end = start + pageSize.value
|
||||
return searchResults.value.slice(start, end)
|
||||
})
|
||||
|
||||
// 获取产品列表
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
const res = await getProductsList()
|
||||
searchResults.value = res.data
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
searchResults.value = res.data.list || res.data
|
||||
}
|
||||
showSearchResults.value = true
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleSearch = async () => {
|
||||
// 如果在表单页面,先关闭表单
|
||||
if (showAddForm.value) {
|
||||
cancelAdd()
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
const res = await getProductsList({
|
||||
keyword: search.value,
|
||||
searchField: select.value,
|
||||
type: filterType.value
|
||||
})
|
||||
searchResults.value = res.data
|
||||
const params = {
|
||||
type: filterType.value || undefined
|
||||
}
|
||||
if (select.value === '1') {
|
||||
params.id = Number(search.value)
|
||||
} else if (select.value === '2') {
|
||||
params.name = search.value
|
||||
} else if (select.value === '3') {
|
||||
params.supplier = search.value
|
||||
}
|
||||
const res = await getProductsList(params)
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
searchResults.value = res.data.list || res.data
|
||||
}
|
||||
showSearchResults.value = true
|
||||
loading.value = false
|
||||
}
|
||||
@@ -79,6 +122,7 @@ const resetSearch = () => {
|
||||
filterType.value = ''
|
||||
showSearchResults.value = false
|
||||
showAddForm.value = false
|
||||
currentPage.value = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
@@ -137,26 +181,36 @@ const saveProduct = async () => {
|
||||
remark: form.remark
|
||||
}
|
||||
|
||||
if (isEditMode.value) {
|
||||
await updateProducts(currentProductId.value, formData)
|
||||
ElMessage.success('更新成功')
|
||||
} else {
|
||||
await createProducts(formData)
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
try {
|
||||
if (isEditMode.value) {
|
||||
await updateProducts(currentProductId.value, formData)
|
||||
ElMessage.success('更新成功')
|
||||
} else {
|
||||
await createProducts(formData)
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
|
||||
cancelAdd()
|
||||
fetchData()
|
||||
cancelAdd()
|
||||
fetchData()
|
||||
} catch (error) {
|
||||
ElMessage.error(error.response?.data?.message || error.message || '操作失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除产品
|
||||
const deleteProduct = async (id) => {
|
||||
ElMessageBox.confirm('确定删除该产品吗?', '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
await deleteProductsAPI(id)
|
||||
ElMessage.success('删除成功')
|
||||
fetchData()
|
||||
try {
|
||||
await deleteProductsAPI(id)
|
||||
ElMessage.success('删除成功')
|
||||
fetchData()
|
||||
} catch (error) {
|
||||
ElMessage.error(error.response?.data?.message || error.message || '删除失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -191,13 +245,22 @@ const getTypeTag = (type) => {
|
||||
return map[type] || 'info'
|
||||
}
|
||||
|
||||
// 页码变化
|
||||
const handleCurrentChange = (val) => {
|
||||
currentPage.value = val
|
||||
}
|
||||
|
||||
// 检查权限
|
||||
const canCreate = computed(() => hasPermission('product', 'create'))
|
||||
const canUpdate = computed(() => hasPermission('product', 'update'))
|
||||
const canDelete = computed(() => hasPermission('product', 'delete'))
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="products-container">
|
||||
<div class="products-container">
|
||||
<!-- 搜索栏区域 -->
|
||||
<div class="products-search">
|
||||
<!-- 第一行:搜索框 + 按钮 -->
|
||||
<div class="search-row">
|
||||
<el-input v-model="search" style="max-width: 500px" placeholder="请输入搜索关键词"
|
||||
class="products-search-with-select" @keyup.enter="handleSearch">
|
||||
@@ -214,13 +277,12 @@ const getTypeTag = (type) => {
|
||||
</el-input>
|
||||
|
||||
<div class="search-buttons">
|
||||
<el-button type="success" @click="addProduct">新增产品</el-button>
|
||||
<el-button type="success" @click="addProduct" v-if="canCreate">新增产品</el-button>
|
||||
<el-button type="default" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二行:筛选条件 -->
|
||||
<div class="filter-row">
|
||||
<div class="filter-row">
|
||||
<span class="filter-label">筛选条件:</span>
|
||||
<el-select v-model="filterType" placeholder="产品类型" clearable style="width: 140px; margin-right: 12px;">
|
||||
<el-option label="原材料" value="raw_material" />
|
||||
@@ -232,7 +294,7 @@ const getTypeTag = (type) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showSearchResults" class="products-list">
|
||||
<div v-if="showSearchResults" class="products-list">
|
||||
<div v-if="loading" class="loading-container">
|
||||
<el-skeleton :rows="5" animated />
|
||||
</div>
|
||||
@@ -241,8 +303,13 @@ const getTypeTag = (type) => {
|
||||
<el-empty description="暂无数据" />
|
||||
</div>
|
||||
|
||||
<div v-else class="products-table">
|
||||
<el-table :data="searchResults" v-loading="loading" border style="width: 100%" @row-dblclick="handleRowDblClick">
|
||||
<div v-else>
|
||||
<div class="list-header">
|
||||
<span class="total-count">共找到 {{ searchResults.length }} 条记录</span>
|
||||
</div>
|
||||
|
||||
<el-table :data="paginatedData" v-loading="loading" border style="width: 100%"
|
||||
@row-dblclick="handleRowDblClick" row-class-name="clickable-row">
|
||||
<el-table-column prop="id" label="ID" width="70" />
|
||||
<el-table-column prop="name" label="产品名称" min-width="120" />
|
||||
<el-table-column prop="type" label="产品类型" width="110">
|
||||
@@ -263,15 +330,23 @@ const getTypeTag = (type) => {
|
||||
<el-table-column prop="supplier" label="供应商" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="editProduct(row)">编辑</el-button>
|
||||
<el-button link type="danger" @click="deleteProduct(row.id)">删除</el-button>
|
||||
<el-button link type="primary" @click="editProduct(row)" v-if="canUpdate">编辑</el-button>
|
||||
<el-button link type="danger" @click="deleteProduct(row.id)" v-if="canDelete">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div style="margin-top: 10px; color: #909399; font-size: 14px;">
|
||||
找到 {{ searchResults.length }} 条结果
|
||||
<el-button link type="primary" @click="clearSearch">清空搜索</el-button>
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
:total="searchResults.length"
|
||||
layout="prev, pager, next"
|
||||
prev-text="上一页"
|
||||
next-text="下一页"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -285,7 +360,7 @@ const getTypeTag = (type) => {
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="产品类型" required>
|
||||
<el-select v-model="form.type" placeholder="请选择产品类型" style="width: 100%;">
|
||||
<el-select v-model="form.type" placeholder="请选择产品类型" style="width: 100%;">
|
||||
<el-option label="原材料" value="raw_material" />
|
||||
<el-option label="包装材料" value="packaging" />
|
||||
<el-option label="设备器具" value="equipment" />
|
||||
@@ -299,22 +374,24 @@ const getTypeTag = (type) => {
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="产品单价" required>
|
||||
<el-input-number v-model="form.price" :min="0" :step="0.01" :precision="2" style="width: 100%;" />
|
||||
<el-input-number v-model="form.price" :min="0" :step="0.01" :precision="2" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="计量单位">
|
||||
<el-input v-model="form.unit" placeholder="请输入计量单位" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="产品规格">
|
||||
<el-form-item label="产品规格">
|
||||
<el-input v-model="form.specification" placeholder="请输入产品规格/型号" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="供应商">
|
||||
<el-input v-model="form.supplier" placeholder="请输入供应商名称" />
|
||||
<el-select v-model="form.supplier" placeholder="请选择供应商" style="width: 100%;" filterable clearable>
|
||||
<el-option v-for="s in supplierList" :key="s.id" :label="s.name" :value="s.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注信息" />
|
||||
</el-form-item>
|
||||
|
||||
@@ -350,7 +427,7 @@ const getTypeTag = (type) => {
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="closeDetail">关闭</el-button>
|
||||
<el-button type="primary" @click="closeDetail(); editProduct(currentDetail)">编辑</el-button>
|
||||
<el-button type="primary" @click="(() => { const d = currentDetail; closeDetail(); editProduct(d) })()">编辑</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
@@ -358,5 +435,78 @@ const getTypeTag = (type) => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.products-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.products-search {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.search-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.search-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 15px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.list-header {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.total-count {
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.loading-container,
|
||||
.empty-container {
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.clickable-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 15px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.amount-text {
|
||||
font-weight: 600;
|
||||
color: #e6a23c;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user