tommy 1 месяц назад
Родитель
Сommit
500828fa97
7 измененных файлов: 715 добавлений и 401 удалений
  1. +15
    -0
      src/app/api/logistic/actions.ts
  2. +5
    -0
      src/app/api/logistic/client.ts
  3. +3
    -0
      src/app/api/shop/actions.ts
  4. +625
    -388
      src/components/Shop/RouteBoard.tsx
  5. +45
    -3
      src/components/Shop/routeBoardVersionLog.ts
  6. +10
    -4
      src/i18n/en/routeboard.json
  7. +12
    -6
      src/i18n/zh/routeboard.json

+ 15
- 0
src/app/api/logistic/actions.ts Просмотреть файл

@@ -48,3 +48,18 @@ export const saveLogisticsBatchCreateAction = async (
headers: { "Content-Type": "application/json" },
});
};

export type DeleteLogisticRequest = {
id: number;
};

export const deleteLogisticAction = async (
data: DeleteLogisticRequest,
): Promise<{ message?: string }> => {
const endpoint = `${BASE_API_URL}/logistic/delete`;
return serverFetchJson<{ message?: string }>(endpoint, {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
};

+ 5
- 0
src/app/api/logistic/client.ts Просмотреть файл

@@ -4,6 +4,7 @@ import {
findAllLogisticsAction,
saveLogisticAction,
saveLogisticsBatchCreateAction,
deleteLogisticAction,
type LogisticRow,
type SaveLogisticRequest,
} from "./actions";
@@ -25,3 +26,7 @@ export const saveLogisticClient = async (
): Promise<LogisticRow> => {
return await saveLogisticAction(data);
};

export const deleteLogisticClient = async (id: number): Promise<void> => {
await deleteLogisticAction({ id });
};

+ 3
- 0
src/app/api/shop/actions.ts Просмотреть файл

@@ -519,6 +519,9 @@ export type TruckLaneVersionDiffLine = {
truckRowId: number;
shopCode: string | null;
changes: DiffFieldChange[];
/** 快照車線(僅欄位異動時 changes 不含 truckLanceCode/remark) */
truckLanceCode?: string | null;
remark?: string | null;
};

export type LogisticMasterDiffLine = {


+ 625
- 388
src/components/Shop/RouteBoard.tsx
Разница между файлами не показана из-за своего большого размера
Просмотреть файл


+ 45
- 3
src/components/Shop/routeBoardVersionLog.ts Просмотреть файл

@@ -27,9 +27,11 @@ function pickField(
return changes.find((c) => c.field === field);
}

export const VERSION_LOG_LOADING_SEQUENCE_LABEL = "裝載順序";

const VERSION_LOG_FIELD_LABEL: Record<string, string> = {
departureTime: "發車時段",
loadingSequence: "裝載順序",
loadingSequence: VERSION_LOG_LOADING_SEQUENCE_LABEL,
branchName: "分店名稱",
districtReference: "區域",
shopCode: "店鋪代碼",
@@ -91,6 +93,19 @@ function buildFieldEditsForRow(
return out.length > 0 ? out : undefined;
}

/** 同車線編輯(如僅裝載順序)時用於 UI 顯示車線 */
export function resolveVersionLogLaneLabel(
row: Pick<VersionLogShopRow, "fromLane" | "toLane">,
): string | undefined {
const lane =
row.toLane && row.toLane !== "—"
? row.toLane
: row.fromLane && row.fromLane !== "—"
? row.fromLane
: undefined;
return lane;
}

export function formatLaneLabel(
truckLanceCode?: string | null,
remark?: string | null,
@@ -138,8 +153,17 @@ export function diffLineToShopRow(
const code = line.shopCode != null ? String(line.shopCode) : "";
const shopName = String(br?.to ?? br?.from ?? (code || "—")).trim() || "—";

const fromLane = formatLaneLabel(tc?.from, rem?.from);
const toLane = formatLaneLabel(tc?.to, rem?.to);
const ctxCode =
line.truckLanceCode != null && String(line.truckLanceCode).trim() !== ""
? String(line.truckLanceCode).trim()
: undefined;
const ctxRem =
line.remark != null && String(line.remark).trim() !== ""
? String(line.remark).trim()
: undefined;

const fromLane = formatLaneLabel(tc?.from ?? ctxCode, rem?.from ?? ctxRem);
const toLane = formatLaneLabel(tc?.to ?? ctxCode, rem?.to ?? ctxRem);
const fromEmpty = fromLane === "—";
const toEmpty = toLane === "—";

@@ -248,6 +272,10 @@ export function buildStagedBoardLogEntries(input: {
fromName?: string;
fromPlate?: string;
}>;
pendingLogisticMasterDeletes?: Array<{
id: number;
logisticName: string;
}>;
pendingImport?: {
fileName: string;
sheetCount: number;
@@ -283,6 +311,7 @@ export function buildStagedBoardLogEntries(input: {
pendingNewLanes,
pendingLogisticMasterAdds,
pendingLogisticMasterEdits,
pendingLogisticMasterDeletes,
pendingImport,
laneLogisticChanges,
lanes,
@@ -349,6 +378,17 @@ export function buildStagedBoardLogEntries(input: {
});
}

for (const p of pendingLogisticMasterDeletes ?? []) {
const name = String(p.logisticName ?? "").trim() || "—";
out.push({
key: `plog-del-${p.id}`,
tag: "unsaved",
kind: "text",
titleKey: "diff_staged_deleteLogisticMaster",
titleParams: { name },
});
}

const dirtyMoveShopIds = new Set<number>();

dirtyMoves.forEach((laneId, shopId) => {
@@ -416,6 +456,8 @@ export function buildStagedBoardLogEntries(input: {
type: "edited",
shopName,
shopCode,
fromLane: laneLabel,
toLane: laneLabel,
truckRowId: shopId,
fieldEdits,
},


+ 10
- 4
src/i18n/en/routeboard.json Просмотреть файл

@@ -87,6 +87,7 @@
"logistic_needMasterTpl": "\"{{name}}\" has no logistics master id—create it with \"Add logistics\" first.",
"diffField_logisticsCompany": "Logistics company",
"diffLogistic_unassigned": "Unassigned",
"diff_onLane": "Lane {{lane}}",
"diff_moveTo": "Move to {{lane}}",
"diff_addedToLane": "Added to lane {{lane}}",
"diff_removedFromLane": "Removed from {{lane}}",
@@ -119,6 +120,7 @@
"version_ui_listAria": "Version history list",
"version_ui_snapshotBadge": "Current snapshot",
"version_ui_id": "Version #{{id}}",
"version_ui_none": "No snapshot yet",
"version_ui_editedBy": "Editor: {{name}}",
"version_note_placeholder": "Note (saved on blur)",
"version_note_saving": "Saving…",
@@ -187,7 +189,6 @@
"logistic_plate": "Plate",
"logistic_driver": "Driver name",
"logistic_phone": "Phone",
"logistic_phone_helper": "Server stores Int digits (e.g. 9811-5780); +852 8-digit local numbers are OK",
"logistic_btn_save": "Save",
"logistic_btn_saveDb": "Save to database",
"shop_autocomplete_label": "Select shop",
@@ -209,7 +210,6 @@
"seqDialog_hint": "Press \"Save changes\" to persist to truck rows.",
"logistics_colLaneCount": "{{count}} lane(s)",
"logistics_masterNoLanes": "Master record exists but no lanes are bound yet; pick this company when adding/editing lanes on the route board.",
"logistics_dataSource": "Data: board lanes (including left filter)",
"tooltip_openLaneBoard": "Open this lane on the route board",
"aria_openLaneBoard": "Open lane on route board",
"tooltip_removeFromLane": "Remove from this lane",
@@ -218,8 +218,14 @@
"aria_pickLane": "Pick lane",
"aria_searchLanes": "Search lanes",
"logistics_colShopCount": "{{count}} shop(s)",
"tooltip_editLogisticsDb": "Edit logistics master (save to database)",
"aria_editLogistics": "Edit logistics master",
"tooltip_editLogisticsDb": "Edit logistics company (persists after Save changes)",
"tooltip_deleteLogistics": "Delete logistics company (persists after Save changes)",
"aria_editLogistics": "Edit logistics company",
"aria_deleteLogistics": "Delete logistics company",
"confirm_deleteLogistic": "Delete logistics company \"{{name}}\"? Press \"Save changes\" to persist.",
"err_logisticDeleteHasLanes": "This company still has {{count}} lane(s). Reassign or remove them first.",
"diff_staged_deleteLogisticMaster": "Delete logistics company (not saved): {{name}}",
"logistic_btn_apply": "Apply",
"tooltip_editDistrict": "Edit district name (press \"Save changes\" to persist)",
"aria_editDistrict": "Edit district",
"tooltip_removeEmptyDistrict": "Remove this staged empty block (deletable before save)",


+ 12
- 6
src/i18n/zh/routeboard.json Просмотреть файл

@@ -78,7 +78,7 @@
"versionNote_saveFail": "備註儲存失敗",
"diff_restoreFail": "恢復失敗",
"confirm_restoreDiscardsEdits": "排程版本還原會捨棄目前看板上其他未儲存變更(拖曳、刪除、新增店鋪/車線、物流欄等)。確定繼續?",
"diff_restoreScheduled": "已排程還原至版本 #{{versionId}};請按「儲存更改」才會寫入後端。",
"diff_restoreScheduled": "已排程還原至版本 #{{versionId}};請按「儲存更改」寫入後端。",
"diff_restoreAlreadyPending": "此版本已在排程還原中;請按「儲存更改」套用。",
"restore_applied": "已從 snapshot 還原並重新載入看板。",
"restore_appliedDroppedStaging": "已套用 snapshot 還原;本次儲存略過其他暫存變更(請重新編輯)。",
@@ -87,6 +87,7 @@
"logistic_needMasterTpl": "「{{name}}」尚無對應物流公司,請先用「新增物流商」建立。",
"diffField_logisticsCompany": "物流公司",
"diffLogistic_unassigned": "未分配",
"diff_onLane": "車線 {{lane}}",
"diff_moveTo": "移至 {{lane}}",
"diff_addedToLane": "新加入車線 {{lane}}",
"diff_removedFromLane": "自 {{lane}} 移除",
@@ -118,7 +119,8 @@
"version_ui_filterAria": "版本列表篩選",
"version_ui_listAria": "版本歷史列表",
"version_ui_snapshotBadge": "目前快照",
"version_ui_id": "版本 #{{id}}",
"version_ui_id": "版本#{{id}}",
"version_ui_none": "尚無快照",
"version_ui_editedBy": "編輯者:{{name}}",
"version_note_placeholder": "備註(離開欄位即儲存)",
"version_note_saving": "儲存中…",
@@ -162,7 +164,7 @@
"diff_noShopDiffHasBoardStaged": "與上一版快照相比,店鋪列無差異;下列為看板上尚未按「儲存更改」寫入的變更(含新增物流公司)。",
"diff_export_blockedTooltip": "匯出檔為後端兩版快照比對,不含看板未儲存變更。請先按「儲存更改」或取消變更後再匯出。",
"diff_export_blockedError": "有看板未儲存變更時無法匯出(Excel 僅含已落庫快照)。",
"diff_markedCount": "已標記 {{count}} 筆 truck 異動(看板可對照)",
"diff_markedCount": "已標記 {{count}} 筆車線異動(看板可對照)",
"diff_noDiffFromPrev": "與上一版無差異",
"diff_loadingEllipsis": "…",
"addShop_dialogTitle": "新增店鋪到車線",
@@ -187,7 +189,6 @@
"logistic_plate": "車牌",
"logistic_driver": "司機姓名",
"logistic_phone": "聯絡電話",
"logistic_phone_helper": "後端為 Int:輸入數字即可(含 9811-5780);+852 八位本地號亦可",
"logistic_btn_save": "儲存",
"logistic_btn_saveDb": "儲存至資料庫",
"shop_autocomplete_label": "選擇店鋪",
@@ -209,7 +210,6 @@
"seqDialog_hint": "按「儲存更改」後寫入 truck 列。",
"logistics_colLaneCount": "{{count}} 條車線",
"logistics_masterNoLanes": "主檔已建立,尚無綁定車線;至「車線看板」新增/編輯車線時可填此公司名稱。",
"logistics_dataSource": "資料來源:看板車線(含左欄篩選)",
"tooltip_openLaneBoard": "在車線看板開此車線",
"aria_openLaneBoard": "開啟車線看板",
"tooltip_removeFromLane": "從此車線移除",
@@ -218,8 +218,14 @@
"aria_pickLane": "選擇車線",
"aria_searchLanes": "搜尋車線",
"logistics_colShopCount": "{{count}} 家店鋪",
"tooltip_editLogisticsDb": "編輯物流公司(寫入資料庫)",
"tooltip_editLogisticsDb": "編輯物流公司(須按「儲存更改」寫入)",
"tooltip_deleteLogistics": "刪除物流公司(須按「儲存更改」寫入)",
"aria_editLogistics": "編輯物流公司",
"aria_deleteLogistics": "刪除物流公司",
"confirm_deleteLogistic": "確定刪除物流公司「{{name}}」?須按「儲存更改」寫入。",
"err_logisticDeleteHasLanes": "此物流公司尚有 {{count}} 條車線,請先移走或改派後再刪除。",
"diff_staged_deleteLogisticMaster": "刪除物流公司(未落庫):{{name}}",
"logistic_btn_apply": "套用",
"tooltip_editDistrict": "編輯地區名稱(按「儲存更改」才寫入)",
"aria_editDistrict": "編輯地區",
"tooltip_removeEmptyDistrict": "移除此暫存區塊(未儲存前可刪)",


Загрузка…
Отмена
Сохранить