feat: support productionID input for order number resolution

- Add OrderNumberResolver service to auto-recognize productionID and 生产订单号 formats
- Integrate MySQL database lookup for productionID to 生产订单号 conversion
- Update OrderNumberInput component with format statistics display
- Modify Extractor and Cleaner to resolve order numbers before processing

Database configuration:
- Table: productionContractData_26 年压力表合同数据
- Fields: 总排号 (productionID), 生产订单号 (production order number)

Supported formats:
- productionID: 2 digits + 1 letter + serial number (e.g., 26B742)
- 生产订单号:SC + 14 digits (e.g., SC70202601040109)
This commit is contained in:
Misaka
2026-03-01 17:00:10 +08:00
parent 079d3aeff6
commit 450eb41f96
8 changed files with 744 additions and 11 deletions

View File

@@ -5,24 +5,78 @@ interface OrderNumberInputProps {
onChange: (value: string) => void
placeholder?: string
label?: string
enableFormatStats?: boolean // Whether to show format statistics
}
interface FormatStats {
productionIdCount: number
orderNumberCount: number
unknownCount: number
}
// Regular expression patterns for order number recognition
const ORDER_PATTERNS = {
// productionID: 2 digits + 1 letter + serial number (1+)
PRODUCTION_ID: /^\d{2}[A-Za-z]\d+$/,
// 生产订单号SC + 14 digits
ORDER_NUMBER: /^SC\d{14}$/
}
/**
* OrderNumberInput - A textarea component for entering line-separated order numbers
* Displays a count of valid order numbers entered
* Supports automatic recognition of productionID and 生产订单号 formats
*/
export const OrderNumberInput: React.FC<OrderNumberInputProps> = ({
value,
onChange,
placeholder = '请输入订单号,每行一个',
label = '订单号列表'
placeholder = '请输入订单号,每行一个\n支持两种格式\n- productionID: 22A1, 22A123\n- 生产订单号SC70202602120085',
label = '订单号列表',
enableFormatStats = true
}) => {
const [count, setCount] = useState(0)
const [stats, setStats] = useState<FormatStats>({
productionIdCount: 0,
orderNumberCount: 0,
unknownCount: 0
})
const recognizeType = (input: string): 'productionId' | 'orderNumber' | 'unknown' => {
const trimmed = input.trim()
if (!trimmed) return 'unknown'
if (ORDER_PATTERNS.ORDER_NUMBER.test(trimmed)) {
return 'orderNumber'
}
if (ORDER_PATTERNS.PRODUCTION_ID.test(trimmed)) {
return 'productionId'
}
return 'unknown'
}
useEffect(() => {
// Count non-empty lines
// Count non-empty lines and categorize by format
const lines = value.split('\n').filter((line) => line.trim().length > 0)
setCount(lines.length)
// Calculate format statistics
const newStats: FormatStats = {
productionIdCount: 0,
orderNumberCount: 0,
unknownCount: 0
}
for (const line of lines) {
const type = recognizeType(line)
if (type === 'productionId') {
newStats.productionIdCount++
} else if (type === 'orderNumber') {
newStats.orderNumberCount++
} else {
newStats.unknownCount++
}
}
setStats(newStats)
}, [value])
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
@@ -33,7 +87,18 @@ export const OrderNumberInput: React.FC<OrderNumberInputProps> = ({
<div className="order-number-input">
<div className="input-header">
<label>{label}</label>
<span className="count-badge">{count} </span>
<div className="stats-wrapper">
<span className="count-badge">{count} </span>
{enableFormatStats && stats.productionIdCount > 0 && (
<span className="stat-badge production-id">{stats.productionIdCount} </span>
)}
{enableFormatStats && stats.orderNumberCount > 0 && (
<span className="stat-badge order-number">{stats.orderNumberCount} </span>
)}
{enableFormatStats && stats.unknownCount > 0 && (
<span className="stat-badge unknown">{stats.unknownCount} </span>
)}
</div>
</div>
<textarea
value={value}
@@ -57,6 +122,11 @@ export const OrderNumberInput: React.FC<OrderNumberInputProps> = ({
font-size: 14px;
color: #333;
}
.stats-wrapper {
display: flex;
gap: 6px;
align-items: center;
}
.count-badge {
background: #e6f7ff;
color: #1890ff;
@@ -65,6 +135,25 @@ export const OrderNumberInput: React.FC<OrderNumberInputProps> = ({
font-size: 12px;
font-weight: 500;
}
.stat-badge {
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
}
.stat-badge.production-id {
background: #f6ffed;
color: #52c41a;
}
.stat-badge.order-number {
background: #fff7e6;
color: #fa8c16;
}
.stat-badge.unknown {
background: #fff1f0;
color: #ff4d4f;
}
.order-textarea {
width: 100%;
padding: 12px;