feat: 接口明细清空 Tab 与 Array 根去重,面单预览走鉴权流
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m39s
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m39s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a24dcb41ff
commit
becb942e5b
@ -97,6 +97,17 @@ export async function getFileAccessUrl(key: string): Promise<AccessUrlResult> {
|
|||||||
return { key: storageKey, url }
|
return { key: storageKey, url }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** GET /file-storage/stream?key= — 鉴权流式读取(面单预览等) */
|
||||||
|
export async function streamFileBlob(keyOrUrl: string): Promise<Blob> {
|
||||||
|
const res = await axiosInstance.get(omsUrl('/file-storage/stream'), {
|
||||||
|
params: { key: keyOrUrl },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 120000,
|
||||||
|
})
|
||||||
|
if (res.data instanceof Blob) return res.data
|
||||||
|
return new Blob([res.data])
|
||||||
|
}
|
||||||
|
|
||||||
/** POST /file-storage/normalize-key */
|
/** POST /file-storage/normalize-key */
|
||||||
export async function normalizeFileStorageKey(pathOrUrl: string): Promise<string | null> {
|
export async function normalizeFileStorageKey(pathOrUrl: string): Promise<string | null> {
|
||||||
const res = await axiosInstance.post(omsUrl('/file-storage/normalize-key'), {
|
const res = await axiosInstance.post(omsUrl('/file-storage/normalize-key'), {
|
||||||
|
|||||||
@ -45,8 +45,10 @@
|
|||||||
defaultResolveParameterType,
|
defaultResolveParameterType,
|
||||||
extractCommentsFromJson,
|
extractCommentsFromJson,
|
||||||
flatParameterListToTableRows,
|
flatParameterListToTableRows,
|
||||||
|
hoistRedundantSyntheticArrayWrapper,
|
||||||
parseJsonTextToParameterFlatList,
|
parseJsonTextToParameterFlatList,
|
||||||
pickBestJsonBodyText,
|
pickBestJsonBodyText,
|
||||||
|
restoreSyntheticArrayWrapper,
|
||||||
sortFlatParameterListParentFirst,
|
sortFlatParameterListParentFirst,
|
||||||
} from '@/utils/jsonParameterTree';
|
} from '@/utils/jsonParameterTree';
|
||||||
|
|
||||||
@ -603,19 +605,50 @@
|
|||||||
pid === BODY_ROOT_VIRTUAL_ID
|
pid === BODY_ROOT_VIRTUAL_ID
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
/** 展示时提升掉的合成 array 包装,导出时还原 */
|
||||||
|
const hoistedBodyArrayWrapper = ref<any | null>(null);
|
||||||
function stripBodyRootFromFlat(rows: any[]): any[] {
|
function stripBodyRootFromFlat(rows: any[]): any[] {
|
||||||
const flat = flattenTreeTableRows(Array.isArray(rows) ? rows : []);
|
const flat = flattenTreeTableRows(Array.isArray(rows) ? rows : []);
|
||||||
return flat
|
return flat
|
||||||
.filter((r) => !isBodyRootRow(r))
|
.filter((r) => !isBodyRootRow(r))
|
||||||
.map((r) => (r.pId === BODY_ROOT_VIRTUAL_ID ? { ...r, pId: 0 } : { ...r }));
|
.map((r) => (r.pId === BODY_ROOT_VIRTUAL_ID ? { ...r, pId: 0 } : { ...r }));
|
||||||
}
|
}
|
||||||
function ensureBodyRootInRows(rows: any[]): any[] {
|
function exportRequestBodyFlatParams(rows: any[]): any[] {
|
||||||
|
const stripped = stripBodyRootFromFlat(rows);
|
||||||
|
if (Number(props.basicFormData?.mappedParamType) !== ParameterType.Array) {
|
||||||
|
return stripped;
|
||||||
|
}
|
||||||
|
return restoreSyntheticArrayWrapper(
|
||||||
|
stripped,
|
||||||
|
hoistedBodyArrayWrapper.value,
|
||||||
|
isTopLevelBodyPid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function remapHoistedArrayActionRow(row: any) {
|
||||||
|
if (!row || isBodyRootRow(row) || row.pId !== BODY_ROOT_VIRTUAL_ID) return row;
|
||||||
|
const wrapper = hoistedBodyArrayWrapper.value;
|
||||||
|
if (wrapper && Number(props.basicFormData?.mappedParamType) === ParameterType.Array) {
|
||||||
|
return { ...row, pId: wrapper.id };
|
||||||
|
}
|
||||||
|
return { ...row, pId: 0 };
|
||||||
|
}
|
||||||
|
function ensureBodyRootInRows(rows: any[], options?: { hoistSyntheticArray?: boolean }): any[] {
|
||||||
const cleaned = stripBodyRootFromFlat(rows);
|
const cleaned = stripBodyRootFromFlat(rows);
|
||||||
if (!showRootMappedParamType.value) return cleaned;
|
if (!showRootMappedParamType.value) {
|
||||||
|
hoistedBodyArrayWrapper.value = null;
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
const rootType =
|
const rootType =
|
||||||
props.basicFormData?.mappedParamType != null && props.basicFormData?.mappedParamType !== ''
|
props.basicFormData?.mappedParamType != null && props.basicFormData?.mappedParamType !== ''
|
||||||
? props.basicFormData.mappedParamType
|
? props.basicFormData.mappedParamType
|
||||||
: ParameterType.Object;
|
: ParameterType.Object;
|
||||||
|
const hoisted = hoistRedundantSyntheticArrayWrapper(cleaned, {
|
||||||
|
enabled:
|
||||||
|
Number(rootType) === ParameterType.Array && options?.hoistSyntheticArray !== false,
|
||||||
|
arrayType: ParameterType.Array,
|
||||||
|
isTopLevelPid: isTopLevelBodyPid,
|
||||||
|
});
|
||||||
|
hoistedBodyArrayWrapper.value = hoisted.hoistedWrapper;
|
||||||
const root = {
|
const root = {
|
||||||
id: BODY_ROOT_VIRTUAL_ID,
|
id: BODY_ROOT_VIRTUAL_ID,
|
||||||
pId: null,
|
pId: null,
|
||||||
@ -627,7 +660,7 @@
|
|||||||
};
|
};
|
||||||
return [
|
return [
|
||||||
root,
|
root,
|
||||||
...cleaned.map((r) =>
|
...hoisted.rows.map((r) =>
|
||||||
isTopLevelBodyPid(r.pId) ? { ...r, pId: BODY_ROOT_VIRTUAL_ID } : { ...r },
|
isTopLevelBodyPid(r.pId) ? { ...r, pId: BODY_ROOT_VIRTUAL_ID } : { ...r },
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@ -647,10 +680,11 @@
|
|||||||
const fromGrid =
|
const fromGrid =
|
||||||
requestTableRef.value?.getTableData?.()?.fullData ||
|
requestTableRef.value?.getTableData?.()?.fullData ||
|
||||||
requestTableRef.value?.xGrid?.getTableData()?.fullData;
|
requestTableRef.value?.xGrid?.getTableData()?.fullData;
|
||||||
return stripBodyRootFromFlat(getTreeTableFlatRows(fromGrid, requestTableProps.data));
|
return exportRequestBodyFlatParams(getTreeTableFlatRows(fromGrid, requestTableProps.data));
|
||||||
}
|
}
|
||||||
function stripBodyRootFromActionRows(actions: any) {
|
function stripBodyRootFromActionRows(actions: any) {
|
||||||
const filterRows = (arr: any[]) => (Array.isArray(arr) ? arr.filter((r) => !isBodyRootRow(r)) : []);
|
const filterRows = (arr: any[]) =>
|
||||||
|
(Array.isArray(arr) ? arr.filter((r) => !isBodyRootRow(r)).map(remapHoistedArrayActionRow) : []);
|
||||||
return {
|
return {
|
||||||
insertRecords: filterRows(actions?.insertRecords),
|
insertRecords: filterRows(actions?.insertRecords),
|
||||||
removeRecords: filterRows(actions?.removeRecords),
|
removeRecords: filterRows(actions?.removeRecords),
|
||||||
@ -1239,6 +1273,35 @@
|
|||||||
{ key: 'Xml', label: 'XML' },
|
{ key: 'Xml', label: 'XML' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const currentRequestTabClearLabel = computed(() => {
|
||||||
|
if (requestTab.value === 'query') return 'Query 参数';
|
||||||
|
if (requestTab.value === 'header') return 'Header 参数';
|
||||||
|
if (requestTab.value === 'fixed') return '固定配置';
|
||||||
|
if (requestTab.value === 'body') return 'Body 参数';
|
||||||
|
if (requestTab.value === 'mappingBody') return 'Mapping Body 参数';
|
||||||
|
if (requestTab.value === 'demo') return 'cURL 内容';
|
||||||
|
return '当前 Tab';
|
||||||
|
});
|
||||||
|
|
||||||
|
const showRequestTabClearAction = computed(() => {
|
||||||
|
if (requestTab.value === 'body' && isNoneBodyContentType.value) return false;
|
||||||
|
return ['query', 'header', 'fixed', 'body', 'mappingBody', 'demo'].includes(requestTab.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const canClearCurrentRequestTab = computed(() => {
|
||||||
|
if (!showRequestTabClearAction.value) return false;
|
||||||
|
if (requestTab.value === 'query') return queryTabCount.value > 0;
|
||||||
|
if (requestTab.value === 'header') return headerTabCount.value > 0;
|
||||||
|
if (requestTab.value === 'fixed') return fixedConfigTabCount.value > 0;
|
||||||
|
if (requestTab.value === 'body') {
|
||||||
|
if (isBodyKvMode.value) return !isBodyKvEmpty.value;
|
||||||
|
return !isBodyTreeEmpty.value;
|
||||||
|
}
|
||||||
|
if (requestTab.value === 'mappingBody') return !isMappingBodyEmpty.value;
|
||||||
|
if (requestTab.value === 'demo') return !!(requestExample.value || '').trim();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
/** 当前 Request Tab 空态提示(展示在顶部 Tab 行正中) */
|
/** 当前 Request Tab 空态提示(展示在顶部 Tab 行正中) */
|
||||||
const requestHeaderEmptyTip = computed(() => {
|
const requestHeaderEmptyTip = computed(() => {
|
||||||
if (requestTab.value === 'query' && queryTabCount.value === 0) {
|
if (requestTab.value === 'query' && queryTabCount.value === 0) {
|
||||||
@ -1356,7 +1419,7 @@
|
|||||||
if (!isDownstreamMode.value) return;
|
if (!isDownstreamMode.value) return;
|
||||||
const toMapping = isMappingBodyContentType(ct) && !isMappingBodyContentType(prev);
|
const toMapping = isMappingBodyContentType(ct) && !isMappingBodyContentType(prev);
|
||||||
if (!toMapping) return;
|
if (!toMapping) return;
|
||||||
const requestFlat = stripBodyRootFromFlat(requestTableProps.data || []);
|
const requestFlat = getRequestBodyFlatParams();
|
||||||
const mappingFlat = flattenTreeTableRows(mappingBodyTableProps.data || []);
|
const mappingFlat = flattenTreeTableRows(mappingBodyTableProps.data || []);
|
||||||
if (requestFlat.length > 0 && mappingFlat.length < requestFlat.length) {
|
if (requestFlat.length > 0 && mappingFlat.length < requestFlat.length) {
|
||||||
await loadFlatListIntoTreeTable(requestFlat, mappingBodyTableProps, mappingBodyTableRef);
|
await loadFlatListIntoTreeTable(requestFlat, mappingBodyTableProps, mappingBodyTableRef);
|
||||||
@ -1439,9 +1502,9 @@
|
|||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Json/Xml 切换 / 首次进入:挂上或拆掉虚拟根节点
|
// Json/Xml 切换 / 根类型变更:挂上、拆掉或提升虚拟根下的合成 array
|
||||||
watch(
|
watch(
|
||||||
showRootMappedParamType,
|
[showRootMappedParamType, () => props.basicFormData?.mappedParamType],
|
||||||
() => {
|
() => {
|
||||||
applyRequestBodyTableData(getRequestBodyFlatParams());
|
applyRequestBodyTableData(getRequestBodyFlatParams());
|
||||||
},
|
},
|
||||||
@ -1754,6 +1817,100 @@
|
|||||||
return isTableRowEditing(row, bodyFormTableRef);
|
return isTableRowEditing(row, bodyFormTableRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function removeTableRows(tableRef: { value?: { removeRow?: (row: any) => any; xGrid?: any } }, rows: any[]) {
|
||||||
|
if (!rows.length) return;
|
||||||
|
const grid = tableRef.value?.xGrid;
|
||||||
|
if (grid?.remove) {
|
||||||
|
await grid.remove(rows);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const row of rows) {
|
||||||
|
await tableRef.value?.removeRow?.(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectRemovableTableRows(
|
||||||
|
tableRef: any,
|
||||||
|
keepRow?: (row: any) => boolean,
|
||||||
|
) {
|
||||||
|
const roots = [...(getTableFullData(tableRef) || [])];
|
||||||
|
if (!keepRow) return roots;
|
||||||
|
const toRemove: any[] = [];
|
||||||
|
roots.forEach((row) => {
|
||||||
|
if (keepRow(row)) {
|
||||||
|
if (Array.isArray(row?.children) && row.children.length) {
|
||||||
|
toRemove.push(...row.children);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toRemove.push(row);
|
||||||
|
});
|
||||||
|
return toRemove;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearCurrentRequestTab() {
|
||||||
|
const tab = requestTab.value;
|
||||||
|
if (tab === 'query') {
|
||||||
|
await removeTableRows(queryTableRef, collectRemovableTableRows(queryTableRef));
|
||||||
|
queryTableProps.data = [];
|
||||||
|
} else if (tab === 'header') {
|
||||||
|
await removeTableRows(headerTableRef, collectRemovableTableRows(headerTableRef));
|
||||||
|
headerTableProps.data = [];
|
||||||
|
} else if (tab === 'fixed') {
|
||||||
|
await removeTableRows(queryFixedTableRef, collectRemovableTableRows(queryFixedTableRef));
|
||||||
|
fixedParams.value = [];
|
||||||
|
fixedParamsHasChanges.value = true;
|
||||||
|
} else if (tab === 'body' && isBodyKvMode.value) {
|
||||||
|
await removeTableRows(bodyFormTableRef, collectRemovableTableRows(bodyFormTableRef));
|
||||||
|
bodyFormTableProps.data = [];
|
||||||
|
} else if (tab === 'body' && !isNoneBodyContentType.value) {
|
||||||
|
if (hoistedBodyArrayWrapper.value) {
|
||||||
|
const persisted = getRequestBodyFlatParams();
|
||||||
|
hoistedBodyArrayWrapper.value = null;
|
||||||
|
const rows = ensureBodyRootInRows(persisted, { hoistSyntheticArray: false });
|
||||||
|
requestTableProps.data = rows;
|
||||||
|
await nextTick();
|
||||||
|
await requestTableRef.value?.reloadData?.(rows);
|
||||||
|
await nextTick();
|
||||||
|
}
|
||||||
|
await removeTableRows(
|
||||||
|
requestTableRef,
|
||||||
|
collectRemovableTableRows(requestTableRef, isBodyRootRow),
|
||||||
|
);
|
||||||
|
hoistedBodyArrayWrapper.value = null;
|
||||||
|
const remaining = getTableFullData(requestTableRef, []);
|
||||||
|
requestTableProps.data = remaining.length ? remaining : ensureBodyRootInRows([]);
|
||||||
|
} else if (tab === 'mappingBody') {
|
||||||
|
await removeTableRows(mappingBodyTableRef, collectRemovableTableRows(mappingBodyTableRef));
|
||||||
|
mappingBodyTableProps.data = [];
|
||||||
|
} else if (tab === 'demo') {
|
||||||
|
requestExample.value = '';
|
||||||
|
}
|
||||||
|
markDirty();
|
||||||
|
refreshTabCounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmClearCurrentRequestTab() {
|
||||||
|
if (!canClearCurrentRequestTab.value) {
|
||||||
|
$baseMessage('当前 Tab 没有可清空的内容', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const label = currentRequestTabClearLabel.value;
|
||||||
|
const content =
|
||||||
|
requestTab.value === 'fixed'
|
||||||
|
? `确定清空当前「${label}」?清空后需点击「保存固定配置」才会同步到服务端。`
|
||||||
|
: requestTab.value === 'demo'
|
||||||
|
? '确定清空当前 cURL 内容?'
|
||||||
|
: `确定清空当前「${label}」?此操作仅影响编辑区,保存接口后才会同步到服务端。`;
|
||||||
|
$baseConfirm({
|
||||||
|
title: '清空确认',
|
||||||
|
content,
|
||||||
|
okText: '清空',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: () => clearCurrentRequestTab(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 新增 Query 参数
|
// 新增 Query 参数
|
||||||
async function addNewQueryParam() {
|
async function addNewQueryParam() {
|
||||||
markDirty();
|
markDirty();
|
||||||
@ -3175,6 +3332,15 @@
|
|||||||
/>
|
/>
|
||||||
<div class="section-header-right">
|
<div class="section-header-right">
|
||||||
<div class="section-actions">
|
<div class="section-actions">
|
||||||
|
<a-button
|
||||||
|
v-if="showRequestTabClearAction"
|
||||||
|
size="small"
|
||||||
|
:disabled="!canClearCurrentRequestTab"
|
||||||
|
title="清空当前 Tab 内容"
|
||||||
|
@click="confirmClearCurrentRequestTab"
|
||||||
|
>
|
||||||
|
清空
|
||||||
|
</a-button>
|
||||||
<template v-if="requestTab === 'query'">
|
<template v-if="requestTab === 'query'">
|
||||||
<a-button size="small" @click="addNewQueryParam">新增</a-button>
|
<a-button size="small" @click="addNewQueryParam">新增</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -72,7 +72,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="batch-preview">
|
<div class="batch-preview">
|
||||||
<iframe v-if="activeItem?.url" ref="previewFrameRef" class="print-preview-frame" :src="activeItem.url" />
|
<div v-if="previewLoading" class="print-preview-loading">面单加载中…</div>
|
||||||
|
<iframe
|
||||||
|
v-else-if="previewSrc"
|
||||||
|
ref="previewFrameRef"
|
||||||
|
class="print-preview-frame"
|
||||||
|
:src="previewSrc"
|
||||||
|
/>
|
||||||
<a-empty v-else description="暂无可预览打印内容" />
|
<a-empty v-else description="暂无可预览打印内容" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -85,7 +91,13 @@
|
|||||||
<span class="print-preview-title">PDF/HTML 预览</span>
|
<span class="print-preview-title">PDF/HTML 预览</span>
|
||||||
<a-button size="small" @click="openActiveInNewTab">新标签打开</a-button>
|
<a-button size="small" @click="openActiveInNewTab">新标签打开</a-button>
|
||||||
</div>
|
</div>
|
||||||
<iframe v-if="activeItem?.url" ref="previewFrameRef" class="print-preview-frame" :src="activeItem.url" />
|
<div v-if="previewLoading" class="print-preview-loading">面单加载中…</div>
|
||||||
|
<iframe
|
||||||
|
v-else-if="previewSrc"
|
||||||
|
ref="previewFrameRef"
|
||||||
|
class="print-preview-frame"
|
||||||
|
:src="previewSrc"
|
||||||
|
/>
|
||||||
<a-empty v-else description="暂无可预览打印内容" />
|
<a-empty v-else description="暂无可预览打印内容" />
|
||||||
</section>
|
</section>
|
||||||
<aside class="single-side-panel">
|
<aside class="single-side-panel">
|
||||||
@ -125,7 +137,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, onBeforeUnmount, ref, watch } from 'vue';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import {
|
import {
|
||||||
fetchHtmlTextForHiprint,
|
fetchHtmlTextForHiprint,
|
||||||
@ -135,6 +147,7 @@ import {
|
|||||||
} from '@/utils/hiprint/hiprintElectronSocket';
|
} from '@/utils/hiprint/hiprintElectronSocket';
|
||||||
import { useHiprintClientState } from '@/utils/hiprint/hiprintClientState';
|
import { useHiprintClientState } from '@/utils/hiprint/hiprintClientState';
|
||||||
import { resolveOmsAssetUrl } from '@/api/oms/common';
|
import { resolveOmsAssetUrl } from '@/api/oms/common';
|
||||||
|
import { fetchPrintAssetBytes, resolvePrintPreviewSrc } from '@/utils/printPreview';
|
||||||
import {
|
import {
|
||||||
buildHiprintBlobPrintExtras,
|
buildHiprintBlobPrintExtras,
|
||||||
HIPRINT_PAPER_PRESET_OPTIONS,
|
HIPRINT_PAPER_PRESET_OPTIONS,
|
||||||
@ -169,34 +182,94 @@ const hiprintClientState = useHiprintClientState();
|
|||||||
const previewFrameRef = ref<HTMLIFrameElement>();
|
const previewFrameRef = ref<HTMLIFrameElement>();
|
||||||
const activeId = ref<number | string | null>(null);
|
const activeId = ref<number | string | null>(null);
|
||||||
const hiprintForm = ref(readHiprintPrintFormFromStorage());
|
const hiprintForm = ref(readHiprintPrintFormFromStorage());
|
||||||
|
const previewSrc = ref('');
|
||||||
|
const previewLoading = ref(false);
|
||||||
|
let previewBlobUrl: string | null = null;
|
||||||
|
let previewLoadToken = 0;
|
||||||
|
|
||||||
const printableItems = computed(() => (props.items || []).filter((x) => !!x.url));
|
const printableItems = computed(() => (props.items || []).filter((x) => !!String(x?.url || '').trim()));
|
||||||
const activeItem = computed(() => {
|
const activeItem = computed(() => {
|
||||||
if (!printableItems.value.length) return null;
|
if (!printableItems.value.length) return null;
|
||||||
if (activeId.value == null) return printableItems.value[0];
|
if (activeId.value == null) return printableItems.value[0];
|
||||||
return printableItems.value.find((x) => x.id === activeId.value) || printableItems.value[0];
|
return printableItems.value.find((x) => x.id === activeId.value) || printableItems.value[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function revokePreviewBlob() {
|
||||||
|
if (!previewBlobUrl) return;
|
||||||
|
URL.revokeObjectURL(previewBlobUrl);
|
||||||
|
previewBlobUrl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPreviewForActive() {
|
||||||
|
const token = ++previewLoadToken;
|
||||||
|
revokePreviewBlob();
|
||||||
|
previewSrc.value = '';
|
||||||
|
if (!props.visible || !activeItem.value?.url) {
|
||||||
|
previewLoading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
previewLoading.value = true;
|
||||||
|
try {
|
||||||
|
const src = await resolvePrintPreviewSrc(activeItem.value.url);
|
||||||
|
if (token !== previewLoadToken) {
|
||||||
|
if (src.startsWith('blob:')) URL.revokeObjectURL(src);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (src.startsWith('blob:')) {
|
||||||
|
previewBlobUrl = src;
|
||||||
|
}
|
||||||
|
previewSrc.value = src;
|
||||||
|
} catch {
|
||||||
|
if (token !== previewLoadToken) return;
|
||||||
|
previewSrc.value = toAbsoluteUrl(activeItem.value.url);
|
||||||
|
} finally {
|
||||||
|
if (token === previewLoadToken) previewLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
(visible) => {
|
(visible) => {
|
||||||
if (!visible) return;
|
if (!visible) {
|
||||||
|
revokePreviewBlob();
|
||||||
|
previewSrc.value = '';
|
||||||
|
previewLoading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
activeId.value = printableItems.value[0]?.id ?? null;
|
activeId.value = printableItems.value[0]?.id ?? null;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[
|
||||||
|
() => props.visible,
|
||||||
|
() => props.items,
|
||||||
|
() => activeItem.value?.id,
|
||||||
|
() => activeItem.value?.url,
|
||||||
|
],
|
||||||
|
() => {
|
||||||
|
void loadPreviewForActive();
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => hiprintForm.value,
|
() => hiprintForm.value,
|
||||||
(val) => writeHiprintPrintFormToStorage(val),
|
(val) => writeHiprintPrintFormToStorage(val),
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
previewLoadToken += 1;
|
||||||
|
revokePreviewBlob();
|
||||||
|
});
|
||||||
|
|
||||||
function openActiveInNewTab() {
|
function openActiveInNewTab() {
|
||||||
if (!activeItem.value?.url) {
|
if (!activeItem.value?.url) {
|
||||||
Message.warning('当前无可打开的打印链接');
|
Message.warning('当前无可打开的打印链接');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
window.open(activeItem.value.url, '_blank', 'noopener,noreferrer');
|
window.open(toAbsoluteUrl(activeItem.value.url) || activeItem.value.url, '_blank', 'noopener,noreferrer');
|
||||||
}
|
}
|
||||||
|
|
||||||
function toAbsoluteUrl(url: string) {
|
function toAbsoluteUrl(url: string) {
|
||||||
@ -211,7 +284,20 @@ function toAbsoluteUrl(url: string) {
|
|||||||
|
|
||||||
async function silentPrintByUrl(url: string) {
|
async function silentPrintByUrl(url: string) {
|
||||||
const extras = buildHiprintBlobPrintExtras(hiprintForm.value);
|
const extras = buildHiprintBlobPrintExtras(hiprintForm.value);
|
||||||
if (/\.pdf(\?|#|$)/i.test(url)) {
|
const isPdf = /\.pdf(\?|#|$)/i.test(url);
|
||||||
|
try {
|
||||||
|
const bytes = await fetchPrintAssetBytes(url);
|
||||||
|
if (isPdf) {
|
||||||
|
await hiprintSendBlobPdf({ pdf_blob: bytes, printExtras: extras });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const html = new TextDecoder('utf-8').decode(bytes);
|
||||||
|
await hiprintSendHtmlPrint({ html, printExtras: extras });
|
||||||
|
return;
|
||||||
|
} catch {
|
||||||
|
// 回退旧逻辑
|
||||||
|
}
|
||||||
|
if (isPdf) {
|
||||||
const pdf_blob = await fetchPdfBytesForHiprint(toAbsoluteUrl(url));
|
const pdf_blob = await fetchPdfBytesForHiprint(toAbsoluteUrl(url));
|
||||||
await hiprintSendBlobPdf({ pdf_blob, printExtras: extras });
|
await hiprintSendBlobPdf({ pdf_blob, printExtras: extras });
|
||||||
return;
|
return;
|
||||||
@ -245,7 +331,7 @@ async function printActive() {
|
|||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
window.open(activeItem.value.url, '_blank', 'noopener,noreferrer');
|
window.open(toAbsoluteUrl(activeItem.value.url) || activeItem.value.url, '_blank', 'noopener,noreferrer');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function printAll() {
|
async function printAll() {
|
||||||
@ -259,7 +345,7 @@ async function printAll() {
|
|||||||
}
|
}
|
||||||
const tasks = printableItems.value.map((item) =>
|
const tasks = printableItems.value.map((item) =>
|
||||||
silentPrintByUrl(item.url).catch(() => {
|
silentPrintByUrl(item.url).catch(() => {
|
||||||
window.open(item.url, '_blank', 'noopener,noreferrer');
|
window.open(toAbsoluteUrl(item.url) || item.url, '_blank', 'noopener,noreferrer');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
const results = await Promise.allSettled(tasks);
|
const results = await Promise.allSettled(tasks);
|
||||||
@ -323,4 +409,14 @@ async function printAll() {
|
|||||||
.print-preview-title { font-size: 12px; font-weight: 600; color: var(--color-text-2); }
|
.print-preview-title { font-size: 12px; font-weight: 600; color: var(--color-text-2); }
|
||||||
.single-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: auto; }
|
.single-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: auto; }
|
||||||
.print-preview-frame { width: 100%; height: 100%; min-height: 640px; border: none; }
|
.print-preview-frame { width: 100%; height: 100%; min-height: 640px; border: none; }
|
||||||
|
.print-preview-loading {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 640px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 13px;
|
||||||
|
background: var(--color-fill-1);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -43,6 +43,83 @@ function defaultIdFactory(): number {
|
|||||||
/** 解析树中的占位节点名,不参与 // 注释路径拼接 */
|
/** 解析树中的占位节点名,不参与 // 注释路径拼接 */
|
||||||
export const SYNTHETIC_PARAM_NAMES = new Set(['obj', 'val', 'array']);
|
export const SYNTHETIC_PARAM_NAMES = new Set(['obj', 'val', 'array']);
|
||||||
|
|
||||||
|
function pickArrayWrapperPersistFields(row: any) {
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
pId: 0,
|
||||||
|
name: row.name,
|
||||||
|
type: row.type,
|
||||||
|
description: row.description ?? '',
|
||||||
|
isRequired: !!row.isRequired,
|
||||||
|
alias: row.alias || row.name,
|
||||||
|
cnName: row.cnName ?? '',
|
||||||
|
optionType: row.optionType ?? 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** JSON 数组根解析出的合成 array 节点(与虚拟根 Array 类型重复) */
|
||||||
|
export function isSyntheticArrayWrapper(row: any, arrayType: number = ParameterType.Array): boolean {
|
||||||
|
if (!row) return false;
|
||||||
|
const name = String(row.name ?? row.Name ?? '').trim().toLowerCase();
|
||||||
|
return name === 'array' && Number(row.type ?? row.Type) === Number(arrayType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 虚拟根已是 Array 时,去掉顶层合成 array 包装,把其子节点提到根下。
|
||||||
|
* 避免类型列连续出现 Array / Array。导出时按 childIds 还原,其它顶层兄弟不动。
|
||||||
|
*/
|
||||||
|
export function hoistRedundantSyntheticArrayWrapper(
|
||||||
|
rows: any[],
|
||||||
|
options: {
|
||||||
|
enabled: boolean;
|
||||||
|
arrayType?: number;
|
||||||
|
isTopLevelPid: (pId: any) => boolean;
|
||||||
|
},
|
||||||
|
): { rows: any[]; hoistedWrapper: any | null } {
|
||||||
|
const list = Array.isArray(rows) ? rows : [];
|
||||||
|
if (!options.enabled) return { rows: list, hoistedWrapper: null };
|
||||||
|
|
||||||
|
const arrayType = options.arrayType ?? ParameterType.Array;
|
||||||
|
const topLevel = list.filter((r) => r && options.isTopLevelPid(r.pId ?? r.PId));
|
||||||
|
const wrappers = topLevel.filter((r) => isSyntheticArrayWrapper(r, arrayType));
|
||||||
|
if (!wrappers.length) return { rows: list, hoistedWrapper: null };
|
||||||
|
|
||||||
|
const wrapper =
|
||||||
|
wrappers.find((w) => list.some((r) => r && (r.pId ?? r.PId) === (w.id ?? w.Id))) || wrappers[0];
|
||||||
|
const wrapperId = wrapper.id ?? wrapper.Id;
|
||||||
|
const childIds = list
|
||||||
|
.filter((r) => r && (r.pId ?? r.PId) === wrapperId)
|
||||||
|
.map((r) => r.id ?? r.Id)
|
||||||
|
.filter((id) => id != null);
|
||||||
|
if (!childIds.length) return { rows: list, hoistedWrapper: null };
|
||||||
|
|
||||||
|
return {
|
||||||
|
rows: list
|
||||||
|
.filter((r) => r && (r.id ?? r.Id) !== wrapperId)
|
||||||
|
.map((r) => ((r.pId ?? r.PId) === wrapperId ? { ...r, pId: 0 } : r)),
|
||||||
|
hoistedWrapper: { ...pickArrayWrapperPersistFields(wrapper), childIds },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 把展示时提升掉的合成 array 包装还原回扁平列表(保存 / 校验) */
|
||||||
|
export function restoreSyntheticArrayWrapper(
|
||||||
|
rows: any[],
|
||||||
|
wrapper: any | null,
|
||||||
|
_isTopLevelPid: (pId: any) => boolean,
|
||||||
|
): any[] {
|
||||||
|
const list = Array.isArray(rows) ? rows : [];
|
||||||
|
if (!wrapper) return list;
|
||||||
|
const wrapperId = wrapper.id ?? wrapper.Id;
|
||||||
|
if (wrapperId == null || list.some((r) => (r?.id ?? r?.Id) === wrapperId)) return list;
|
||||||
|
const { childIds, ...wrapperRow } = wrapper;
|
||||||
|
const childIdSet = new Set((childIds || []).map((id: any) => id));
|
||||||
|
if (!childIdSet.size) return list;
|
||||||
|
return [
|
||||||
|
{ ...wrapperRow, pId: 0 },
|
||||||
|
...list.map((r) => (childIdSet.has(r?.id ?? r?.Id) ? { ...r, pId: wrapperId } : r)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
type JsonPathFrame = { kind: 'object' | 'array'; key?: string };
|
type JsonPathFrame = { kind: 'object' | 'array'; key?: string };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
130
src/utils/printPreview.ts
Normal file
130
src/utils/printPreview.ts
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import { axiosInstance } from '@/api/rbac'
|
||||||
|
import { omsUrl, resolveOmsAssetUrl } from '@/api/oms/common'
|
||||||
|
import { normalizeFileStorageKey } from '@/api/fileStorage'
|
||||||
|
|
||||||
|
function guessMime(pathOrUrl: string): string {
|
||||||
|
if (/\.pdf(\?|#|$)/i.test(pathOrUrl)) return 'application/pdf'
|
||||||
|
if (/\.html?(\?|#|$)/i.test(pathOrUrl)) return 'text/html; charset=utf-8'
|
||||||
|
return 'application/octet-stream'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 开发环境把生产 Admin 绝对地址改写成 Vite 同源代理路径 */
|
||||||
|
function rewriteDevAdminAssetPath(url: string): string {
|
||||||
|
if (!import.meta.env.DEV) return url
|
||||||
|
const trimmed = String(url || '').trim()
|
||||||
|
if (!trimmed) return trimmed
|
||||||
|
const proxied = trimmed.replace(/^https?:\/\/api\.(?:ideship|nanafa)\.com\/admin/i, '')
|
||||||
|
return proxied || trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
function toSameOriginPath(url: string): string {
|
||||||
|
const resolved = rewriteDevAdminAssetPath(resolveOmsAssetUrl(url) || url)
|
||||||
|
if (!resolved) return ''
|
||||||
|
if (/^https?:\/\//i.test(resolved)) return resolved
|
||||||
|
return resolved.startsWith('/') ? resolved : `/${resolved}`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchBlobViaStream(keyOrUrl: string): Promise<Blob> {
|
||||||
|
const res = await axiosInstance.get(omsUrl('/file-storage/stream'), {
|
||||||
|
params: { key: keyOrUrl },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 120000,
|
||||||
|
})
|
||||||
|
const type = String(res.headers['content-type'] || guessMime(keyOrUrl))
|
||||||
|
if (res.data instanceof Blob) {
|
||||||
|
return res.data.type ? res.data : new Blob([res.data], { type })
|
||||||
|
}
|
||||||
|
return new Blob([res.data], { type })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchBlobViaSameOriginPath(path: string): Promise<Blob> {
|
||||||
|
const res = await axiosInstance.get(path, {
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 120000,
|
||||||
|
})
|
||||||
|
const type = String(res.headers['content-type'] || guessMime(path))
|
||||||
|
if (res.data instanceof Blob) {
|
||||||
|
return res.data.type ? res.data : new Blob([res.data], { type })
|
||||||
|
}
|
||||||
|
return new Blob([res.data], { type })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将打印 URL 转为可在 iframe 中预览的 blob: 地址(同源,避免 COS / api 跨域空白)。
|
||||||
|
* 失败时回退为可直链打开的绝对地址。
|
||||||
|
*/
|
||||||
|
export async function resolvePrintPreviewSrc(rawUrl: string): Promise<string> {
|
||||||
|
const raw = String(rawUrl || '').trim()
|
||||||
|
if (!raw) return ''
|
||||||
|
|
||||||
|
const candidates = new Set<string>()
|
||||||
|
candidates.add(raw)
|
||||||
|
const asset = resolveOmsAssetUrl(raw)
|
||||||
|
if (asset) candidates.add(asset)
|
||||||
|
candidates.add(rewriteDevAdminAssetPath(raw))
|
||||||
|
if (asset) candidates.add(rewriteDevAdminAssetPath(asset))
|
||||||
|
|
||||||
|
try {
|
||||||
|
const key = await normalizeFileStorageKey(raw)
|
||||||
|
if (key) candidates.add(key)
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
const value = String(candidate || '').trim()
|
||||||
|
if (!value) continue
|
||||||
|
try {
|
||||||
|
const blob = await fetchBlobViaStream(value)
|
||||||
|
return URL.createObjectURL(blob)
|
||||||
|
} catch {
|
||||||
|
// try next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sameOriginPath = toSameOriginPath(raw)
|
||||||
|
if (sameOriginPath.startsWith('/')) {
|
||||||
|
try {
|
||||||
|
const blob = await fetchBlobViaSameOriginPath(sameOriginPath)
|
||||||
|
return URL.createObjectURL(blob)
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return `${window.location.origin}${sameOriginPath}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return sameOriginPath
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 供 hiprint 静默打印:优先经 /file-storage/stream 拉取字节 */
|
||||||
|
export async function fetchPrintAssetBytes(rawUrl: string): Promise<Uint8Array> {
|
||||||
|
const raw = String(rawUrl || '').trim()
|
||||||
|
if (!raw) throw new Error('打印地址为空')
|
||||||
|
|
||||||
|
const candidates = new Set<string>([raw])
|
||||||
|
try {
|
||||||
|
const key = await normalizeFileStorageKey(raw)
|
||||||
|
if (key) candidates.add(key)
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
const value = String(candidate || '').trim()
|
||||||
|
if (!value) continue
|
||||||
|
try {
|
||||||
|
const blob = await fetchBlobViaStream(value)
|
||||||
|
return new Uint8Array(await blob.arrayBuffer())
|
||||||
|
} catch {
|
||||||
|
// try next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = toSameOriginPath(raw)
|
||||||
|
if (path.startsWith('/')) {
|
||||||
|
const blob = await fetchBlobViaSameOriginPath(path)
|
||||||
|
return new Uint8Array(await blob.arrayBuffer())
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('无法下载面单文件')
|
||||||
|
}
|
||||||
@ -1263,10 +1263,6 @@
|
|||||||
Message.warning('请选择要打印的订单');
|
Message.warning('请选择要打印的订单');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
|
||||||
Message.warning('请先在顶部打开并连接打印开关');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await withOrderActionLoading('singlePrint', async () => {
|
await withOrderActionLoading('singlePrint', async () => {
|
||||||
const { printUrl } = await postOrderPrint({ id: getOrderRowId(row)! });
|
const { printUrl } = await postOrderPrint({ id: getOrderRowId(row)! });
|
||||||
@ -1277,6 +1273,9 @@
|
|||||||
}
|
}
|
||||||
singlePrintItems.value = [buildPrintItem(row, printUrl)];
|
singlePrintItems.value = [buildPrintItem(row, printUrl)];
|
||||||
singlePrintVisible.value = true;
|
singlePrintVisible.value = true;
|
||||||
|
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
||||||
|
Message.info('已打开面单预览;静默打印需先在顶部连接打印开关');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e?.message !== 'busy') {
|
if (e?.message !== 'busy') {
|
||||||
@ -1290,10 +1289,6 @@
|
|||||||
batchPrintItems.value = [];
|
batchPrintItems.value = [];
|
||||||
const rows = requireSelectedRows();
|
const rows = requireSelectedRows();
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
|
||||||
Message.warning('请先在顶部打开并连接打印开关');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await withOrderActionLoading('batchPrint', async () => {
|
await withOrderActionLoading('batchPrint', async () => {
|
||||||
await runSelectedOrderBatch({
|
await runSelectedOrderBatch({
|
||||||
@ -1643,7 +1638,7 @@
|
|||||||
() => activeTab.value === 'pendingPayment' && hasPermission('Order.MyOrders.Pay'),
|
() => activeTab.value === 'pendingPayment' && hasPermission('Order.MyOrders.Pay'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const actionRenderer = () => {
|
const actionRenderer = (_rowData: any, rowIndex: number) => {
|
||||||
const parts = [
|
const parts = [
|
||||||
renderWarehouseActionBtn('view', t('common.viewDetail'), 'view'),
|
renderWarehouseActionBtn('view', t('common.viewDetail'), 'view'),
|
||||||
renderWarehouseActionBtn('track', t('order.myOrders.actions.viewTrack'), 'track'),
|
renderWarehouseActionBtn('track', t('order.myOrders.actions.viewTrack'), 'track'),
|
||||||
@ -1655,7 +1650,7 @@
|
|||||||
if (showRowPayAction.value) {
|
if (showRowPayAction.value) {
|
||||||
parts.push(renderWarehouseActionBtn('pay', t('order.myOrders.actions.pay'), 'pay'));
|
parts.push(renderWarehouseActionBtn('pay', t('order.myOrders.actions.pay'), 'pay'));
|
||||||
}
|
}
|
||||||
return wrapWarehouseActions(parts);
|
return wrapWarehouseActions(parts, rowIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
const customCellRenderer = (row: number, col: number, value: any, rowData: any) => {
|
const customCellRenderer = (row: number, col: number, value: any, rowData: any) => {
|
||||||
|
|||||||
@ -1218,10 +1218,6 @@
|
|||||||
Message.warning('请选择要打印的订单');
|
Message.warning('请选择要打印的订单');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
|
||||||
Message.warning('请先在顶部打开并连接打印开关');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await withOrderActionLoading('singlePrint', async () => {
|
await withOrderActionLoading('singlePrint', async () => {
|
||||||
const { printUrl } = await postOrderPrint({ id: getOrderRowId(row)! });
|
const { printUrl } = await postOrderPrint({ id: getOrderRowId(row)! });
|
||||||
@ -1232,6 +1228,9 @@
|
|||||||
}
|
}
|
||||||
singlePrintItems.value = [buildPrintItem(row, printUrl)];
|
singlePrintItems.value = [buildPrintItem(row, printUrl)];
|
||||||
singlePrintVisible.value = true;
|
singlePrintVisible.value = true;
|
||||||
|
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
||||||
|
Message.info('已打开面单预览;静默打印需先在顶部连接打印开关');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e?.message !== 'busy') {
|
if (e?.message !== 'busy') {
|
||||||
@ -1245,10 +1244,6 @@
|
|||||||
batchPrintItems.value = [];
|
batchPrintItems.value = [];
|
||||||
const rows = requireSelectedRows();
|
const rows = requireSelectedRows();
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
|
||||||
Message.warning('请先在顶部打开并连接打印开关');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await withOrderActionLoading('batchPrint', async () => {
|
await withOrderActionLoading('batchPrint', async () => {
|
||||||
await runSelectedOrderBatch({
|
await runSelectedOrderBatch({
|
||||||
@ -1564,7 +1559,7 @@
|
|||||||
() => activeTab.value === 'pendingPayment' && hasPermission('Order.WaybillManage.Pay'),
|
() => activeTab.value === 'pendingPayment' && hasPermission('Order.WaybillManage.Pay'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const actionRenderer = () => {
|
const actionRenderer = (_rowData: any, rowIndex: number) => {
|
||||||
const parts = [
|
const parts = [
|
||||||
renderWarehouseActionBtn('view', t('common.viewDetail'), 'view'),
|
renderWarehouseActionBtn('view', t('common.viewDetail'), 'view'),
|
||||||
renderWarehouseActionBtn('track', t('order.myOrders.actions.viewTrack'), 'track'),
|
renderWarehouseActionBtn('track', t('order.myOrders.actions.viewTrack'), 'track'),
|
||||||
@ -1576,7 +1571,7 @@
|
|||||||
if (showRowPayAction.value) {
|
if (showRowPayAction.value) {
|
||||||
parts.push(renderWarehouseActionBtn('pay', t('order.myOrders.actions.pay'), 'pay'));
|
parts.push(renderWarehouseActionBtn('pay', t('order.myOrders.actions.pay'), 'pay'));
|
||||||
}
|
}
|
||||||
return wrapWarehouseActions(parts);
|
return wrapWarehouseActions(parts, rowIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
const customCellRenderer = (row: number, col: number, value: any, rowData: any) => {
|
const customCellRenderer = (row: number, col: number, value: any, rowData: any) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user