class CkgdDetail extends Component {
state = {
basicInfo: {},
goodsList: [],
attachments: []
};
componentDidMount() {
this.fetchDetailData();
}
fetchDetailData = async () => {
const [basicRes, goodsRes, attachRes] = await Promise.all([
this.getBasicInfo(),
this.getGoodsList(),
this.getAttachments()
]);
this.setState({
basicInfo: basicRes.result,
goodsList: goodsRes.result,
attachments: attachRes.result
});
};
getBasicInfo = () => {
return post(allUrl.ckfpCkgd.relDatails, {}, {
header: allUrl.header,
condition: { entryId: this.props.entryId }
});
};
getGoodsList = () => {
return post(allUrl.ckfpCkgd.goodsList, {}, {
header: allUrl.header,
condition: { entryId: this.props.entryId }
});
};
getAttachments = () => {
return post(allUrl.ckfpCkgd.attachmentList, {}, {
header: allUrl.header,
condition: { entryId: this.props.entryId }
});
};
render() {
const { basicInfo, goodsList, attachments } = this.state;
return (
<div className="ckgd-detail">
<BasicInfoPanel data={basicInfo} />
<GoodsListPanel data={goodsList} />
<AttachmentPanel data={attachments} />
</div>
);
}
}
class BasicInfoPanel extends Component {
render() {
const { data } = this.props;
return (
<Card title="基礎信息" className="basic-info-panel">
<Descriptions column={3} bordered>
<Descriptions.Item label="報關(guān)單號">{data.entryId}</Descriptions.Item>
<Descriptions.Item label="出口日期">{data.iEDate}</Descriptions.Item>
<Descriptions.Item label="申報狀態(tài)">{this.getStatusText(data.sbztDm)}</Descriptions.Item>
<Descriptions.Item label="經(jīng)營(yíng)單位">{data.tradeName}</Descriptions.Item>
<Descriptions.Item label="申報單位">{data.declareName}</Descriptions.Item>
<Descriptions.Item label="出口口岸">{data.customsName}</Descriptions.Item>
</Descriptions>
</Card>
);
}
getStatusText = (status) => {
const statusMap = {
'Y': '已申報',
'N': '未申報',
'B': '部分申報'
};
return statusMap[status] || status;
};
}
class GoodsListPanel extends Component {
state = {
selectedGoods: []
};
columns = [
{
title: '序號',
dataIndex: 'index',
key: 'index',
width: 60,
render: (text, record, index) => index + 1
},
{
title: '商品編碼',
dataIndex: 'codeTs',
key: 'codeTs',
width: 100
},
{
title: '商品名稱(chēng)',
dataIndex: 'gName',
key: 'gName',
width: 200
},
{
title: '規格型號',
dataIndex: 'gModel',
key: 'gModel',
width: 150
},
{
title: '數量',
dataIndex: 'qty',
key: 'qty',
width: 80
},
{
title: '單價(jià)',
dataIndex: 'price',
key: 'price',
width: 100,
render: (text) => `¥${text}`
},
{
title: '總價(jià)',
dataIndex: 'totalPrice',
key: 'totalPrice',
width: 100,
render: (text) => `¥${text}`
}
];
handleSelectionChange = (selectedRowKeys) => {
this.setState({ selectedGoods: selectedRowKeys });
};
render() {
const { data } = this.props;
const { selectedGoods } = this.state;
return (
<Card title="商品明細" className="goods-list-panel">
<DynamicTable
id="goodsTable"
myKey="id"
columns={this.columns}
dataSource={data}
showSelectionCheckbox={true}
selectedRowKeys={selectedGoods}
onTableSelect={this.handleSelectionChange}
/>
</Card>
);
}
}
class AttachmentPanel extends Component {
state = {
uploading: false
};
handleUpload = async (file) => {
this.setState({ uploading: true });
try {
const formData = new FormData();
formData.append('file', file);
formData.append('entryId', this.props.entryId);
const res = await postFormData(allUrl.ckfpCkgd.importFile, {}, formData);
if (res.msgKey) {
message.success('文件上傳成功');
this.props.onUploadSuccess();
} else {
message.error('文件上傳失敗');
}
} catch (error) {
message.error('上傳過(guò)程中發(fā)生錯誤');
} finally {
this.setState({ uploading: false });
}
};
handleDownload = (attachment) => {
const downloadUrl = `${allUrl.systemUrl}${attachment.filePath}`;
window.open(downloadUrl, '_blank');
};
render() {
const { data } = this.props;
const { uploading } = this.state;
return (
<Card title="附件管理" className="attachment-panel">
<Upload
accept=".pdf,.doc,.docx,.xls,.xlsx,.jpg,.png"
beforeUpload={this.handleUpload}
showUploadList={false}
disabled={uploading}
>
<Button type="primary" loading={uploading}>
<Icon type="upload" /> 上傳附件
</Button>
</Upload>
<List
dataSource={data}
renderItem={item => (
<List.Item
actions={[
<Button onClick={() => this.handleDownload(item)}>
下載
</Button>
]}
>
<List.Item.Meta
title={item.fileName}
description={`上傳時(shí)間: ${item.uploadTime}`}
/>
</List.Item>
)}
/>
</Card>
);
}
}
class AdvancedFilter extends Component {
state = {
filters: [],
fieldOptions: [
{ value: 'entryId', label: '報關(guān)單號' },
{ value: 'tradeName', label: '經(jīng)營(yíng)單位' },
{ value: 'iEDate', label: '出口日期' },
{ value: 'customsName', label: '出口口岸' }
],
operatorOptions: [
{ value: '=', label: '等于' },
{ value: '!=', label: '不等于' },
{ value: '>', label: '大于' },
{ value: '<', label: '小于' },
{ value: 'like', label: '包含' }
]
};
handleAddFilter = () => {
this.setState(prevState => ({
filters: [...prevState.filters, {
key: Date.now(),
field: '',
operator: '=',
value: ''
}]
}));
};
handleRemoveFilter = (key) => {
this.setState(prevState => ({
filters: prevState.filters.filter(filter => filter.key !== key)
}));
};
handleFilterChange = (key, field, value) => {
this.setState(prevState => ({
filters: prevState.filters.map(filter =>
filter.key === key ? { ...filter, [field]: value } : filter
)
}));
};
handleApplyFilters = () => {
const validFilters = this.state.filters.filter(filter =>
filter.field && filter.value
);
this.props.onFiltersChange(validFilters);
};
render() {
const { filters, fieldOptions, operatorOptions } = this.state;
return (
<div className="advanced-filter">
<div className="filter-header">
<span>高級篩選</span>
<Button size="small" onClick={this.handleAddFilter}>
添加條件
</Button>
</div>
<div className="filter-list">
{filters.map(filter => (
<FilterRow
key={filter.key}
filter={filter}
fieldOptions={fieldOptions}
operatorOptions={operatorOptions}
onChange={this.handleFilterChange}
onRemove={() => this.handleRemoveFilter(filter.key)}
/>
))}
</div>
<div className="filter-actions">
<Button type="primary" onClick={this.handleApplyFilters}>
應用篩選
</Button>
<Button onClick={this.handleClearFilters}>
清空
</Button>
</div>
</div>
);
}
}
class FilterRow extends Component {
handleFieldChange = (value) => {
this.props.onChange(this.props.filter.key, 'field', value);
};
handleOperatorChange = (value) => {
this.props.onChange(this.props.filter.key, 'operator', value);
};
handleValueChange = (e) => {
this.props.onChange(this.props.filter.key, 'value', e.target.value);
};
render() {
const { filter, fieldOptions, operatorOptions, onRemove } = this.props;
return (
<div className="filter-row">
<Select
value={filter.field}
onChange={this.handleFieldChange}
options={fieldOptions}
placeholder="選擇字段"
style={{ width: 120 }}
/>
<Select
value={filter.operator}
onChange={this.handleOperatorChange}
options={operatorOptions}
style={{ width: 100, margin: '0 8px' }}
/>
<Input
value={filter.value}
onChange={this.handleValueChange}
placeholder="輸入值"
style={{ width: 200 }}
/>
<Button
type="link"
danger
onClick={onRemove}
style={{ marginLeft: 8 }}
>
刪除
</Button>
</div>
);
}
}
class StatisticCard extends Component {
render() {
const { title, value, prefix, suffix, precision, trend } = this.props;
return (
<Card className="statistic-card">
<Statistic
title={title}
value={value}
precision={precision || 0}
prefix={prefix}
suffix={suffix}
valueStyle={{ color: this.getTrendColor(trend) }}
/>
{trend && (
<div className="trend-indicator">
<Icon type={trend > 0 ? 'arrow-up' : 'arrow-down'} />
<span>{Math.abs(trend)}%</span>
</div>
)}
</Card>
);
}
getTrendColor = (trend) => {
if (!trend) return '#000';
return trend > 0 ? '#f5222d' : '#52c41a';
};
}
class ChartDisplay extends Component {
state = {
chartData: [],
loading: true
};
componentDidMount() {
this.fetchChartData();
}
fetchChartData = async () => {
try {
const res = await post(allUrl.statistics.chartData, {}, {
header: allUrl.header,
condition: this.props.chartConfig
});
if (res.msgKey) {
this.setState({
chartData: res.result,
loading: false
});
}
} catch (error) {
this.setState({ loading: false });
message.error('獲取圖表數據失敗');
}
};
render() {
const { chartData, loading } = this.state;
const { type, title } = this.props;
return (
<Card title={title} loading={loading}>
{type === 'bar' && <BarChart data={chartData} />}
{type === 'line' && <LineChart data={chartData} />}
{type === 'pie' && <PieChart data={chartData} />}
</Card>
);
}
}
class BatchOperationPanel extends Component {
state = {
selectedCount: 0,
operationType: ''
};
componentDidUpdate(prevProps) {
if (prevProps.selectedItems !== this.props.selectedItems) {
this.setState({
selectedCount: this.props.selectedItems.length
});
}
}
handleOperation = async () => {
const { selectedItems, operationType } = this.state;
if (selectedItems.length === 0) {
message.warning('請選擇要操作的數據');
return;
}
try {
const res = await post(allUrl.batchOperation, {}, {
header: allUrl.header,
condition: {
operation: operationType,
items: selectedItems
}
});
if (res.msgKey) {
message.success('操作成功');
this.props.onOperationSuccess();
} else {
message.error(res.msgInfo);
}
} catch (error) {
message.error('操作失敗');
}
};
render() {
const { selectedCount, operationType } = this.state;
return (
<div className="batch-operation-panel">
<span>已選擇 {selectedCount} 項</span>
<Select
value={operationType}
onChange={value => this.setState({ operationType: value })}
placeholder="選擇操作"
style={{ width: 120, margin: '0 8px' }}
>
<Option value="export">導出</Option>
<Option value="delete">刪除</Option>
<Option value="approve">審批</Option>
</Select>
<Button
type="primary"
onClick={this.handleOperation}
disabled={!operationType}
>
執行操作
</Button>
</div>
);
}
}
class CkgdManagement extends Component {
state = {
data: [],
loading: false,
pagination: {
current: 1,
pageSize: 20,
total: 0
},
filters: {},
selectedRows: []
};
componentDidMount() {
this.loadData();
}
loadData = async (page = 1, filters = {}) => {
this.setState({ loading: true });
try {
const res = await post(allUrl.ckfpCkgd.list, {}, {
header: allUrl.header,
condition: {
page: page,
pageSize: this.state.pagination.pageSize,
queries: filters
}
});
if (res.msgKey) {
this.setState({
data: res.result,
pagination: {
...this.state.pagination,
current: page,
total: res.total
},
loading: false
});
}
} catch (error) {
this.setState({ loading: false });
message.error('加載數據失敗');
}
};
handleSearch = (filters) => {
this.setState({ filters }, () => {
this.loadData(1, filters);
});
};
handlePageChange = (page) => {
this.loadData(page, this.state.filters);
};
handleSelectionChange = (selectedRows) => {
this.setState({ selectedRows });
};
render() {
const { data, loading, pagination, selectedRows } = this.state;
return (
<div className="ckgd-management">
<AdvancedFilter onFiltersChange={this.handleSearch} />
<BatchOperationPanel
selectedItems={selectedRows}
onOperationSuccess={this.loadData}
/>
<DynamicTable
id="ckgdTable"
myKey="entryId"
columns={this.getColumns()}
dataSource={data}
loading={loading}
showSelectionCheckbox={true}
selectedRowKeys={selectedRows}
onTableSelect={this.handleSelectionChange}
/>
<DynamicPagination
totalItems={pagination.total}
currentPage={pagination.current}
initialPageSize={pagination.pageSize}
onPageChange={this.handlePageChange}
/>
</div>
);
}
getColumns = () => [
// 列配置...
];
}
這些業(yè)務(wù)組件展示了如何將基礎組件組合成完整的業(yè)務(wù)功能模塊,實(shí)現了高度的可復用性和維護性。