5
5
This commit is contained in:
742
src/App.tsx
742
src/App.tsx
@ -3,6 +3,7 @@ import { Button, Typography, List, Card, Space, Divider, Spin, Table, Select, Mo
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { addDays, format } from 'date-fns';
|
||||
import { zhCN } from 'date-fns/locale';
|
||||
import { executePricingQuery, executeSecondaryProcessQuery, executePricingDetailsQuery } from './services/apiService';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
@ -36,7 +37,12 @@ export default function App() {
|
||||
const [selectedRecords, setSelectedRecords] = useState<string[]>([]);
|
||||
const [recordDetails, setRecordDetails] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [queryResults, setQueryResults] = useState<any[]>([]);
|
||||
const [queryLoading, setQueryLoading] = useState(false);
|
||||
const [secondaryProcessResults, setSecondaryProcessResults] = useState<any[]>([]);
|
||||
const [secondaryProcessLoading, setSecondaryProcessLoading] = useState(false);
|
||||
const [pricingDetailsResults, setPricingDetailsResults] = useState<any[]>([]);
|
||||
const [pricingDetailsLoading, setPricingDetailsLoading] = useState(false);
|
||||
|
||||
// 标签相关状态
|
||||
const [labelOptions, setLabelOptions] = useState<{[key: string]: any[]}>({});
|
||||
@ -88,8 +94,6 @@ export default function App() {
|
||||
const [batchProgressList, setBatchProgressList] = useState<{ index: number; foreignId: string; status: 'success' | 'failed'; message?: string }[]>([]);
|
||||
const [batchCurrentRowInfo, setBatchCurrentRowInfo] = useState<{ index: number; foreignId: string; style: string; color: string } | null>(null);
|
||||
const batchAbortRef = useRef<boolean>(false);
|
||||
const [lastSavedDeliveryRecordId, setLastSavedDeliveryRecordId] = useState<string | null>(null);
|
||||
const [lastSavedDeliveryVersion, setLastSavedDeliveryVersion] = useState<number | null>(null);
|
||||
// 删除未使用的 deliveryRecords 状态
|
||||
const [selectedDeliveryRecordId, setSelectedDeliveryRecordId] = useState<string>('');
|
||||
// 从货期记录读取到的record_ids(用于保存回写)
|
||||
@ -103,6 +107,9 @@ export default function App() {
|
||||
const resetGlobalState = (opts?: { resetMode?: boolean }) => {
|
||||
// 运行时加载状态
|
||||
setLoading(false);
|
||||
setQueryLoading(false);
|
||||
setSecondaryProcessLoading(false);
|
||||
setPricingDetailsLoading(false);
|
||||
setLabelLoading(false);
|
||||
setAdjustLoading(false);
|
||||
setTimelineLoading(false);
|
||||
@ -216,6 +223,26 @@ export default function App() {
|
||||
// 新表ID(批量生成表)
|
||||
const BATCH_TABLE_ID = 'tblXO7iSxBYxrqtY';
|
||||
|
||||
const fetchAllRecordsByPage = async (table: any, params?: any) => {
|
||||
let token: any = undefined;
|
||||
let all: any[] = [];
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
const res: any = await table.getRecordsByPage({ pageSize: Math.min(200, (params && params.pageSize) ? params.pageSize : 200), pageToken: token, ...(params || {}) });
|
||||
const recs: any[] = Array.isArray(res?.records) ? res.records : [];
|
||||
all = all.concat(recs);
|
||||
const nextToken = res?.pageToken;
|
||||
const hm = !!res?.hasMore;
|
||||
token = nextToken;
|
||||
if (!hm && !nextToken) break;
|
||||
}
|
||||
return all;
|
||||
};
|
||||
|
||||
const getRecordTotalByPage = async (table: any, params?: any) => {
|
||||
const res: any = await table.getRecordIdListByPage({ pageSize: 1, ...(params || {}) });
|
||||
return res?.total || 0;
|
||||
};
|
||||
|
||||
// 已移除:调整模式不再加载货期记录列表
|
||||
|
||||
// 入口选择处理
|
||||
@ -229,8 +256,7 @@ export default function App() {
|
||||
const openBatchModal = async () => {
|
||||
try {
|
||||
const batchTable = await bitable.base.getTable(BATCH_TABLE_ID);
|
||||
const res = await batchTable.getRecords({ pageSize: 5000 });
|
||||
const total = res.records?.length || 0;
|
||||
const total = await getRecordTotalByPage(batchTable);
|
||||
setBatchTotalRows(total);
|
||||
setBatchStartRow(1);
|
||||
setBatchEndRow(total > 0 ? total : 1);
|
||||
@ -258,11 +284,15 @@ export default function App() {
|
||||
setTimelineLoading(true);
|
||||
try {
|
||||
const deliveryTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID);
|
||||
const deliveryRecord = await deliveryTable.getRecordById(deliveryRecordId);
|
||||
const nodeDetailsVal = deliveryRecord?.fields?.[DELIVERY_NODE_DETAILS_FIELD_ID];
|
||||
const nodeDetailsField: any = await deliveryTable.getField(DELIVERY_NODE_DETAILS_FIELD_ID);
|
||||
const recordIdsField: any = await deliveryTable.getField(DELIVERY_RECORD_IDS_FIELD_ID);
|
||||
const snapshotField: any = await deliveryTable.getField(DELIVERY_SNAPSHOT_JSON_FIELD_ID);
|
||||
const startTimeField: any = await deliveryTable.getField(DELIVERY_START_TIME_FIELD_ID);
|
||||
|
||||
const nodeDetailsVal = await nodeDetailsField.getValue(deliveryRecordId);
|
||||
// 读取record_ids文本字段并保留原始文本(用于原样写回)
|
||||
try {
|
||||
const recordIdsTextVal = deliveryRecord?.fields?.[DELIVERY_RECORD_IDS_FIELD_ID];
|
||||
const recordIdsTextVal = await recordIdsField.getValue(deliveryRecordId);
|
||||
const raw = extractText(recordIdsTextVal);
|
||||
if (raw && raw.trim() !== '') {
|
||||
setRestoredRecordIdsText(raw.trim());
|
||||
@ -284,7 +314,7 @@ export default function App() {
|
||||
|
||||
// 优先使用货期记录表中的快照字段进行一键还原(新方案)
|
||||
try {
|
||||
const deliverySnapVal = deliveryRecord?.fields?.[DELIVERY_SNAPSHOT_JSON_FIELD_ID];
|
||||
const deliverySnapVal = await snapshotField.getValue(deliveryRecordId);
|
||||
let deliverySnapStr: string | null = null;
|
||||
if (typeof deliverySnapVal === 'string') {
|
||||
deliverySnapStr = deliverySnapVal;
|
||||
@ -357,7 +387,7 @@ export default function App() {
|
||||
}
|
||||
|
||||
if (!startTimeRestored) {
|
||||
const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID];
|
||||
const startTimeValue = await startTimeField.getValue(deliveryRecordId);
|
||||
if (startTimeValue) {
|
||||
let extractedStartTime: Date | null = null;
|
||||
if (typeof startTimeValue === 'number') {
|
||||
@ -427,6 +457,120 @@ export default function App() {
|
||||
}
|
||||
|
||||
const processTable = await bitable.base.getTable(PROCESS_DATA_TABLE_ID);
|
||||
try {
|
||||
const processSnapshotField: any = await processTable.getField(PROCESS_SNAPSHOT_JSON_FIELD_ID);
|
||||
let snapStr: string | null = null;
|
||||
for (const id of recordIds) {
|
||||
const snapVal = await processSnapshotField.getValue(id);
|
||||
const candidate = extractText(snapVal);
|
||||
if (candidate && candidate.trim() !== '') {
|
||||
snapStr = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (snapStr && snapStr.trim() !== '') {
|
||||
const snapshot = JSON.parse(snapStr);
|
||||
if (Array.isArray(snapshot.timelineResults)) {
|
||||
setIsRestoringSnapshot(true);
|
||||
|
||||
if (snapshot.selectedLabels) setSelectedLabels(snapshot.selectedLabels);
|
||||
if (!mode && snapshot.mode) setMode(snapshot.mode);
|
||||
if (snapshot.foreignId) setCurrentForeignId(snapshot.foreignId);
|
||||
if (snapshot.styleText) setCurrentStyleText(snapshot.styleText);
|
||||
if (snapshot.colorText) setCurrentColorText(snapshot.colorText);
|
||||
if (snapshot.text2) setCurrentText2(snapshot.text2);
|
||||
|
||||
if (snapshot.generationModeState) {
|
||||
const genState = snapshot.generationModeState;
|
||||
if (genState.currentForeignId) setCurrentForeignId(genState.currentForeignId);
|
||||
if (genState.currentStyleText) setCurrentStyleText(genState.currentStyleText);
|
||||
if (genState.currentColorText) setCurrentColorText(genState.currentColorText);
|
||||
if (genState.currentText2) setCurrentText2(genState.currentText2);
|
||||
if (genState.currentVersionNumber !== undefined) setCurrentVersionNumber(genState.currentVersionNumber);
|
||||
if (genState.recordDetails && Array.isArray(genState.recordDetails)) {
|
||||
setRecordDetails(genState.recordDetails);
|
||||
}
|
||||
}
|
||||
|
||||
if (snapshot.version !== undefined) {
|
||||
let vNum: number | null = null;
|
||||
if (typeof snapshot.version === 'number') {
|
||||
vNum = snapshot.version;
|
||||
} else if (typeof snapshot.version === 'string') {
|
||||
const match = snapshot.version.match(/\d+/);
|
||||
if (match) {
|
||||
vNum = parseInt(match[0], 10);
|
||||
}
|
||||
}
|
||||
if (vNum !== null && !isNaN(vNum)) setCurrentVersionNumber(vNum);
|
||||
}
|
||||
|
||||
if (snapshot.timelineAdjustments) setTimelineAdjustments(snapshot.timelineAdjustments);
|
||||
if (snapshot.expectedDateTimestamp) {
|
||||
setExpectedDate(new Date(snapshot.expectedDateTimestamp));
|
||||
} else if (snapshot.expectedDateString) {
|
||||
setExpectedDate(new Date(snapshot.expectedDateString));
|
||||
}
|
||||
|
||||
let startTimeRestored = false;
|
||||
if (snapshot.startTimestamp) {
|
||||
setStartTime(new Date(snapshot.startTimestamp));
|
||||
startTimeRestored = true;
|
||||
} else if (snapshot.startString) {
|
||||
const parsed = new Date(snapshot.startString);
|
||||
if (!isNaN(parsed.getTime())) {
|
||||
setStartTime(parsed);
|
||||
startTimeRestored = true;
|
||||
}
|
||||
}
|
||||
if (!startTimeRestored) {
|
||||
const startTimeValue = await startTimeField.getValue(deliveryRecordId);
|
||||
if (startTimeValue) {
|
||||
let extractedStartTime: Date | null = null;
|
||||
if (typeof startTimeValue === 'number') {
|
||||
extractedStartTime = new Date(startTimeValue);
|
||||
} else if (Array.isArray(startTimeValue) && startTimeValue.length > 0) {
|
||||
const timestamp = startTimeValue[0];
|
||||
if (typeof timestamp === 'number') {
|
||||
extractedStartTime = new Date(timestamp);
|
||||
}
|
||||
}
|
||||
if (extractedStartTime && !isNaN(extractedStartTime.getTime())) {
|
||||
setStartTime(extractedStartTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setTimelineResults(snapshot.timelineResults);
|
||||
Modal.confirm({
|
||||
title: '是否调整标签?',
|
||||
content: '选择“是”将允许修改标签并重新生成计划(版本按V2/V3/V4递增)',
|
||||
okText: '是,调整标签',
|
||||
cancelText: '否,直接还原',
|
||||
onOk: async () => {
|
||||
setLabelAdjustmentFlow(true);
|
||||
setTimelineVisible(false);
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({ toastType: 'info', message: '请在下方修改标签后点击重新生成计划' });
|
||||
}
|
||||
},
|
||||
onCancel: async () => {
|
||||
setLabelAdjustmentFlow(false);
|
||||
setTimelineVisible(true);
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({ toastType: 'success', message: '已按快照一模一样还原流程数据' });
|
||||
}
|
||||
}
|
||||
});
|
||||
setTimelineLoading(false);
|
||||
setIsRestoringSnapshot(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('从节点快照快速还原失败,继续旧流程:', e);
|
||||
}
|
||||
|
||||
const records = await Promise.all(recordIds.map(id => processTable.getRecordById(id)));
|
||||
|
||||
// 优先使用文本2快照一模一样还原
|
||||
@ -523,7 +667,7 @@ export default function App() {
|
||||
|
||||
// 如果快照中没有起始时间信息,则从当前选中的货期记录中获取
|
||||
if (!startTimeRestored) {
|
||||
const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID];
|
||||
const startTimeValue = await startTimeField.getValue(deliveryRecordId);
|
||||
if (startTimeValue) {
|
||||
let extractedStartTime: Date | null = null;
|
||||
if (typeof startTimeValue === 'number') {
|
||||
@ -897,7 +1041,7 @@ export default function App() {
|
||||
});
|
||||
|
||||
// 如果没有快照恢复起始时间,则从当前货期记录中获取起始时间
|
||||
const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID];
|
||||
const startTimeValue = await startTimeField.getValue(deliveryRecordId);
|
||||
if (startTimeValue) {
|
||||
let extractedStartTime: Date | null = null;
|
||||
if (typeof startTimeValue === 'number') {
|
||||
@ -963,6 +1107,7 @@ export default function App() {
|
||||
const DELIVERY_TEXT2_FIELD_ID = 'fldG6LZnmU';
|
||||
// 记录ID文本字段(货期记录表新增)
|
||||
const DELIVERY_RECORD_IDS_FIELD_ID = 'fldq3u7h7H';
|
||||
const DELIVERY_FACTORY_DEPARTURE_DATE_FIELD_ID = 'fldZFdZDKj';
|
||||
|
||||
// OMS看板表相关常量(新增)
|
||||
const OMS_BOARD_TABLE_ID = 'tbl7j8bCpUbFmGuk'; // OMS看板表ID
|
||||
@ -1516,8 +1661,8 @@ export default function App() {
|
||||
if (recordId && tableId === DELIVERY_RECORD_TABLE_ID) {
|
||||
// 如果选中的是货期记录表的记录,从中获取起始时间
|
||||
const deliveryTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID);
|
||||
const deliveryRecord = await deliveryTable.getRecordById(recordId);
|
||||
const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID];
|
||||
const startTimeField: any = await deliveryTable.getField(DELIVERY_START_TIME_FIELD_ID);
|
||||
const startTimeValue = await startTimeField.getValue(recordId);
|
||||
|
||||
if (startTimeValue) {
|
||||
let extractedStartTime: Date | null = null;
|
||||
@ -1539,22 +1684,23 @@ export default function App() {
|
||||
// 从OMS看板匹配对应的货期记录后,尝试获取其起始时间
|
||||
try {
|
||||
const omsTable = await bitable.base.getTable(OMS_BOARD_TABLE_ID);
|
||||
const omsRecord = await omsTable.getRecordById(recordId);
|
||||
const planTextRaw = omsRecord?.fields?.[OMS_PLAN_TEXT_FIELD_ID];
|
||||
const planVersionRaw = omsRecord?.fields?.[OMS_PLAN_VERSION_FIELD_ID];
|
||||
const planTextField: any = await omsTable.getField(OMS_PLAN_TEXT_FIELD_ID);
|
||||
const planVersionField: any = await omsTable.getField(OMS_PLAN_VERSION_FIELD_ID);
|
||||
const planTextRaw = await planTextField.getValue(recordId);
|
||||
const planVersionRaw = await planVersionField.getValue(recordId);
|
||||
const planText = extractText(planTextRaw)?.trim();
|
||||
let planVersion: number | null = null;
|
||||
if (typeof planVersionRaw === 'number') {
|
||||
planVersion = planVersionRaw;
|
||||
} else if (typeof planVersionRaw === 'string') {
|
||||
const m = planVersionRaw.match(/\d+/);
|
||||
if (m) planVersion = parseInt(m[0], 10);
|
||||
const m = planVersionRaw.match(/\d+(?:\.\d+)?/);
|
||||
if (m) planVersion = parseFloat(m[0]);
|
||||
} else if (planVersionRaw && typeof planVersionRaw === 'object') {
|
||||
const v = (planVersionRaw as any).value ?? (planVersionRaw as any).text;
|
||||
if (typeof v === 'number') planVersion = v;
|
||||
else if (typeof v === 'string') {
|
||||
const m = v.match(/\d+/);
|
||||
if (m) planVersion = parseInt(m[0], 10);
|
||||
const m = v.match(/\d+(?:\.\d+)?/);
|
||||
if (m) planVersion = parseFloat(m[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1562,8 +1708,8 @@ export default function App() {
|
||||
const deliveryRecordId = await findDeliveryRecordIdByPlan(planText, planVersion);
|
||||
if (deliveryRecordId) {
|
||||
const deliveryTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID);
|
||||
const deliveryRecord = await deliveryTable.getRecordById(deliveryRecordId);
|
||||
const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID];
|
||||
const startTimeField: any = await deliveryTable.getField(DELIVERY_START_TIME_FIELD_ID);
|
||||
const startTimeValue = await startTimeField.getValue(deliveryRecordId);
|
||||
if (startTimeValue) {
|
||||
let extractedStartTime: Date | null = null;
|
||||
if (typeof startTimeValue === 'number') {
|
||||
@ -1597,30 +1743,47 @@ export default function App() {
|
||||
const findDeliveryRecordIdByPlan = async (planText: string, planVersion: number): Promise<string | null> => {
|
||||
try {
|
||||
const deliveryTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID);
|
||||
// 拉取一定数量的记录进行匹配(如需可优化为分页/索引)
|
||||
const recordsResult = await deliveryTable.getRecords({ pageSize: 5000 });
|
||||
const records = recordsResult.records || [];
|
||||
for (const rec of records) {
|
||||
const fields = rec?.fields || {};
|
||||
const recordIdsTextVal = fields[DELIVERY_RECORD_IDS_FIELD_ID];
|
||||
const versionVal = fields[DELIVERY_VERSION_FIELD_ID];
|
||||
const recordIdsText = extractText(recordIdsTextVal)?.trim();
|
||||
let versionNum: number | null = null;
|
||||
if (typeof versionVal === 'number') versionNum = versionVal;
|
||||
else if (typeof versionVal === 'string') {
|
||||
const m = versionVal.match(/\d+/);
|
||||
if (m) versionNum = parseInt(m[0], 10);
|
||||
} else if (versionVal && typeof versionVal === 'object') {
|
||||
const v = (versionVal as any).value ?? (versionVal as any).text;
|
||||
if (typeof v === 'number') versionNum = v;
|
||||
else if (typeof v === 'string') {
|
||||
const m = v.match(/\d+/);
|
||||
if (m) versionNum = parseInt(m[0], 10);
|
||||
const versionField: any = await deliveryTable.getField(DELIVERY_VERSION_FIELD_ID);
|
||||
const planFilter = {
|
||||
conjunction: 'and',
|
||||
conditions: [{ fieldId: DELIVERY_RECORD_IDS_FIELD_ID, operator: 'is', value: planText }]
|
||||
};
|
||||
const sort = DELIVERY_CREATE_TIME_FIELD_ID
|
||||
? [{ fieldId: DELIVERY_CREATE_TIME_FIELD_ID, desc: true }]
|
||||
: undefined;
|
||||
|
||||
let token: any = undefined;
|
||||
const eps = 1e-9;
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
const res: any = await versionField.getFieldValueListByPage({
|
||||
pageSize: 200,
|
||||
pageToken: token,
|
||||
filter: planFilter,
|
||||
sort,
|
||||
stringValue: true
|
||||
});
|
||||
const fieldValues: any[] = Array.isArray(res?.fieldValues) ? res.fieldValues : [];
|
||||
|
||||
for (const fv of fieldValues) {
|
||||
const recordId = fv?.recordId;
|
||||
const raw = fv?.value;
|
||||
let v: number | null = null;
|
||||
if (typeof raw === 'number') {
|
||||
v = raw;
|
||||
} else {
|
||||
const s = typeof raw === 'string' ? raw : extractText(raw);
|
||||
const m = (s || '').match(/\d+(?:\.\d+)?/);
|
||||
if (m) v = parseFloat(m[0]);
|
||||
}
|
||||
if (v !== null && Math.abs(v - planVersion) < eps) {
|
||||
return recordId || null;
|
||||
}
|
||||
}
|
||||
if (recordIdsText && versionNum !== null && recordIdsText === planText && versionNum === planVersion) {
|
||||
return rec.id || rec.recordId || null;
|
||||
}
|
||||
|
||||
const nextToken = res?.pageToken;
|
||||
const hasMore = !!res?.hasMore;
|
||||
token = nextToken;
|
||||
if (!hasMore && !nextToken) break;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
@ -1779,11 +1942,7 @@ export default function App() {
|
||||
|
||||
// 1. 先获取匹配的流程节点(复用预览功能的逻辑)
|
||||
const processTable = await bitable.base.getTable(PROCESS_CONFIG_TABLE_ID);
|
||||
const processRecordsResult = await processTable.getRecords({
|
||||
pageSize: 5000
|
||||
});
|
||||
|
||||
const processRecords = processRecordsResult.records || [];
|
||||
const processRecords = await fetchAllRecordsByPage(processTable);
|
||||
const matchedProcessNodes: any[] = [];
|
||||
|
||||
// 匹配流程配置节点
|
||||
@ -1965,11 +2124,7 @@ export default function App() {
|
||||
console.log('按顺序排列的流程节点:', matchedProcessNodes);
|
||||
|
||||
// 2. 优化:预先获取所有时效数据并建立索引
|
||||
const timelineRecordsResult = await timelineTable.getRecords({
|
||||
pageSize: 5000
|
||||
});
|
||||
|
||||
const timelineRecords = timelineRecordsResult.records || [];
|
||||
const timelineRecords = await fetchAllRecordsByPage(timelineTable);
|
||||
|
||||
// 优化2:预处理时效数据,建立节点名称到记录的映射
|
||||
const timelineIndexByNode = new Map<string, any[]>();
|
||||
@ -3008,7 +3163,8 @@ export default function App() {
|
||||
DELIVERY_CUSTOMER_EXPECTED_DATE_FIELD_ID,
|
||||
DELIVERY_NODE_DETAILS_FIELD_ID,
|
||||
DELIVERY_ADJUSTMENT_INFO_FIELD_ID, // 添加货期调整信息字段
|
||||
DELIVERY_START_TIME_FIELD_ID // 新增:起始时间字段
|
||||
DELIVERY_START_TIME_FIELD_ID, // 新增:起始时间字段
|
||||
DELIVERY_FACTORY_DEPARTURE_DATE_FIELD_ID
|
||||
];
|
||||
console.log('需要检查的字段ID列表:', fieldsToCheck);
|
||||
|
||||
@ -3028,6 +3184,7 @@ export default function App() {
|
||||
const startTimeField = await deliveryRecordTable.getField(DELIVERY_START_TIME_FIELD_ID);
|
||||
const snapshotField = await deliveryRecordTable.getField(DELIVERY_SNAPSHOT_JSON_FIELD_ID);
|
||||
const recordIdsTextField = await deliveryRecordTable.getField(DELIVERY_RECORD_IDS_FIELD_ID);
|
||||
const factoryDepartureDateField = await deliveryRecordTable.getField(DELIVERY_FACTORY_DEPARTURE_DATE_FIELD_ID);
|
||||
console.log('成功获取所有字段对象');
|
||||
|
||||
// 检查标签汇总字段的类型
|
||||
@ -3160,32 +3317,27 @@ export default function App() {
|
||||
customerExpectedDate = expectedDateToUse.getTime();
|
||||
}
|
||||
|
||||
let factoryDepartureDate = null as number | null;
|
||||
const reservationInbound = timelineResults.find(r => r?.nodeName === '预约入库');
|
||||
const reservationEnd = reservationInbound?.estimatedEnd;
|
||||
if (reservationEnd instanceof Date && !isNaN(reservationEnd.getTime())) {
|
||||
factoryDepartureDate = reservationEnd.getTime();
|
||||
} else if (typeof reservationEnd === 'number' && Number.isFinite(reservationEnd)) {
|
||||
factoryDepartureDate = reservationEnd;
|
||||
} else if (typeof reservationEnd === 'string' && reservationEnd.trim() !== '') {
|
||||
const parsed = parseDate(reservationEnd);
|
||||
if (parsed) {
|
||||
factoryDepartureDate = parsed.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
// 创建当前时间戳
|
||||
const currentTime = new Date().getTime();
|
||||
|
||||
// 计算版本号(数字)并格式化货期调整信息
|
||||
let versionNumber = 1;
|
||||
try {
|
||||
if (mode === 'adjust' && currentVersionNumber !== null) {
|
||||
// 调整模式:优先使用快照version +1
|
||||
versionNumber = currentVersionNumber + 1;
|
||||
} else if (foreignId) {
|
||||
const existing = await deliveryRecordTable.getRecords({
|
||||
pageSize: 5000,
|
||||
filter: {
|
||||
conjunction: 'and',
|
||||
conditions: [{
|
||||
fieldId: DELIVERY_FOREIGN_ID_FIELD_ID,
|
||||
operator: 'is',
|
||||
value: [foreignId]
|
||||
}]
|
||||
}
|
||||
});
|
||||
const count = existing.records?.length || 0;
|
||||
versionNumber = count + 1;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('计算版本号失败:', e);
|
||||
if (mode === 'adjust' && currentVersionNumber !== null) {
|
||||
versionNumber = currentVersionNumber + 1;
|
||||
}
|
||||
|
||||
let adjustmentInfo = `版本:V${versionNumber}`;
|
||||
@ -3374,6 +3526,7 @@ export default function App() {
|
||||
const snapshotCell = await snapshotField.createCell(snapshotJson);
|
||||
const expectedDateCell = expectedDeliveryDate ? await expectedDateField.createCell(expectedDeliveryDate) : null;
|
||||
const customerExpectedDateCell = customerExpectedDate ? await customerExpectedDateField.createCell(customerExpectedDate) : null;
|
||||
const factoryDepartureDateCell = factoryDepartureDate ? await factoryDepartureDateField.createCell(factoryDepartureDate) : null;
|
||||
// 对于关联记录字段,确保传入的是记录ID数组
|
||||
const nodeDetailsCell = processRecordIds.length > 0 ?
|
||||
await nodeDetailsField.createCell({ recordIds: processRecordIds }) : null;
|
||||
@ -3394,21 +3547,12 @@ export default function App() {
|
||||
if (labelsCell) recordCells.push(labelsCell);
|
||||
if (expectedDateCell) recordCells.push(expectedDateCell);
|
||||
if (customerExpectedDateCell) recordCells.push(customerExpectedDateCell);
|
||||
if (factoryDepartureDateCell) recordCells.push(factoryDepartureDateCell);
|
||||
if (nodeDetailsCell) recordCells.push(nodeDetailsCell);
|
||||
if (adjustmentInfoCell) recordCells.push(adjustmentInfoCell);
|
||||
|
||||
// 添加记录到货期记录表
|
||||
const addedRecord = await deliveryRecordTable.addRecord(recordCells);
|
||||
// 保存最近一次写入的记录ID与版本号
|
||||
try {
|
||||
const savedId = typeof addedRecord === 'string'
|
||||
? addedRecord
|
||||
: (((addedRecord as any)?.id || (addedRecord as any)?.recordId) as string);
|
||||
if (savedId) {
|
||||
setLastSavedDeliveryRecordId(savedId);
|
||||
setLastSavedDeliveryVersion(versionNumber);
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return addedRecord;
|
||||
} catch (error) {
|
||||
@ -3533,70 +3677,13 @@ export default function App() {
|
||||
}
|
||||
}
|
||||
|
||||
// 先删除该foreign_id的所有现有记录
|
||||
console.log('=== 开始删除现有记录 ===');
|
||||
console.log('使用的foreign_id:', foreignId);
|
||||
try {
|
||||
console.log('正在查询现有记录...');
|
||||
const existingRecords = await processDataTable.getRecords({
|
||||
pageSize: 5000,
|
||||
filter: {
|
||||
conjunction: 'And',
|
||||
conditions: [{
|
||||
fieldId: FOREIGN_ID_FIELD_ID,
|
||||
operator: 'is',
|
||||
value: [foreignId]
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
console.log('查询到现有记录数量:', existingRecords.records?.length || 0);
|
||||
|
||||
if (existingRecords.records && existingRecords.records.length > 0) {
|
||||
const recordIdsToDelete = existingRecords.records.map(record => record.id);
|
||||
console.log('准备删除的记录ID:', recordIdsToDelete);
|
||||
await processDataTable.deleteRecords(recordIdsToDelete);
|
||||
console.log(`成功删除 ${recordIdsToDelete.length} 条现有记录`);
|
||||
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'info',
|
||||
message: `已删除 ${recordIdsToDelete.length} 条现有流程数据`
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log('没有找到需要删除的现有记录');
|
||||
}
|
||||
} catch (deleteError) {
|
||||
console.error('删除现有记录时出错:', deleteError);
|
||||
// 继续执行,不中断流程
|
||||
}
|
||||
|
||||
// 构建页面快照JSON(确保可一模一样还原)
|
||||
// 计算版本号:数字。默认按 foreign_id 在货期记录表的记录数量 + 1
|
||||
// 计算版本号:仅在调整模式下递增
|
||||
let versionNumber = 1;
|
||||
try {
|
||||
const deliveryRecordTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID);
|
||||
if (mode === 'adjust' && currentVersionNumber !== null) {
|
||||
// 调整模式:优先使用快照version +1,避免公式字段读取错误
|
||||
versionNumber = currentVersionNumber + 1;
|
||||
} else if (foreignId) {
|
||||
const existing = await deliveryRecordTable.getRecords({
|
||||
pageSize: 5000,
|
||||
filter: {
|
||||
conjunction: 'and',
|
||||
conditions: [{
|
||||
fieldId: DELIVERY_FOREIGN_ID_FIELD_ID,
|
||||
operator: 'is',
|
||||
value: [foreignId]
|
||||
}]
|
||||
}
|
||||
});
|
||||
const count = existing.records?.length || 0;
|
||||
versionNumber = count + 1;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('计算版本号失败:', e);
|
||||
if (mode === 'adjust' && currentVersionNumber !== null) {
|
||||
versionNumber = currentVersionNumber + 1;
|
||||
}
|
||||
|
||||
|
||||
@ -3607,6 +3694,12 @@ export default function App() {
|
||||
const result = timelineResults[index];
|
||||
console.log('处理节点数据:', result);
|
||||
|
||||
const hasValidTimelineValue = typeof result.timelineValue === 'number' && Number.isFinite(result.timelineValue) && result.timelineValue > 0;
|
||||
if (!hasValidTimelineValue) {
|
||||
console.log(`跳过节点 "${result.nodeName}" - 未匹配到时效值`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否有有效的预计完成时间(只检查结束时间)
|
||||
const hasValidEndTime = (
|
||||
result.estimatedEnd &&
|
||||
@ -3653,22 +3746,20 @@ export default function App() {
|
||||
|
||||
// 组合所有Cell到一个记录中
|
||||
const versionCell = await versionField.createCell(versionNumber);
|
||||
const timelinessCell = (typeof result.timelineValue === 'number')
|
||||
? await timelinessField.createCell(result.timelineValue)
|
||||
: null;
|
||||
const timelinessCell = await timelinessField.createCell(result.timelineValue);
|
||||
const recordCells = [
|
||||
foreignIdCell,
|
||||
processNameCell,
|
||||
processOrderCell,
|
||||
styleCell,
|
||||
colorCell,
|
||||
versionCell
|
||||
versionCell,
|
||||
timelinessCell
|
||||
];
|
||||
|
||||
// 只有当时间戳存在时才添加日期Cell
|
||||
if (startDateCell) recordCells.push(startDateCell);
|
||||
if (endDateCell) recordCells.push(endDateCell);
|
||||
if (timelinessCell) recordCells.push(timelinessCell);
|
||||
|
||||
console.log(`准备写入的Cell记录 - ${result.nodeName}:`, recordCells);
|
||||
recordCellsToAdd.push(recordCells);
|
||||
@ -3712,7 +3803,7 @@ export default function App() {
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'warning',
|
||||
message: '没有有效的流程数据可以写入 - 所有节点都缺少时效数据'
|
||||
message: '没有有效的流程数据可以写入 - 未匹配到时效值'
|
||||
});
|
||||
}
|
||||
return [];
|
||||
@ -3782,9 +3873,15 @@ export default function App() {
|
||||
const batchTable = await bitable.base.getTable(BATCH_TABLE_ID);
|
||||
const fieldMetaList = await batchTable.getFieldMetaList();
|
||||
const nameToId = new Map<string, string>();
|
||||
for (const f of fieldMetaList) nameToId.set(f.name, f.id);
|
||||
const res = await batchTable.getRecords({ pageSize: 5000 });
|
||||
const rows = res.records || [];
|
||||
for (const meta of (fieldMetaList || [])) {
|
||||
if (!meta) continue;
|
||||
const nm = (meta as any).name;
|
||||
const id = (meta as any).id;
|
||||
if (typeof nm === 'string' && typeof id === 'string' && nm.trim() && id.trim()) {
|
||||
nameToId.set(nm, id);
|
||||
}
|
||||
}
|
||||
const rows = await fetchAllRecordsByPage(batchTable);
|
||||
const total = rows.length;
|
||||
const startIndex1 = range?.start && range.start > 0 ? range.start : 1;
|
||||
const endIndex1 = range?.end && range.end > 0 ? range.end : total;
|
||||
@ -3933,7 +4030,169 @@ export default function App() {
|
||||
|
||||
|
||||
|
||||
// 执行定价数据查询
|
||||
const executeQuery = async (packId: string, packType: string) => {
|
||||
if (queryLoading) return;
|
||||
setQueryLoading(true);
|
||||
try {
|
||||
// 使用 apiService 中的函数
|
||||
const data = await executePricingQuery(packId, packType, selectedLabels);
|
||||
setQueryResults(data);
|
||||
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'success',
|
||||
message: '查询成功'
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('数据库查询出错:', error);
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'error',
|
||||
message: `数据库查询失败: ${error.message}`
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setQueryLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 执行二次工艺查询
|
||||
const executeSecondaryProcessQueryLocal = async (packId: string, packType: string) => {
|
||||
if (secondaryProcessLoading) return;
|
||||
setSecondaryProcessLoading(true);
|
||||
try {
|
||||
const data = await executeSecondaryProcessQuery(packId, packType);
|
||||
setSecondaryProcessResults(data);
|
||||
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'success',
|
||||
message: '二次工艺查询成功'
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('二次工艺查询出错:', error);
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'error',
|
||||
message: `二次工艺查询失败: ${error.message}`
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setSecondaryProcessLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 执行定价详情查询
|
||||
const executePricingDetailsQueryLocal = async (packId: string) => {
|
||||
if (pricingDetailsLoading) return;
|
||||
setPricingDetailsLoading(true);
|
||||
try {
|
||||
const data = await executePricingDetailsQuery(packId);
|
||||
setPricingDetailsResults(data);
|
||||
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'success',
|
||||
message: '定价详情查询成功'
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('定价详情查询出错:', error);
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'error',
|
||||
message: `定价详情查询失败: ${error.message}`
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setPricingDetailsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理数据库查询
|
||||
const handleQueryDatabase = async (record: any) => {
|
||||
// 从记录字段中提取 packId 和 packType
|
||||
let packId = '';
|
||||
let packType = '';
|
||||
|
||||
// 提取 pack_id (fldpvBfeC0)
|
||||
if (record.fields.fldpvBfeC0 && Array.isArray(record.fields.fldpvBfeC0) && record.fields.fldpvBfeC0.length > 0) {
|
||||
packId = record.fields.fldpvBfeC0[0].text;
|
||||
}
|
||||
|
||||
// 提取 pack_type (fldSAF9qXe)
|
||||
if (record.fields.fldSAF9qXe && record.fields.fldSAF9qXe.text) {
|
||||
packType = record.fields.fldSAF9qXe.text;
|
||||
}
|
||||
|
||||
if (!packId || !packType) {
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'error',
|
||||
message: '缺少必要的查询参数 (pack_id 或 pack_type)'
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await executeQuery(packId, packType);
|
||||
};
|
||||
|
||||
// 处理二次工艺查询
|
||||
const handleSecondaryProcessQuery = async (record: any) => {
|
||||
// 从记录字段中提取 packId 和 packType
|
||||
let packId = '';
|
||||
let packType = '';
|
||||
|
||||
// 提取 pack_id (fldpvBfeC0)
|
||||
if (record.fields.fldpvBfeC0 && Array.isArray(record.fields.fldpvBfeC0) && record.fields.fldpvBfeC0.length > 0) {
|
||||
packId = record.fields.fldpvBfeC0[0].text;
|
||||
}
|
||||
|
||||
// 提取 pack_type (fldSAF9qXe)
|
||||
if (record.fields.fldSAF9qXe && record.fields.fldSAF9qXe.text) {
|
||||
packType = record.fields.fldSAF9qXe.text;
|
||||
}
|
||||
|
||||
if (!packId || !packType) {
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'error',
|
||||
message: '缺少必要的查询参数 (pack_id 或 pack_type)'
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await executeSecondaryProcessQueryLocal(packId, packType);
|
||||
};
|
||||
|
||||
// 处理定价详情查询
|
||||
const handlePricingDetailsQuery = async (record: any) => {
|
||||
// 从记录字段中提取 packId
|
||||
let packId = '';
|
||||
|
||||
// 提取 pack_id (fldpvBfeC0)
|
||||
if (record.fields.fldpvBfeC0 && Array.isArray(record.fields.fldpvBfeC0) && record.fields.fldpvBfeC0.length > 0) {
|
||||
packId = record.fields.fldpvBfeC0[0].text;
|
||||
}
|
||||
|
||||
if (!packId) {
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({
|
||||
toastType: 'error',
|
||||
message: '缺少必要的查询参数 (pack_id)'
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await executePricingDetailsQueryLocal(packId);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -4173,12 +4432,115 @@ export default function App() {
|
||||
const handleClearRecords = () => {
|
||||
setSelectedRecords([]);
|
||||
setRecordDetails([]);
|
||||
setQueryResults([]);
|
||||
setSecondaryProcessResults([]);
|
||||
setPricingDetailsResults([]);
|
||||
// 同时清空标签选择
|
||||
setSelectedLabels({});
|
||||
setExpectedDate(null);
|
||||
};
|
||||
|
||||
// 定价数据表格列定义
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: 'Pack ID',
|
||||
dataIndex: 'pack_id',
|
||||
key: 'pack_id',
|
||||
},
|
||||
{
|
||||
title: 'Pack Type',
|
||||
dataIndex: 'pack_type',
|
||||
key: 'pack_type',
|
||||
},
|
||||
{
|
||||
title: '物料编码',
|
||||
dataIndex: 'material_code',
|
||||
key: 'material_code',
|
||||
},
|
||||
{
|
||||
title: '一级分类',
|
||||
dataIndex: 'category1_name',
|
||||
key: 'category1_name',
|
||||
},
|
||||
{
|
||||
title: '二级分类',
|
||||
dataIndex: 'category2_name',
|
||||
key: 'category2_name',
|
||||
},
|
||||
{
|
||||
title: '三级分类',
|
||||
dataIndex: 'category3_name',
|
||||
key: 'category3_name',
|
||||
},
|
||||
{
|
||||
title: '品类属性',
|
||||
dataIndex: '品类属性',
|
||||
key: '品类属性',
|
||||
},
|
||||
];
|
||||
|
||||
// 二次工艺表格列定义
|
||||
const secondaryProcessColumns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: 'Foreign ID',
|
||||
dataIndex: 'foreign_id',
|
||||
key: 'foreign_id',
|
||||
},
|
||||
{
|
||||
title: 'Pack Type',
|
||||
dataIndex: 'pack_type',
|
||||
key: 'pack_type',
|
||||
},
|
||||
{
|
||||
title: '项目',
|
||||
dataIndex: 'costs_item',
|
||||
key: 'costs_item',
|
||||
},
|
||||
{
|
||||
title: '二次工艺',
|
||||
dataIndex: 'costs_type',
|
||||
key: 'costs_type',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remarks',
|
||||
key: 'remarks',
|
||||
},
|
||||
];
|
||||
|
||||
// 定价详情表格列定义
|
||||
const pricingDetailsColumns = [
|
||||
{
|
||||
title: 'Pack ID',
|
||||
dataIndex: 'pack_id',
|
||||
key: 'pack_id',
|
||||
},
|
||||
{
|
||||
title: '倍率',
|
||||
dataIndex: '倍率',
|
||||
key: '倍率',
|
||||
},
|
||||
{
|
||||
title: '加工费',
|
||||
dataIndex: '加工费',
|
||||
key: '加工费',
|
||||
},
|
||||
{
|
||||
title: '总价',
|
||||
dataIndex: '总价',
|
||||
key: '总价',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
@ -4330,30 +4692,6 @@ export default function App() {
|
||||
调整流程日期
|
||||
</Title>
|
||||
<Text type="tertiary" style={{ fontSize: '14px' }}>读取货期记录,精确还原时间线</Text>
|
||||
{lastSavedDeliveryRecordId && lastSavedDeliveryVersion !== null && (
|
||||
<Card style={{ marginTop: '12px', padding: '12px', backgroundColor: '#f6f8fa', borderRadius: 8 }}>
|
||||
<Text strong>最近保存的货期记录</Text>
|
||||
<div style={{ marginTop: 8, display: 'flex', gap: 12, flexWrap: 'wrap' }}>
|
||||
<span style={{
|
||||
fontSize: 12,
|
||||
color: '#1f2937',
|
||||
background: 'rgba(245, 158, 11, 0.12)',
|
||||
border: '1px solid rgba(245, 158, 11, 0.32)',
|
||||
borderRadius: 6,
|
||||
padding: '4px 8px',
|
||||
fontWeight: 600
|
||||
}}>版本:V{lastSavedDeliveryVersion}</span>
|
||||
<span style={{
|
||||
fontSize: 12,
|
||||
color: '#111827',
|
||||
background: 'rgba(17, 24, 39, 0.08)',
|
||||
border: '1px solid rgba(17, 24, 39, 0.18)',
|
||||
borderRadius: 6,
|
||||
padding: '4px 8px'
|
||||
}}>record_id:{lastSavedDeliveryRecordId}</span>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* 功能入口切换与调整入口 */}
|
||||
@ -5534,7 +5872,49 @@ export default function App() {
|
||||
{/* 批量处理配置已移除 */}
|
||||
</main>
|
||||
)}
|
||||
|
||||
{mode === 'generate' && (
|
||||
<>
|
||||
{/* 面料数据查询结果 */}
|
||||
{queryResults.length > 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<Title heading={4}>面料数据查询结果 ({queryResults.length} 条)</Title>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={queryResults.map((item, index) => ({ ...item, key: index }))}
|
||||
pagination={{ pageSize: 10 }}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{/* 二次工艺查询结果 */}
|
||||
{secondaryProcessResults.length > 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<Title heading={4}>二次工艺查询结果 ({secondaryProcessResults.length} 条)</Title>
|
||||
<Table
|
||||
columns={secondaryProcessColumns}
|
||||
dataSource={secondaryProcessResults.map((item, index) => ({ ...item, key: index }))}
|
||||
pagination={{ pageSize: 10 }}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{/* 工艺价格查询结果 */}
|
||||
{pricingDetailsResults.length > 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<Title heading={4}>工艺价格查询结果 ({pricingDetailsResults.length} 条)</Title>
|
||||
<Table
|
||||
columns={pricingDetailsColumns}
|
||||
dataSource={pricingDetailsResults.map((item, index) => ({ ...item, key: index }))}
|
||||
pagination={{ pageSize: 10 }}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user