This commit is contained in:
2026-09-15 09:58:15 +08:00
parent 3f998bdb88
commit 4bf66d9571
90 changed files with 1599 additions and 1388 deletions
@@ -28,13 +28,14 @@ describe('BatchImportDialog', () => {
vi.unstubAllGlobals()
})
it('uses the shared two-step flow and submits a validated XLSX file', async () => {
it.each([false, true])('uses the shared two-step flow and submits a validated XLSX file (dense: %s)', async (dense) => {
vi.stubGlobal('ResizeObserver', ResizeObserverStub)
const result: BatchImportResult = {
fileName: 'partners.xlsx', totalRows: 2, importedRows: 2, success: true, errors: [],
}
const importFile = vi.fn().mockResolvedValue(result)
const wrapper = mount(BatchImportDialog, {
attrs: { class: dense ? 'admin-density-dialog' : undefined },
attachTo: document.body,
props: {
modelValue: true,
@@ -48,6 +49,7 @@ describe('BatchImportDialog', () => {
})
await flushPromises()
expect(document.querySelector('.batch-import-dialog')?.classList.contains('admin-density-dialog')).toBe(dense)
expect(document.body.textContent).toContain('下载标准模板')
expect(document.body.textContent).toContain('选择填写后的文件')
expect(button('开始导入')?.hasAttribute('disabled')).toBe(true)
+13 -13
View File
@@ -211,27 +211,27 @@ function downloadErrors() {
</template>
<style scoped>
.batch-import-flow { display: grid; gap: 16px; }
.batch-import-step { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 16px; border: 1px solid var(--cc-border); border-radius: 8px; background: #fafbfc; }
.batch-import-step__heading { display: flex; min-width: 0; align-items: flex-start; gap: 12px; }
.batch-import-step__heading > span { display: grid; width: 24px; height: 24px; flex: 0 0 24px; place-items: center; border-radius: 50%; background: var(--cc-primary); color: #fff; font-size: 12px; font-weight: 700; }
.batch-import-step__heading strong, .batch-import-errors__heading strong { color: var(--cc-text); font-size: 14px; }
.batch-import-step__heading p, .batch-import-errors__heading p { margin: 4px 0 0; color: var(--cc-muted); font-size: 12px; line-height: 1.55; }
.batch-import-file { display: inline-flex; min-width: 190px; max-width: 280px; align-items: center; justify-content: center; gap: 8px; padding: 9px 14px; border: 1px solid var(--cc-border); border-radius: 6px; background: #fff; color: var(--cc-primary); cursor: pointer; }
.batch-import-flow { display: grid; gap: var(--admin-space-5, 16px); }
.batch-import-step { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-6, 20px); padding: var(--admin-space-5, 16px); border: 1px solid var(--cc-border); border-radius: var(--admin-radius, 8px); background: #fafbfc; }
.batch-import-step__heading { display: flex; min-width: 0; align-items: flex-start; gap: var(--admin-space-5, 12px); }
.batch-import-step__heading > span { display: grid; width: 24px; height: 24px; flex: 0 0 24px; place-items: center; border-radius: 50%; background: var(--cc-primary); color: #fff; font-size: var(--admin-font-caption, 12px); font-weight: 700; }
.batch-import-step__heading strong, .batch-import-errors__heading strong { color: var(--cc-text); font-size: var(--admin-font-section, 14px); }
.batch-import-step__heading p, .batch-import-errors__heading p { margin: var(--admin-space-1, 4px) 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); line-height: 1.55; }
.batch-import-file { display: inline-flex; min-width: 190px; max-width: 280px; align-items: center; justify-content: center; gap: var(--admin-space-3, 8px); padding: var(--admin-space-2, 9px) var(--admin-space-5, 14px); border: 1px solid var(--cc-border); border-radius: var(--admin-radius, 6px); background: #fff; color: var(--cc-primary); cursor: pointer; }
.batch-import-file:hover { border-color: var(--cc-primary); }
.batch-import-file:focus-within { outline: 2px solid color-mix(in srgb, var(--cc-primary) 28%, transparent); outline-offset: 2px; }
.batch-import-file.disabled { cursor: not-allowed; opacity: .6; }
.batch-import-file input { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.batch-import-file span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.batch-import-flow :deep(.el-alert p) { margin: 5px 0 0; }
.batch-import-errors { overflow: hidden; border: 1px solid #f4c7c7; border-radius: 8px; }
.batch-import-errors__heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px 16px; background: #fff5f5; }
.batch-import-flow :deep(.el-alert p) { margin: var(--admin-space-1, 5px) 0 0; }
.batch-import-errors { overflow: hidden; border: 1px solid #f4c7c7; border-radius: var(--admin-radius, 8px); }
.batch-import-errors__heading { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-5, 16px); padding: var(--admin-space-5, 14px) var(--admin-space-5, 16px); background: #fff5f5; }
.batch-import-errors__list { max-height: 220px; overflow: auto; }
.batch-import-error { display: grid; grid-template-columns: 72px 130px minmax(0, 1fr); gap: 10px; padding: 10px 16px; border-top: 1px solid #f4dddd; font-size: 12px; }
.batch-import-error { display: grid; grid-template-columns: 72px 130px minmax(0, 1fr); gap: var(--admin-space-4, 10px); padding: var(--admin-space-4, 10px) var(--admin-space-5, 16px); border-top: 1px solid #f4dddd; font-size: var(--admin-font-caption, 12px); }
.batch-import-error span { color: #b42318; font-weight: 600; }
.batch-import-error p { margin: 0; overflow-wrap: anywhere; }
.batch-import-errors__overflow { margin: 0; padding: 10px 16px; border-top: 1px solid #f4dddd; color: var(--cc-muted); font-size: 12px; }
.batch-import-footer { display: flex; justify-content: flex-end; gap: 10px; }
.batch-import-errors__overflow { margin: 0; padding: var(--admin-space-4, 10px) var(--admin-space-5, 16px); border-top: 1px solid #f4dddd; color: var(--cc-muted); font-size: var(--admin-font-caption, 12px); }
.batch-import-footer { display: flex; justify-content: flex-end; gap: var(--admin-space-4, 10px); }
.batch-import-footer :deep(.el-button + .el-button) { margin-left: 0; }
@media (max-width: 720px) {
@@ -16,6 +16,12 @@ describe('showExportTaskCreatedDialog', () => {
)
})
it.each([false, true])('opts into the shared dialog style explicitly: %s', async (dense) => {
const confirm = vi.spyOn(ElMessageBox, 'confirm').mockResolvedValue({} as never)
await showExportTaskCreatedDialog({ taskName: '财务导出', dense })
expect(confirm).toHaveBeenCalledWith(expect.any(String), '导出任务已创建', expect.objectContaining({ customClass: dense ? 'admin-density-confirm' : undefined }))
})
it('keeps the user on the current page when the dialog is dismissed', async () => {
vi.spyOn(ElMessageBox, 'confirm').mockRejectedValue('cancel')
@@ -3,6 +3,7 @@ import { ElMessageBox } from 'element-plus'
export interface ExportTaskCreatedDialogOptions {
taskName: string
taskNo?: string | null
dense?: boolean
}
export async function showExportTaskCreatedDialog(options: ExportTaskCreatedDialogOptions) {
@@ -14,6 +15,7 @@ export async function showExportTaskCreatedDialog(options: ExportTaskCreatedDial
'导出任务已创建',
{
type: 'success',
customClass: options.dense ? 'admin-density-confirm' : undefined,
confirmButtonText: '前往下载中心',
cancelButtonText: '留在当前页',
distinguishCancelAndClose: true,