Files
shanxia2.0/web-admin/src/views/pl/inventory/ledger.vue
T
2026-08-21 16:50:47 +08:00

287 lines
8.1 KiB
Vue

<!-- 盘存统计 -->
<template>
<div class="page-container">
<section class="panel">
<div class="filter-block">
<el-form :model="searchForm" class="filter-form" @submit.prevent>
<div class="filter-grid">
<el-form-item label="产品名称">
<el-input
v-model="searchForm.productName"
placeholder="搜索产品名称"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
</div>
<div class="filter-actions">
<div class="filter-actions-end">
<el-button class="btn-ghost" :loading="loading" @click="handleReset">
<el-icon><RefreshLeft /></el-icon>重置
</el-button>
<el-button class="btn-main" type="primary" :loading="loading" @click="handleSearch">
<el-icon><Search /></el-icon>搜索
</el-button>
</div>
</div>
</el-form>
</div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="产品名称/编号"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="fetchData"
@reset-columns="resetToDefault"
/>
</div>
</div>
<div class="list-body list-wrap">
<ListTable table-key="pl-inventory-ledger"
class="list-table"
:data="inventoryList"
v-loading="loading"
stripe
row-key="id"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
min-width="68"
align="center"
header-align="center"
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column
prop="productName"
label="产品名称"
min-width="240"
align="left"
header-align="left"
class-name="col-name-wrap"
label-class-name="col-name-wrap"
/>
<el-table-column
v-if="isColumnVisible('brandName')"
label="品牌"
min-width="110"
show-overflow-tooltip
>
<template #default="{ row }">
{{ row.brandName || '—' }}
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productNo')"
prop="productNo"
label="产品编号"
min-width="120"
/>
<el-table-column
v-if="isColumnVisible('model')"
prop="model"
label="规格型号"
min-width="120"
/>
<el-table-column
v-if="isColumnVisible('unit')"
prop="unit"
label="单位"
min-width="80"
align="center"
/>
<el-table-column
v-if="isColumnVisible('stockQty')"
prop="stockQty"
label="库存数量"
min-width="120"
align="right"
>
<template #default="{ row }">
<span
:class="{
'text-danger': row.stockQty < (row.lowStockWarning || 0),
'text-warning': row.stockQty > (row.highStockWarning || Infinity)
}"
>
{{ row.stockQty }}
</span>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('warnThreshold')"
label="预警阈值"
min-width="150"
align="center"
>
<template #default="{ row }">
{{ row.lowStockWarning || '-' }} ~ {{ row.highStockWarning || '-' }}
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('status')"
label="状态"
min-width="100"
align="center"
>
<template #default="{ row }">
<el-tag v-if="row.stockQty < (row.lowStockWarning || 0)" type="danger" size="small">
库存不足
</el-tag>
<el-tag
v-else-if="row.stockQty > (row.highStockWarning || Infinity)"
type="warning"
size="small"
>
库存过高
</el-tag>
<el-tag v-else type="success" size="small">正常</el-tag>
</template>
</el-table-column>
</ListTable>
<div class="pagination list-pager-bar">
<div class="list-pager-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 100, 500]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</div>
</div>
</section>
</div>
</template>
<script setup lang="ts">
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { RefreshLeft, Search } from '@element-plus/icons-vue'
import { inventoryApi, type InventoryRecord } from '@/api/pl'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
const listColumnDefs = [
{ prop: 'brandName', label: '品牌' },
{ prop: 'productNo', label: '产品编号' },
{ prop: 'model', label: '规格型号' },
{ prop: 'unit', label: '单位' },
{ prop: 'stockQty', label: '库存数量' },
{ prop: 'warnThreshold', label: '预警阈值' },
{ prop: 'status', label: '状态' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('pl-inventory-ledger-list-columns', listColumnDefs)
const loading = ref(false)
const selectedRows = ref<any[]>([])
const inventoryList = ref<InventoryRecord[]>([])
const quickSearchKeyword = ref('')
const searchForm = reactive({
productName: ''
})
const pagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
function handleSelectionChange(rows: any[]) {
selectedRows.value = rows
}
async function fetchData() {
loading.value = true
try {
const keyword = quickSearchKeyword.value.trim()
const res = await inventoryApi.ledger({
productName: keyword || searchForm.productName || undefined,
page: pagination.page,
pageSize: pagination.pageSize
})
const data = res.data ?? res
inventoryList.value = data.list || []
pagination.total = data.total || 0
} finally {
loading.value = false
}
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
fetchData()
}
function handleReset() {
searchForm.productName = ''
quickSearchKeyword.value = ''
pagination.page = 1
fetchData()
}
function handleQuickSearch() {
pagination.page = 1
fetchData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
fetchData()
}
function handlePageChange() {
fetchData()
}
function handleSizeChange() {
pagination.page = 1
fetchData()
}
useDbInstantSearch(() => ({ productName: searchForm.productName }), handleSearch)
onMounted(() => {
fetchData()
})
</script>
<style scoped>
.text-danger {
color: var(--el-color-danger);
}
.text-warning {
color: var(--el-color-warning);
}
</style>