¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-card class="search-card" shadow="never"> |
| | | <el-form :model="searchForm" :inline="true"> |
| | | <el-form-item label="åååç§°ï¼"> |
| | | <el-input v-model="searchForm.productName" placeholder="请è¾å
¥åååç§°" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºååç§°ï¼"> |
| | | <el-input v-model="searchForm.supplierName" placeholder="请è¾å
¥ä¾åºååç§°" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="ä»·æ ¼ç¶æï¼"> |
| | | <el-select v-model="searchForm.status" placeholder="è¯·éæ©ç¶æ" clearable> |
| | | <el-option label="ææ" value="active" /> |
| | | <el-option label="å·²è¿æ" value="expired" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleSearch">æç´¢</el-button> |
| | | <el-button @click="resetSearch">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <el-card class="table-card" shadow="never"> |
| | | <div class="table-header"> |
| | | <el-button type="primary" @click="openDialog('add')">æ°å¢ä»·æ ¼</el-button> |
| | | <el-button type="success" @click="handleBatchUpdate">æ¹éæ´æ°</el-button> |
| | | <el-button type="danger" @click="handleBatchDelete">æ¹éå é¤</el-button> |
| | | </div> |
| | | |
| | | <el-table :data="tableData" border v-loading="loading" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | <el-table-column label="åååç§°" prop="productName" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specification" /> |
| | | <el-table-column label="ä¾åºååç§°" prop="supplierName" /> |
| | | <el-table-column label="åä»·æ ¼" prop="oldPrice" width="120"> |
| | | <template #default="{ row }">Â¥{{ row.oldPrice.toFixed(2) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="æ°ä»·æ ¼" prop="newPrice" width="120"> |
| | | <template #default="{ row }">Â¥{{ row.newPrice.toFixed(2) }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="è°ä»·å¹
度" prop="priceChange" width="120"> |
| | | <template #default="{ row }"> |
| | | <span :style="{ color: row.priceChange >= 0 ? '#f56c6c' : '#67c23a' }"> |
| | | {{ row.priceChange >= 0 ? '+' : '' }}{{ row.priceChange.toFixed(2) }}% |
| | | </span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="çææ¶é´" prop="effectiveTime" width="180" /> |
| | | <el-table-column label="ç¶æ" prop="status" width="100"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStatusType(row.status)">{{ getStatusText(row.status) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="200" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button type="primary" link @click="openDialog('edit', row)">ç¼è¾</el-button> |
| | | <el-button type="success" link @click="handleApply(row)" v-if="row.status === 'pending'">åºç¨</el-button> |
| | | <el-button type="danger" link @click="handleDelete(row)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | |
| | | <el-dialog v-model="dialogVisible" :title="dialogType === 'add' ? 'æ°å¢ä»·æ ¼' : 'ç¼è¾ä»·æ ¼'" width="600px"> |
| | | <el-form :model="formData" label-width="120px"> |
| | | <el-form-item label="åååç§°"> |
| | | <el-select v-model="formData.productName" placeholder="è¯·éæ©åå" style="width: 100%"> |
| | | <el-option label="ååA" value="ååA" /> |
| | | <el-option label="ååB" value="ååB" /> |
| | | <el-option label="ååC" value="ååC" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼åå·"> |
| | | <el-input v-model="formData.specification" placeholder="请è¾å
¥è§æ ¼åå·" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºååç§°"> |
| | | <el-select v-model="formData.supplierName" placeholder="è¯·éæ©ä¾åºå" style="width: 100%"> |
| | | <el-option label="ä¾åºåA" value="ä¾åºåA" /> |
| | | <el-option label="ä¾åºåB" value="ä¾åºåB" /> |
| | | <el-option label="ä¾åºåC" value="ä¾åºåC" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="åä»·æ ¼"> |
| | | <el-input v-model="formData.oldPrice" placeholder="请è¾å
¥åä»·æ ¼" type="number" /> |
| | | </el-form-item> |
| | | <el-form-item label="æ°ä»·æ ¼"> |
| | | <el-input v-model="formData.newPrice" placeholder="请è¾å
¥æ°ä»·æ ¼" type="number" /> |
| | | </el-form-item> |
| | | <el-form-item label="çææ¶é´"> |
| | | <el-date-picker v-model="formData.effectiveTime" type="datetime" placeholder="éæ©çææ¶é´" style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="è°ä»·åå "> |
| | | <el-select v-model="formData.reason" placeholder="è¯·éæ©è°ä»·åå " style="width: 100%"> |
| | | <el-option label="å¸åºä»·æ ¼åå¨" value="market" /> |
| | | <el-option label="ææ¬åå" value="cost" /> |
| | | <el-option label="ä¾åºåè°æ´" value="supplier" /> |
| | | <el-option label="å
¶ä»åå " value="other" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨"> |
| | | <el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请è¾å
¥å¤æ³¨ä¿¡æ¯" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleSubmit">ç¡®å®</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | |
| | | const loading = ref(false) |
| | | const dialogVisible = ref(false) |
| | | const dialogType = ref('add') |
| | | const selectedRows = ref([]) |
| | | |
| | | const searchForm = reactive({ |
| | | productName: '', |
| | | supplierName: '', |
| | | status: '' |
| | | }) |
| | | |
| | | const formData = reactive({ |
| | | productName: '', |
| | | specification: '', |
| | | supplierName: '', |
| | | oldPrice: 0, |
| | | newPrice: 0, |
| | | effectiveTime: '', |
| | | reason: '', |
| | | remark: '' |
| | | }) |
| | | |
| | | const mockData = [ |
| | | { |
| | | id: 1, |
| | | productName: 'ååA', |
| | | specification: 'è§æ ¼1', |
| | | supplierName: 'ä¾åºåA', |
| | | oldPrice: 50.00, |
| | | newPrice: 55.00, |
| | | priceChange: 10.00, |
| | | effectiveTime: '2025-12-01 00:00:00', |
| | | status: 'active', |
| | | reason: 'å¸åºä»·æ ¼åå¨', |
| | | remark: 'å¸åºä»·æ ¼ä¸æ¶¨' |
| | | }, |
| | | { |
| | | id: 2, |
| | | productName: 'ååB', |
| | | specification: 'è§æ ¼2', |
| | | supplierName: 'ä¾åºåB', |
| | | oldPrice: 80.00, |
| | | newPrice: 75.00, |
| | | priceChange: -6.25, |
| | | effectiveTime: '2025-12-01 00:00:00', |
| | | status: 'active', |
| | | reason: 'ææ¬åå', |
| | | remark: 'ææ¬ä¸é' |
| | | } |
| | | ] |
| | | |
| | | const tableData = ref([...mockData]) |
| | | |
| | | const getStatusType = (status) => { |
| | | const statusMap = { active: 'success', expired: 'info', pending: 'warning' } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | |
| | | const getStatusText = (status) => { |
| | | const statusMap = { active: 'ææ', expired: 'å·²è¿æ', pending: 'å¾
çæ' } |
| | | return statusMap[status] || 'æªç¥' |
| | | } |
| | | |
| | | const handleSearch = () => { |
| | | loading.value = true |
| | | setTimeout(() => { loading.value = false }, 500) |
| | | } |
| | | |
| | | const resetSearch = () => { |
| | | Object.assign(searchForm, { productName: '', supplierName: '', status: '' }) |
| | | } |
| | | |
| | | const openDialog = (type, row = {}) => { |
| | | dialogType.value = type |
| | | if (type === 'edit' && row.id) { |
| | | Object.assign(formData, { |
| | | productName: row.productName, |
| | | specification: row.specification, |
| | | supplierName: row.supplierName, |
| | | oldPrice: row.oldPrice, |
| | | newPrice: row.newPrice, |
| | | effectiveTime: row.effectiveTime, |
| | | reason: row.reason, |
| | | remark: row.remark |
| | | }) |
| | | } else { |
| | | Object.assign(formData, { |
| | | productName: '', |
| | | specification: '', |
| | | supplierName: '', |
| | | oldPrice: 0, |
| | | newPrice: 0, |
| | | effectiveTime: '', |
| | | reason: '', |
| | | remark: '' |
| | | }) |
| | | } |
| | | dialogVisible.value = true |
| | | } |
| | | |
| | | const handleSubmit = () => { |
| | | if (dialogType.value === 'add') { |
| | | const priceChange = ((formData.newPrice - formData.oldPrice) / formData.oldPrice) * 100 |
| | | const newPrice = { |
| | | id: Date.now(), |
| | | productName: formData.productName, |
| | | specification: formData.specification, |
| | | supplierName: formData.supplierName, |
| | | oldPrice: formData.oldPrice, |
| | | newPrice: formData.newPrice, |
| | | priceChange: priceChange, |
| | | effectiveTime: formData.effectiveTime, |
| | | status: 'pending', |
| | | reason: formData.reason, |
| | | remark: formData.remark |
| | | } |
| | | tableData.value.unshift(newPrice) |
| | | ElMessage.success('æ°å¢æå') |
| | | } |
| | | dialogVisible.value = false |
| | | } |
| | | |
| | | const handleApply = (row) => { |
| | | row.status = 'active' |
| | | ElMessage.success('ä»·æ ¼å·²åºç¨') |
| | | } |
| | | |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm('ç¡®å®è¦å é¤è¿æ¡è®°å½åï¼', 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | const index = tableData.value.findIndex(item => item.id === row.id) |
| | | if (index !== -1) { |
| | | tableData.value.splice(index, 1) |
| | | ElMessage.success('å 餿å') |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const handleBatchUpdate = () => { |
| | | ElMessage.success('æ¹éæ´æ°æå') |
| | | } |
| | | |
| | | const handleBatchDelete = () => { |
| | | ElMessage.success('æ¹éå 餿å') |
| | | } |
| | | |
| | | const handleSelectionChange = (rows) => { |
| | | selectedRows.value = rows |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { padding: 20px; } |
| | | .search-card { margin-bottom: 20px; } |
| | | .table-card { margin-bottom: 20px; } |
| | | .table-header { margin-bottom: 20px; } |
| | | </style> |