246 lines
7.9 KiB
TypeScript
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>
|
|
)
|
|
}
|