246 lines
7.9 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import { ArrowLeft, ArrowRight } from 'lucide-react'
import type {
SupplyChainSupplierInformationFilters,
SupplyChainSupplierInformationPage,
SupplyChainSupplierInformationRow,
} from '@/lib/supply-chain-supplier-information'
import { SupplierInformationEditDialog } from './SupplierInformationEditDialog'
import { SupplierInformationFilters } from './SupplierInformationFilters'
import { SupplierInformationTable } from './SupplierInformationTable'
type SupplierInformationViewProps = {
data: SupplyChainSupplierInformationPage
filters: SupplyChainSupplierInformationFilters
canEdit: boolean
}
function pageHref(
page: number,
filters: SupplyChainSupplierInformationFilters,
pageSize: number,
) {
const params = new URLSearchParams()
Object.entries(filters).forEach(([key, value]) => {
if (value) params.set(key, value)
})
if (page > 1) params.set('page', String(page))
if (pageSize !== 50) params.set('pageSize', String(pageSize))
const query = params.toString()
return query
? `/supply-chain/purchase/supplier-information?${query}`
: '/supply-chain/purchase/supplier-information'
}
export function SupplierInformationView({
data,
filters,
canEdit,
}: SupplierInformationViewProps) {
const router = useRouter()
const { rows, pageInfo } = data
const [pageInput, setPageInput] = useState(String(pageInfo.page))
const [editingRow, setEditingRow] = useState<SupplyChainSupplierInformationRow | null>(null)
const hasPrevious = pageInfo.page > 1
const hasNext = pageInfo.page < pageInfo.totalPages
useEffect(() => {
setPageInput(String(pageInfo.page))
}, [pageInfo.page])
const goToPage = (page: number, pageSize = pageInfo.pageSize) => {
router.push(pageHref(page, filters, pageSize))
}
const commitPageInput = () => {
const parsed = Number(pageInput)
if (!Number.isFinite(parsed)) {
setPageInput(String(pageInfo.page))
return
}
const nextPage = Math.min(Math.max(1, Math.trunc(parsed)), pageInfo.totalPages)
setPageInput(String(nextPage))
if (nextPage !== pageInfo.page) goToPage(nextPage)
}
return (
<main className="supplier-information-page">
<header className="supplier-toolbar">
<div className="supplier-title-wrap">
<h1>供应商信息</h1>
<span>共 {pageInfo.total} 家</span>
</div>
</header>
<SupplierInformationFilters filters={filters} pageSize={pageInfo.pageSize} />
<SupplierInformationTable
rows={rows}
startIndex={(pageInfo.page - 1) * pageInfo.pageSize}
canEdit={canEdit}
onEdit={setEditingRow}
/>
<footer className="supplier-pagination">
<span className="supplier-page-total">共 {pageInfo.total} 家</span>
<button
type="button"
className="supplier-page-btn"
disabled={!hasPrevious}
onClick={() => goToPage(pageInfo.page - 1)}
>
<ArrowLeft size={12} strokeWidth={1.8} />
上一页
</button>
<span className="supplier-page-num">
<input
type="text"
inputMode="numeric"
pattern="[0-9]*"
className="supplier-page-input"
aria-label="跳转页码"
value={pageInput}
onChange={(event) => setPageInput(event.target.value.replace(/\D/g, ''))}
onBlur={commitPageInput}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault()
commitPageInput()
}
}}
/>
<span className="supplier-page-sep">/</span>
<span>{pageInfo.totalPages} 页</span>
</span>
<button
type="button"
className="supplier-page-btn"
disabled={!hasNext}
onClick={() => goToPage(pageInfo.page + 1)}
>
下一页
<ArrowRight size={12} strokeWidth={1.8} />
</button>
<span className="supplier-page-size-wrap">
每页
<select
className="supplier-page-size"
value={pageInfo.pageSize}
onChange={(event) => goToPage(1, Number(event.target.value))}
>
<option value={50}>50</option>
<option value={100}>100</option>
<option value={200}>200</option>
</select>
家
</span>
</footer>
{editingRow ? (
<SupplierInformationEditDialog
key={editingRow.supplierKey}
row={editingRow}
onClose={() => setEditingRow(null)}
onSaved={() => {
setEditingRow(null)
router.refresh()
}}
/>
) : null}
<style jsx>{`
.supplier-information-page {
position: relative;
display: flex;
height: 100%;
min-height: 0;
flex-direction: column;
background: var(--sc-bg);
color: var(--sc-ink);
}
.supplier-toolbar {
display: flex;
min-height: 54px;
align-items: center;
justify-content: space-between;
padding: 9px 18px;
border-bottom: 1px solid var(--sc-border);
background: var(--sc-surface);
}
.supplier-title-wrap {
display: flex;
align-items: baseline;
gap: 10px;
}
.supplier-title-wrap h1 { margin: 0; font-size: 17px; font-weight: 700; }
.supplier-title-wrap span { color: var(--sc-subtle); font-size: 12px; }
.supplier-pagination {
position: absolute;
bottom: 24px;
left: 50%;
z-index: 30;
display: flex;
align-items: center;
gap: 10px;
padding: 8px 16px;
transform: translateX(-50%);
border: 1px solid var(--sc-border);
border-radius: 999px;
background: rgba(255, 255, 255, 0.92);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06), 0 2px 4px rgba(0, 0, 0, 0.04);
color: var(--sc-muted);
font-size: 13px;
white-space: nowrap;
backdrop-filter: blur(16px);
}
.supplier-page-total { margin-right: 8px; }
.supplier-page-btn {
display: inline-flex;
height: 30px;
align-items: center;
gap: 4px;
border: 1px solid var(--sc-border);
border-radius: 4px;
padding: 0 12px;
background: var(--sc-surface);
color: var(--sc-ink);
font-size: 13px;
cursor: pointer;
}
.supplier-page-btn:hover:not(:disabled) { background: var(--sc-hover); }
.supplier-page-btn:disabled { color: var(--sc-subtle); cursor: not-allowed; opacity: 0.45; }
.supplier-page-num,
.supplier-page-size-wrap { display: inline-flex; align-items: center; gap: 4px; }
.supplier-page-input,
.supplier-page-size {
height: 28px;
border: 1px solid var(--sc-border);
border-radius: 4px;
outline: none;
background: var(--sc-surface);
color: var(--sc-ink);
font-size: 13px;
}
.supplier-page-input { width: 52px; padding: 0 6px; font-weight: 600; text-align: center; }
.supplier-page-size { padding: 0 8px; cursor: pointer; }
.supplier-page-input:focus,
.supplier-page-size:focus { border-color: var(--sc-muted); box-shadow: 0 0 0 2px rgba(82, 82, 91, 0.12); }
.supplier-page-sep { color: var(--sc-subtle); }
.supplier-page-size-wrap { gap: 6px; margin-left: 8px; }
@media (max-width: 700px) {
.supplier-pagination {
bottom: 12px;
max-width: calc(100% - 24px);
gap: 6px;
padding: 7px 10px;
overflow-x: auto;
}
.supplier-page-total { display: none; }
}
`}</style>
</main>
)
}