287 lines
8.1 KiB
Vue
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>
|