<template> 
 | 
  <div class="app-container"> 
 | 
    <el-card shadow="never"> 
 | 
      <div class="toolbar"> 
 | 
        <el-input 
 | 
          v-model="query.keyword" 
 | 
          placeholder="搜索名称/类别" 
 | 
          clearable 
 | 
          style="width: 240px" 
 | 
          @keyup.enter="handleSearch" 
 | 
        /> 
 | 
        <el-select 
 | 
          v-model="query.status" 
 | 
          placeholder="状态" 
 | 
          clearable 
 | 
          style="width: 140px; margin-left: 12px" 
 | 
        > 
 | 
          <el-option label="启用" value="启用" /> 
 | 
          <el-option label="停用" value="停用" /> 
 | 
        </el-select> 
 | 
        <el-button type="primary" style="margin-left: 12px" @click="handleSearch">查询</el-button> 
 | 
        <el-button @click="resetQuery">重置</el-button> 
 | 
        <el-button type="success" plain style="float: right" @click="openCreate">新增</el-button> 
 | 
      </div> 
 | 
  
 | 
      <el-table :data="pagedList" border style="width: 100%" height="480" stripe> 
 | 
        <el-table-column prop="id" label="编号" width="90" sortable /> 
 | 
        <el-table-column prop="name" label="名称" min-width="140" /> 
 | 
        <el-table-column prop="category" label="类别" width="120" /> 
 | 
        <el-table-column prop="stock" label="库存" width="100" sortable /> 
 | 
        <el-table-column prop="price" label="单价(¥)" width="120"> 
 | 
          <template #default="scope">{{ formatPrice(scope.row.price) }}</template> 
 | 
        </el-table-column> 
 | 
        <el-table-column label="状态" width="120"> 
 | 
          <template #default="scope"> 
 | 
            <el-tag :type="scope.row.status === '启用' ? 'success' : 'info'">{{ scope.row.status }}</el-tag> 
 | 
          </template> 
 | 
        </el-table-column> 
 | 
        <el-table-column prop="updatedAt" label="更新时间" min-width="160" /> 
 | 
        <el-table-column label="操作" width="180" fixed="right"> 
 | 
          <template #default="scope"> 
 | 
            <el-button link type="primary" @click="openEdit(scope.row)">编辑</el-button> 
 | 
            <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> 
 | 
          </template> 
 | 
        </el-table-column> 
 | 
      </el-table> 
 | 
  
 | 
      <div class="pagination"> 
 | 
        <el-pagination 
 | 
          background 
 | 
          layout="total, sizes, prev, pager, next, jumper" 
 | 
          :total="filteredList.length" 
 | 
          :page-sizes="[5, 10, 20, 50]" 
 | 
          :page-size="pager.pageSize" 
 | 
          :current-page="pager.pageNum" 
 | 
          @size-change="handleSizeChange" 
 | 
          @current-change="handleCurrentChange" 
 | 
        /> 
 | 
      </div> 
 | 
    </el-card> 
 | 
  
 | 
    <el-dialog v-model="dialogVisible" :title="isEdit ? '编辑' : '新增'" width="520px"> 
 | 
      <el-form :model="form" :rules="rules" ref="formRef" label-width="90px"> 
 | 
        <el-form-item label="名称" prop="name"> 
 | 
          <el-input v-model="form.name" placeholder="请输入名称" /> 
 | 
        </el-form-item> 
 | 
        <el-form-item label="类别" prop="category"> 
 | 
          <el-select v-model="form.category" placeholder="请选择类别" style="width: 100%"> 
 | 
            <el-option label="原料" value="原料" /> 
 | 
            <el-option label="半成品" value="半成品" /> 
 | 
            <el-option label="成品" value="成品" /> 
 | 
          </el-select> 
 | 
        </el-form-item> 
 | 
        <el-form-item label="库存" prop="stock"> 
 | 
          <el-input v-model.number="form.stock" type="number" min="0" /> 
 | 
        </el-form-item> 
 | 
        <el-form-item label="单价(¥)" prop="price"> 
 | 
          <el-input v-model.number="form.price" type="number" min="0" step="0.01" /> 
 | 
        </el-form-item> 
 | 
        <el-form-item label="状态" prop="status"> 
 | 
          <el-radio-group v-model="form.status"> 
 | 
            <el-radio label="启用">启用</el-radio> 
 | 
            <el-radio label="停用">停用</el-radio> 
 | 
          </el-radio-group> 
 | 
        </el-form-item> 
 | 
      </el-form> 
 | 
      <template #footer> 
 | 
        <el-button @click="dialogVisible = false">取 消</el-button> 
 | 
        <el-button type="primary" @click="submitForm">确 定</el-button> 
 | 
      </template> 
 | 
    </el-dialog> 
 | 
  </div> 
 | 
   
 | 
</template> 
 | 
  
 | 
<script setup> 
 | 
import { ref, reactive, computed, nextTick } from 'vue' 
 | 
import { ElMessage, ElMessageBox } from 'element-plus' 
 | 
  
 | 
defineOptions({ name: 'FakePage' }) 
 | 
  
 | 
const query = reactive({ 
 | 
  keyword: '', 
 | 
  status: '' 
 | 
}) 
 | 
  
 | 
const pager = reactive({ 
 | 
  pageNum: 1, 
 | 
  pageSize: 10 
 | 
}) 
 | 
  
 | 
const allList = ref(generateMockData()) 
 | 
  
 | 
const filteredList = computed(() => { 
 | 
  const keyword = (query.keyword || '').trim() 
 | 
  const status = query.status 
 | 
  return allList.value.filter(item => { 
 | 
    const hitKeyword = !keyword || item.name.includes(keyword) || item.category.includes(keyword) 
 | 
    const hitStatus = !status || item.status === status 
 | 
    return hitKeyword && hitStatus 
 | 
  }) 
 | 
}) 
 | 
  
 | 
const pagedList = computed(() => { 
 | 
  const start = (pager.pageNum - 1) * pager.pageSize 
 | 
  const end = start + pager.pageSize 
 | 
  return filteredList.value.slice(start, end) 
 | 
}) 
 | 
  
 | 
function handleSearch() { 
 | 
  pager.pageNum = 1 
 | 
} 
 | 
  
 | 
function resetQuery() { 
 | 
  query.keyword = '' 
 | 
  query.status = '' 
 | 
  pager.pageNum = 1 
 | 
} 
 | 
  
 | 
function handleSizeChange(size) { 
 | 
  pager.pageSize = size 
 | 
  pager.pageNum = 1 
 | 
} 
 | 
  
 | 
function handleCurrentChange(page) { 
 | 
  pager.pageNum = page 
 | 
} 
 | 
  
 | 
function formatPrice(val) { 
 | 
  return Number(val || 0).toFixed(2) 
 | 
} 
 | 
  
 | 
// 新增/编辑 
 | 
const dialogVisible = ref(false) 
 | 
const isEdit = ref(false) 
 | 
const formRef = ref() 
 | 
const form = reactive({ id: null, name: '', category: '', stock: 0, price: 0, status: '启用' }) 
 | 
  
 | 
const rules = { 
 | 
  name: [{ required: true, message: '请输入名称', trigger: 'blur' }], 
 | 
  category: [{ required: true, message: '请选择类别', trigger: 'change' }], 
 | 
  stock: [{ required: true, message: '请输入库存', trigger: 'blur' }], 
 | 
  price: [{ required: true, message: '请输入单价', trigger: 'blur' }] 
 | 
} 
 | 
  
 | 
function openCreate() { 
 | 
  isEdit.value = false 
 | 
  Object.assign(form, { id: null, name: '', category: '', stock: 0, price: 0, status: '启用' }) 
 | 
  dialogVisible.value = true 
 | 
  nextTick(() => formRef.value?.clearValidate?.()) 
 | 
} 
 | 
  
 | 
function openEdit(row) { 
 | 
  isEdit.value = true 
 | 
  Object.assign(form, JSON.parse(JSON.stringify(row))) 
 | 
  dialogVisible.value = true 
 | 
  nextTick(() => formRef.value?.clearValidate?.()) 
 | 
} 
 | 
  
 | 
function submitForm() { 
 | 
  formRef.value?.validate?.((valid) => { 
 | 
    if (!valid) return 
 | 
    if (isEdit.value) { 
 | 
      const index = allList.value.findIndex(x => x.id === form.id) 
 | 
      if (index > -1) { 
 | 
        allList.value[index] = { ...form, updatedAt: nowString() } 
 | 
        ElMessage.success('已保存') 
 | 
      } 
 | 
    } else { 
 | 
      const newId = Date.now() 
 | 
      allList.value.unshift({ ...form, id: newId, updatedAt: nowString() }) 
 | 
      ElMessage.success('已新增') 
 | 
    } 
 | 
    dialogVisible.value = false 
 | 
  }) 
 | 
} 
 | 
  
 | 
function handleDelete(row) { 
 | 
  ElMessageBox.confirm(`确认删除【${row.name}】吗?`, '提示', { type: 'warning' }) 
 | 
    .then(() => { 
 | 
      allList.value = allList.value.filter(x => x.id !== row.id) 
 | 
      ElMessage.success('已删除') 
 | 
    }) 
 | 
    .catch(() => {}) 
 | 
} 
 | 
  
 | 
function generateMockData() { 
 | 
  const categories = ['原料', '半成品', '成品'] 
 | 
  const statusOptions = ['启用', '停用'] 
 | 
  const list = [] 
 | 
  for (let i = 1; i <= 36; i++) { 
 | 
    list.push({ 
 | 
      id: i, 
 | 
      name: `物料-${i.toString().padStart(3, '0')}`, 
 | 
      category: categories[i % categories.length], 
 | 
      stock: Math.floor(Math.random() * 1000), 
 | 
      price: (Math.random() * 500 + 10).toFixed(2), 
 | 
      status: statusOptions[i % 2], 
 | 
      updatedAt: nowString() 
 | 
    }) 
 | 
  } 
 | 
  return list 
 | 
} 
 | 
  
 | 
function nowString() { 
 | 
  const d = new Date() 
 | 
  const yyyy = d.getFullYear() 
 | 
  const MM = String(d.getMonth() + 1).padStart(2, '0') 
 | 
  const dd = String(d.getDate()).padStart(2, '0') 
 | 
  const hh = String(d.getHours()).padStart(2, '0') 
 | 
  const mm = String(d.getMinutes()).padStart(2, '0') 
 | 
  const ss = String(d.getSeconds()).padStart(2, '0') 
 | 
  return `${yyyy}-${MM}-${dd} ${hh}:${mm}:${ss}` 
 | 
} 
 | 
</script> 
 | 
  
 | 
<style scoped> 
 | 
.toolbar { 
 | 
  margin-bottom: 12px; 
 | 
} 
 | 
.pagination { 
 | 
  margin-top: 12px; 
 | 
  text-align: right; 
 | 
} 
 | 
</style> 
 |