| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
| | | |
| | | |
| | | |