| | |
| | | |
| | | import { h } from 'vue'; |
| | | |
| | | import { message, notification } from 'ant-design-vue'; |
| | | import { message, Modal } from 'ant-design-vue'; |
| | | |
| | | export interface ImportResultNotifyOptions { |
| | | /** 业务主体,用于拼标题与建议,如「设备」「客户」 */ |
| | |
| | | /** |
| | | * 统一展示 Excel 导入结果。 |
| | | * |
| | | * 只要后端返回了失败明细,就绝不能再提示「操作成功」——改为常驻通知逐行列出失败原因, |
| | | * 只要后端返回了失败明细,就绝不能再提示「操作成功」——改为弹窗逐行列出失败原因, |
| | | * 并按失败原因给出下一步动作;全部成功时才提示成功。 |
| | | * |
| | | * 失败明细可能上百行,所以用弹窗(弹窗内列表可滚动),避免长消息糊满屏幕。 |
| | | * |
| | | * @returns 实际写入的条数(新增 + 更新),供调用方决定是否需要刷新列表 |
| | | */ |
| | |
| | | return importedCount; |
| | | } |
| | | |
| | | const nodes: VNodeChild[] = [ |
| | | const hints = buildFailureHints( |
| | | failureEntries.map(([, reason]) => reason), |
| | | { subject, masterDataHint }, |
| | | ); |
| | | |
| | | const content: VNodeChild = h('div', [ |
| | | h( |
| | | 'div', |
| | | `新增成功 ${createCount} 条,更新成功 ${updateCount} 条,失败 ${failureEntries.length} 条:`, |
| | | `新增成功 ${createCount} 条,更新成功 ${updateCount} 条,失败 ${failureEntries.length} 条。`, |
| | | ), |
| | | ...failureEntries.map(([name, reason]) => h('div', `${name}:${reason}`)), |
| | | ...buildFailureHints(failureEntries.map(([, reason]) => reason), { |
| | | subject, |
| | | masterDataHint, |
| | | }).map((hint) => h('div', { class: 'mt-2 text-red-500' }, hint)), |
| | | ]; |
| | | h( |
| | | 'div', |
| | | { |
| | | class: |
| | | 'mt-2 max-h-72 overflow-y-auto rounded border border-solid border-gray-200 px-3 py-2 text-sm leading-6', |
| | | }, |
| | | failureEntries.map(([name, reason]) => |
| | | h('div', { class: 'py-0.5' }, `${name}:${reason}`), |
| | | ), |
| | | ), |
| | | ...hints.map((hint) => |
| | | h('div', { class: 'mt-2 text-red-500' }, hint), |
| | | ), |
| | | ]); |
| | | |
| | | const notify = importedCount === 0 ? notification.error : notification.warning; |
| | | notify({ |
| | | message: |
| | | const openModal = importedCount === 0 ? Modal.error : Modal.warning; |
| | | openModal({ |
| | | title: |
| | | importedCount === 0 |
| | | ? `${subject}导入失败:${failureEntries.length} 行全部未导入` |
| | | : `导入完成,部分${subject}导入失败(成功 ${importedCount} 条 / 失败 ${failureEntries.length} 条)`, |
| | | description: () => h('div', nodes), |
| | | duration: null, |
| | | : `导入完成:成功 ${importedCount} 条,失败 ${failureEntries.length} 条`, |
| | | content, |
| | | width: 680, |
| | | centered: true, |
| | | okText: '知道了', |
| | | }); |
| | | return importedCount; |
| | | } |
| | |
| | | /** 按失败原因归纳下一步动作,避免用户只看到「失败」却不知道改哪里 */ |
| | | function buildFailureHints( |
| | | reasons: string[], |
| | | { subject, masterDataHint }: Pick<ImportResultNotifyOptions, 'masterDataHint' | 'subject'>, |
| | | { |
| | | subject, |
| | | masterDataHint, |
| | | }: Pick<ImportResultNotifyOptions, 'masterDataHint' | 'subject'>, |
| | | ): string[] { |
| | | const hints = new Set<string>(); |
| | | for (const reason of reasons) { |