fix: align Excel exports with asset records
This commit is contained in:
parent
c69fd7ce4f
commit
83fcfbca3e
93
backend/src/controllers/__tests__/borrowExport.test.ts
Normal file
93
backend/src/controllers/__tests__/borrowExport.test.ts
Normal file
@ -0,0 +1,93 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { PassThrough, Readable } from 'node:stream'
|
||||
import { finished } from 'node:stream/promises'
|
||||
import test from 'node:test'
|
||||
import ExcelJS from 'exceljs'
|
||||
import prisma from '../../utils/prisma'
|
||||
import { getBorrowRecordList } from '../borrowController'
|
||||
import { exportBorrowRecords } from '../exportController'
|
||||
|
||||
const readBorrowWorkbook = async (query: Record<string, string>) => {
|
||||
const stream = new PassThrough()
|
||||
const chunks: Buffer[] = []
|
||||
stream.on('data', chunk => chunks.push(Buffer.from(chunk)))
|
||||
let statusCode = 200
|
||||
let errorBody: unknown
|
||||
const response = Object.assign(stream, {
|
||||
setHeader() {},
|
||||
status(code: number) { statusCode = code; return this },
|
||||
json(value: unknown) { errorBody = value; stream.end() }
|
||||
})
|
||||
await exportBorrowRecords({ query } as any, response as any)
|
||||
await finished(stream)
|
||||
assert.equal(statusCode, 200, JSON.stringify(errorBody))
|
||||
const workbook = new ExcelJS.Workbook()
|
||||
await workbook.xlsx.read(Readable.from(chunks))
|
||||
return workbook.getWorksheet('借用记录')!
|
||||
}
|
||||
|
||||
test('借用列表与 Excel 对状态、人员(含当前工号)、资产关键词取交集,只有列表分页', async (t) => {
|
||||
const queries: any[] = []
|
||||
let countWhere: unknown
|
||||
const originalFindMany = prisma.borrowRecord.findMany
|
||||
const originalCount = prisma.borrowRecord.count
|
||||
;(prisma.borrowRecord.findMany as any) = async (args: unknown) => { queries.push(args); return [] }
|
||||
;(prisma.borrowRecord.count as any) = async ({ where }: any) => { countWhere = where; return 0 }
|
||||
t.after(() => { prisma.borrowRecord.findMany = originalFindMany; prisma.borrowRecord.count = originalCount })
|
||||
const query = { status: '已借出', person_id: '8', person_name: 'EMP-18', keyword: '主机', page: '2', pageSize: '5' }
|
||||
await getBorrowRecordList({ query } as any, {
|
||||
json() {}, status(code: number) { assert.fail(`列表意外返回 ${code}`) }
|
||||
} as any)
|
||||
await readBorrowWorkbook(query)
|
||||
const [list, excel] = queries
|
||||
assert.deepEqual(list.where, { AND: [
|
||||
{ status: '已借出' }, { person_id: 8 },
|
||||
{ OR: [
|
||||
{ person_name: { contains: 'EMP-18' } }, { person_nickname: { contains: 'EMP-18' } },
|
||||
{ person: { is: { job_number: { contains: 'EMP-18' } } } }
|
||||
] },
|
||||
{ OR: [{ equipment_code: { contains: '主机' } }, { equipment: { is: { equipment_name: { contains: '主机' } } } }] }
|
||||
] })
|
||||
assert.deepEqual(countWhere, list.where)
|
||||
assert.deepEqual(excel.where, list.where)
|
||||
assert.equal(list.skip, 5); assert.equal(list.take, 5)
|
||||
assert.equal(excel.skip, undefined); assert.equal(excel.take, undefined)
|
||||
|
||||
await readBorrowWorkbook({ ...query, start_date: '2026-11-01', end_date: '2026-11-09T16:00:00+08:00' })
|
||||
assert.deepEqual(queries[2].where.AND, [...list.where.AND, { borrow_time: {
|
||||
gte: new Date('2026-11-01T00:00:00Z'), lte: new Date('2026-11-09T08:00:00Z')
|
||||
} }])
|
||||
})
|
||||
|
||||
test('借用 Excel 在非上海运行时仍按北京时间显示跨日日期,保留长期和空日期', async (t) => {
|
||||
const previousTimezone = process.env.TZ
|
||||
process.env.TZ = 'America/Los_Angeles'
|
||||
const record = {
|
||||
equipment_code: 'JS-TEST-01', equipment: { equipment_name: '主机', unit_price: 100 },
|
||||
person_name: '测试', person_nickname: '员工', person: { job_number: 'EMP-18' },
|
||||
borrow_time: new Date('2026-11-08T16:01:02Z'),
|
||||
expected_return_time: new Date('2026-11-09T16:15:00Z'),
|
||||
return_time: new Date('2026-11-10T16:30:42Z'),
|
||||
is_long_term: false, status: '已归还', return_condition: '正常', children_snapshot: null
|
||||
}
|
||||
const originalFindMany = prisma.borrowRecord.findMany
|
||||
;(prisma.borrowRecord.findMany as any) = async () => [
|
||||
record,
|
||||
{ ...record, equipment_code: 'JS-TEST-02', is_long_term: true, return_time: null },
|
||||
{ ...record, equipment_code: 'JS-TEST-03', expected_return_time: null, return_time: null }
|
||||
]
|
||||
t.after(() => { prisma.borrowRecord.findMany = originalFindMany })
|
||||
try {
|
||||
const worksheet = await readBorrowWorkbook({})
|
||||
assert.equal(worksheet.getCell('G2').text, '2026-11-09 00:01:02')
|
||||
assert.equal(worksheet.getCell('H2').text, '2026-11-10')
|
||||
assert.equal(worksheet.getCell('I2').text, '2026-11-11 00:30:42')
|
||||
assert.equal(worksheet.getCell('H3').text, '长期')
|
||||
assert.equal(worksheet.getCell('I3').text, '')
|
||||
assert.equal(worksheet.getCell('H4').text, '')
|
||||
assert.equal(worksheet.getCell('I4').text, '')
|
||||
} finally {
|
||||
if (previousTimezone === undefined) delete process.env.TZ
|
||||
else process.env.TZ = previousTimezone
|
||||
}
|
||||
})
|
||||
@ -9,6 +9,7 @@ import {
|
||||
} from '../utils/assetValue'
|
||||
import { getBorrowAnomalyMeta } from '../utils/borrowAnomaly'
|
||||
import { assertReturnStorageDepartmentContract } from '../utils/borrowDepartment'
|
||||
import { buildBorrowRecordFilters } from '../utils/borrowRecordFilters'
|
||||
import {
|
||||
buildParentAssetFirstOrderBy,
|
||||
sortParentAssetsFirst
|
||||
@ -1065,43 +1066,8 @@ export const getBorrowRecordByCode = async (req: Request, res: Response) => {
|
||||
// 获取借用记录列表
|
||||
export const getBorrowRecordList = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { page = 1, pageSize = 10, status, person_name, person_id, keyword } = req.query
|
||||
|
||||
const filters: any[] = []
|
||||
|
||||
if (status) {
|
||||
filters.push({ status })
|
||||
}
|
||||
|
||||
const personIdNum = parseNumber(person_id)
|
||||
if (personIdNum) {
|
||||
filters.push({ person_id: personIdNum })
|
||||
}
|
||||
|
||||
if (person_name) {
|
||||
const kw = person_name as string
|
||||
filters.push({
|
||||
OR: [
|
||||
{ person_name: { contains: kw } },
|
||||
{ person_nickname: { contains: kw } }
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
if (keyword) {
|
||||
const kw = keyword as string
|
||||
filters.push({
|
||||
OR: [
|
||||
{ equipment_code: { contains: kw } },
|
||||
{
|
||||
equipment: {
|
||||
equipment_name: { contains: kw }
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
const { page = 1, pageSize = 10 } = req.query
|
||||
const filters = buildBorrowRecordFilters(req.query)
|
||||
const where = filters.length ? { AND: filters } : undefined
|
||||
const skip = (Number(page) - 1) * Number(pageSize)
|
||||
|
||||
|
||||
@ -8,8 +8,10 @@ import {
|
||||
summarizeEquipmentExportHierarchy
|
||||
} from '../utils/equipmentExportHierarchy'
|
||||
import {
|
||||
formatShanghaiExportDateTime,
|
||||
formatShanghaiExportFilenameTimestamp
|
||||
} from '../utils/equipmentExportMetadata'
|
||||
import { buildBorrowRecordFilters } from '../utils/borrowRecordFilters'
|
||||
import { calculateEquipmentExportValues } from '../utils/equipmentExportValues'
|
||||
import { buildEquipmentKeywordSearchConditions } from '../utils/equipmentKeywordSearch'
|
||||
import { buildSyncedBusinessDepartmentWhere } from '../utils/organization'
|
||||
@ -311,22 +313,15 @@ export const exportEquipment = async (req: Request, res: Response) => {
|
||||
// 导出借用记录
|
||||
export const exportBorrowRecords = async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { status, person_name, start_date, end_date } = req.query
|
||||
|
||||
const where: any = {}
|
||||
if (status) where.status = status
|
||||
if (person_name) {
|
||||
where.OR = [
|
||||
{ person_name: { contains: String(person_name) } },
|
||||
{ person_nickname: { contains: String(person_name) } },
|
||||
{ person: { is: { job_number: { contains: String(person_name) } } } }
|
||||
]
|
||||
}
|
||||
const { start_date, end_date } = req.query
|
||||
const filters = buildBorrowRecordFilters(req.query)
|
||||
if (start_date || end_date) {
|
||||
where.borrow_time = {}
|
||||
if (start_date) where.borrow_time.gte = new Date(String(start_date))
|
||||
if (end_date) where.borrow_time.lte = new Date(String(end_date))
|
||||
filters.push({ borrow_time: {
|
||||
gte: start_date ? new Date(String(start_date)) : undefined,
|
||||
lte: end_date ? new Date(String(end_date)) : undefined
|
||||
} })
|
||||
}
|
||||
const where = filters.length ? { AND: filters } : undefined
|
||||
|
||||
const records = await prisma.borrowRecord.findMany({
|
||||
where,
|
||||
@ -378,9 +373,10 @@ export const exportBorrowRecords = async (req: Request, res: Response) => {
|
||||
person_nickname: record.person_nickname,
|
||||
current_job_number: record.person?.job_number || '',
|
||||
department: record.person?.mainDepartment?.name || record.person?.department || '',
|
||||
borrow_time: record.borrow_time ? new Date(record.borrow_time).toLocaleString('zh-CN') : '',
|
||||
expected_return_time: record.is_long_term ? '长期' : (record.expected_return_time ? new Date(record.expected_return_time).toLocaleDateString('zh-CN') : ''),
|
||||
return_time: record.return_time ? new Date(record.return_time).toLocaleString('zh-CN') : '',
|
||||
borrow_time: record.borrow_time ? formatShanghaiExportDateTime(record.borrow_time) : '',
|
||||
expected_return_time: record.is_long_term ? '长期' :
|
||||
(record.expected_return_time ? formatShanghaiExportDateTime(record.expected_return_time).slice(0, 10) : ''),
|
||||
return_time: record.return_time ? formatShanghaiExportDateTime(record.return_time) : '',
|
||||
equipment_value: Number(record.equipment?.unit_price || 0) + sumSnapshotChildrenValue(record.children_snapshot),
|
||||
status: record.status,
|
||||
return_condition: record.return_condition,
|
||||
@ -388,7 +384,7 @@ export const exportBorrowRecords = async (req: Request, res: Response) => {
|
||||
})
|
||||
})
|
||||
|
||||
const timestamp = new Date().toISOString().slice(0, 10).replace(/-/g, '')
|
||||
const timestamp = formatShanghaiExportFilenameTimestamp(new Date()).slice(0, 8)
|
||||
const filename = `借用记录_${timestamp}.xlsx`
|
||||
|
||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||
|
||||
@ -351,6 +351,8 @@ test('工作簿应生成三张行政导出表并保留日期、金额和版式
|
||||
equipment_name: '财务电脑',
|
||||
equipment_type: '电脑',
|
||||
status: '可用',
|
||||
department: { name: '财务部' },
|
||||
storageLocation: { name: '财务库' },
|
||||
purchase_date: '2026-03-04T00:00:00.000Z',
|
||||
unit_price: '6000.5',
|
||||
total_value: 6000.5,
|
||||
@ -361,7 +363,17 @@ test('工作簿应生成三张行政导出表并保留日期、金额和版式
|
||||
equipment_name: '内存',
|
||||
equipment_type: '内存条',
|
||||
equipment_model: '32GB',
|
||||
total_value: 300
|
||||
total_value: 300,
|
||||
department: { name: '设备部' },
|
||||
storageLocation: { name: '配件库' }
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
equipment_code: 'PART-SSD',
|
||||
equipment_name: '固态硬盘',
|
||||
equipment_type: '固态硬盘',
|
||||
department: null,
|
||||
storageLocation: null
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -389,7 +401,13 @@ test('工作簿应生成三张行政导出表并保留日期、金额和版式
|
||||
assert.equal(mainSheet.getCell('H2').border.top?.color?.argb, 'FFD1D5DB')
|
||||
assert.equal(mainSheet.getCell('S2').border.right?.style, 'thin')
|
||||
assert.strictEqual(mainSheet.getCell('A1').border, mainSheet.getCell('S2').border)
|
||||
assert.strictEqual(mainSheet.getCell('A1').border, relationSheet.getCell('J2').border)
|
||||
assert.strictEqual(mainSheet.getCell('A1').border, relationSheet.getCell('L2').border)
|
||||
assert.equal(relationSheet.getCell('K1').value, '子资产部门')
|
||||
assert.equal(relationSheet.getCell('L1').value, '子资产原库位')
|
||||
assert.equal(relationSheet.getCell('K2').value, '设备部')
|
||||
assert.equal(relationSheet.getCell('L2').value, '配件库')
|
||||
assert.equal(relationSheet.getCell('K3').value, '公共库存')
|
||||
assert.equal(relationSheet.getCell('L3').value, null)
|
||||
assert.equal(mainSheet.getCell('J2').alignment.wrapText, true)
|
||||
assert.equal(mainSheet.getCell('F2').value, null)
|
||||
assert.equal(mainSheet.getCell('L2').value, null)
|
||||
|
||||
30
backend/src/utils/borrowRecordFilters.ts
Normal file
30
backend/src/utils/borrowRecordFilters.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import type { Prisma } from '@prisma/client'
|
||||
|
||||
export const buildBorrowRecordFilters = (query: {
|
||||
status?: unknown
|
||||
person_id?: unknown
|
||||
person_name?: unknown
|
||||
keyword?: unknown
|
||||
}): Prisma.BorrowRecordWhereInput[] => {
|
||||
const { status, person_id, person_name, keyword } = query
|
||||
const filters: Prisma.BorrowRecordWhereInput[] = []
|
||||
if (typeof status === 'string' && status) filters.push({ status })
|
||||
|
||||
const personId = typeof person_id === 'string' || typeof person_id === 'number' ? Number(person_id) : 0
|
||||
if (Number.isSafeInteger(personId) && personId > 0) filters.push({ person_id: personId })
|
||||
|
||||
if (typeof person_name === 'string' && person_name) filters.push({
|
||||
OR: [
|
||||
{ person_name: { contains: person_name } },
|
||||
{ person_nickname: { contains: person_name } },
|
||||
{ person: { is: { job_number: { contains: person_name } } } }
|
||||
]
|
||||
})
|
||||
if (typeof keyword === 'string' && keyword) filters.push({
|
||||
OR: [
|
||||
{ equipment_code: { contains: keyword } },
|
||||
{ equipment: { is: { equipment_name: { contains: keyword } } } }
|
||||
]
|
||||
})
|
||||
return filters
|
||||
}
|
||||
@ -38,6 +38,8 @@ export interface EquipmentAdministrativeRelationRow {
|
||||
child_equipment_type: string
|
||||
child_equipment_model: string
|
||||
child_own_total_value: number
|
||||
child_department: string
|
||||
child_storage_location: string
|
||||
}
|
||||
|
||||
const HARDWARE_TYPE_ALIASES = new Map<string, AdministrativeHardwareSlot>([
|
||||
@ -159,7 +161,9 @@ export const buildEquipmentAdministrativeRows = (
|
||||
child_equipment_name: child.equipment_name,
|
||||
child_equipment_type: child.assetType?.name || child.equipment_type || '',
|
||||
child_equipment_model: child.equipment_model || '',
|
||||
child_own_total_value: toSafeNumber(child.total_value)
|
||||
child_own_total_value: toSafeNumber(child.total_value),
|
||||
child_department: child.department?.name || '公共库存',
|
||||
child_storage_location: child.storageLocation?.name || ''
|
||||
})
|
||||
})
|
||||
|
||||
@ -327,7 +331,9 @@ export const buildAdministrativeEquipmentWorkbook = (
|
||||
{ header: '子资产名称', key: 'child_equipment_name', width: 24 },
|
||||
{ header: '子资产类型', key: 'child_equipment_type', width: 16 },
|
||||
{ header: '子资产型号', key: 'child_equipment_model', width: 24 },
|
||||
{ header: '子资产本体价值', key: 'child_own_total_value', width: 18 }
|
||||
{ header: '子资产本体价值', key: 'child_own_total_value', width: 18 },
|
||||
{ header: '子资产部门', key: 'child_department', width: 18 },
|
||||
{ header: '子资产原库位', key: 'child_storage_location', width: 24 }
|
||||
]
|
||||
applyHeaderStyle(relationWorksheet)
|
||||
relationWorksheet.addRows(
|
||||
|
||||
@ -1,4 +1,14 @@
|
||||
const SHANGHAI_TIME_ZONE = 'Asia/Shanghai'
|
||||
const shanghaiDateTimeFormatter = new Intl.DateTimeFormat('en-CA', {
|
||||
timeZone: SHANGHAI_TIME_ZONE,
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hourCycle: 'h23'
|
||||
})
|
||||
|
||||
type ShanghaiDateTimeParts = {
|
||||
year: string
|
||||
@ -14,16 +24,7 @@ const getShanghaiDateTimeParts = (date: Date): ShanghaiDateTimeParts => {
|
||||
throw new Error('导出时间无效')
|
||||
}
|
||||
|
||||
const parts = new Intl.DateTimeFormat('en-CA', {
|
||||
timeZone: SHANGHAI_TIME_ZONE,
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hourCycle: 'h23'
|
||||
}).formatToParts(date)
|
||||
const parts = shanghaiDateTimeFormatter.formatToParts(date)
|
||||
const partMap = new Map(parts.map((part) => [part.type, part.value]))
|
||||
|
||||
return {
|
||||
@ -55,7 +56,7 @@ export const buildEquipmentExportDescriptionRows = (exportedAt: Date) => [
|
||||
{
|
||||
item: '关系口径',
|
||||
description:
|
||||
'按导出时同一次只读事务快照中的当前 parent_id / children 父子关系生成,不包含历史挂载记录。'
|
||||
'按导出时同一次只读事务快照中的当前 parent_id / children 父子关系生成,不包含历史挂载记录。子资产部门和原库位来自该配件的台账,未指定部门显示为公共库存,未登记库位留空。'
|
||||
},
|
||||
{
|
||||
item: '时效提醒',
|
||||
|
||||
@ -27,6 +27,7 @@ export const exportEquipment = (params?: {
|
||||
export const exportBorrowRecords = (params?: {
|
||||
status?: string
|
||||
person_name?: string
|
||||
keyword?: string
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
}) => {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<router-link to="/borrow/scan" class="btn-primary text-sm">扫码出库</router-link>
|
||||
<router-link to="/return/scan" class="btn-secondary text-sm">扫码入库</router-link>
|
||||
<button class="btn-secondary text-sm" :disabled="exporting" @click="handleExport">
|
||||
<button class="btn-secondary text-sm" :disabled="exporting || !appliedFilters" @click="handleExport">
|
||||
{{ exporting ? '导出中...' : '导出' }}
|
||||
</button>
|
||||
</div>
|
||||
@ -173,9 +173,9 @@
|
||||
<div class="pagination-bar">
|
||||
<p class="text-sm text-text-secondary">共 {{ formatNumber(total) }} 条记录</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<button @click="handlePrevPage" :disabled="currentPage === 1" class="btn-secondary text-sm">上一页</button>
|
||||
<button @click="handlePrevPage" :disabled="loading || currentPage === 1" class="btn-secondary text-sm">上一页</button>
|
||||
<span class="text-sm text-text-primary">{{ currentPage }} / {{ totalPages }}</span>
|
||||
<button @click="handleNextPage" :disabled="currentPage >= totalPages" class="btn-secondary text-sm">下一页</button>
|
||||
<button @click="handleNextPage" :disabled="loading || currentPage >= totalPages" class="btn-secondary text-sm">下一页</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -218,6 +218,9 @@ import { getBorrowRecordList } from '@/api/borrow'
|
||||
import { downloadFile, exportBorrowRecords } from '@/api/export'
|
||||
import type { BorrowRecord } from '@/types'
|
||||
import { formatDateTime, formatNumber } from '@/utils/format'
|
||||
import { createRequestScope } from '@/utils/requestScope'
|
||||
|
||||
type BorrowFilters = Pick<Parameters<typeof getBorrowRecordList>[0], 'status' | 'person_name' | 'keyword'>
|
||||
|
||||
const borrowRecords = ref<BorrowRecord[]>([])
|
||||
const loading = ref(false)
|
||||
@ -231,6 +234,8 @@ const filters = reactive({
|
||||
person_name: '',
|
||||
keyword: ''
|
||||
})
|
||||
const appliedFilters = ref<BorrowFilters | null>(null)
|
||||
const listRequests = createRequestScope()
|
||||
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
|
||||
|
||||
@ -241,30 +246,34 @@ const pageSummary = computed(() => {
|
||||
return { borrowed, returned, longTerm }
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
const fetchData = async (page: number, requestFilters: BorrowFilters = appliedFilters.value ?? {}) => {
|
||||
const request = listRequests.begin()
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getBorrowRecordList({
|
||||
page: currentPage.value,
|
||||
page,
|
||||
pageSize: pageSize.value,
|
||||
status: filters.status || undefined,
|
||||
person_name: filters.person_name || undefined,
|
||||
keyword: filters.keyword || undefined
|
||||
...requestFilters
|
||||
})
|
||||
if (res.data) {
|
||||
if (listRequests.isCurrent(request) && res.data) {
|
||||
borrowRecords.value = res.data.list
|
||||
total.value = res.data.total
|
||||
currentPage.value = page
|
||||
appliedFilters.value = requestFilters
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取借用记录失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
if (listRequests.isCurrent(request)) loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
currentPage.value = 1
|
||||
fetchData()
|
||||
fetchData(1, {
|
||||
status: filters.status || undefined,
|
||||
person_name: filters.person_name || undefined,
|
||||
keyword: filters.keyword || undefined
|
||||
})
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
@ -275,12 +284,10 @@ const handleReset = () => {
|
||||
}
|
||||
|
||||
const handleExport = async () => {
|
||||
if (exporting.value || !appliedFilters.value) return
|
||||
exporting.value = true
|
||||
try {
|
||||
const response = await exportBorrowRecords({
|
||||
status: filters.status || undefined,
|
||||
person_name: filters.person_name || undefined
|
||||
})
|
||||
const response = await exportBorrowRecords(appliedFilters.value)
|
||||
downloadFile(response.data as Blob, '借用记录.xlsx')
|
||||
} catch (error) {
|
||||
console.error('导出借用记录失败:', error)
|
||||
@ -290,16 +297,14 @@ const handleExport = async () => {
|
||||
}
|
||||
|
||||
const handlePrevPage = () => {
|
||||
if (currentPage.value > 1) {
|
||||
currentPage.value--
|
||||
fetchData()
|
||||
if (!loading.value && currentPage.value > 1) {
|
||||
fetchData(currentPage.value - 1)
|
||||
}
|
||||
}
|
||||
|
||||
const handleNextPage = () => {
|
||||
if (currentPage.value < totalPages.value) {
|
||||
currentPage.value++
|
||||
fetchData()
|
||||
if (!loading.value && currentPage.value < totalPages.value) {
|
||||
fetchData(currentPage.value + 1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,6 +318,6 @@ const getStatusBadgeClass = (status: string) => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
fetchData(1)
|
||||
})
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user