mirror of
https://github.com/m1ngsama/FUJI.git
synced 2025-12-24 10:51:27 +00:00
add export dialog
This commit is contained in:
parent
6a99120e14
commit
74c95626c8
5 changed files with 132 additions and 16 deletions
|
|
@ -24,6 +24,7 @@
|
|||
"@fullcalendar/react": "^6.1.17",
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@heroui/react": "2.7.6",
|
||||
"@internationalized/date": "^3.8.1",
|
||||
"@logto/browser": "^2.2.18",
|
||||
"@react-stately/data": "^3.13.0",
|
||||
"@stylistic/eslint-plugin": "^2.13.0",
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ importers:
|
|||
'@heroui/react':
|
||||
specifier: 2.7.6
|
||||
version: 2.7.6(@types/react@18.3.20)(framer-motion@11.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwindcss@3.4.17)
|
||||
'@internationalized/date':
|
||||
specifier: ^3.8.1
|
||||
version: 3.8.1
|
||||
'@logto/browser':
|
||||
specifier: ^2.2.18
|
||||
version: 2.2.18
|
||||
|
|
@ -1468,8 +1471,8 @@ packages:
|
|||
'@internationalized/date@3.7.0':
|
||||
resolution: {integrity: sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==}
|
||||
|
||||
'@internationalized/date@3.8.0':
|
||||
resolution: {integrity: sha512-J51AJ0fEL68hE4CwGPa6E0PO6JDaVLd8aln48xFCSy7CZkZc96dGEGmLs2OEEbBxcsVZtfrqkXJwI2/MSG8yKw==}
|
||||
'@internationalized/date@3.8.1':
|
||||
resolution: {integrity: sha512-PgVE6B6eIZtzf9Gu5HvJxRK3ufUFz9DhspELuhW/N0GuMGMTLvPQNRkHP2hTuP9lblOk+f+1xi96sPiPXANXAA==}
|
||||
|
||||
'@internationalized/message@3.1.7':
|
||||
resolution: {integrity: sha512-gLQlhEW4iO7DEFPf/U7IrIdA3UyLGS0opeqouaFwlMObLUzwexRjbygONHDVbC9G9oFLXsLyGKYkJwqXw/QADg==}
|
||||
|
|
@ -7404,7 +7407,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@swc/helpers': 0.5.17
|
||||
|
||||
'@internationalized/date@3.8.0':
|
||||
'@internationalized/date@3.8.1':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.17
|
||||
|
||||
|
|
@ -7522,7 +7525,7 @@ snapshots:
|
|||
|
||||
'@react-aria/calendar@3.7.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.1
|
||||
'@react-aria/i18n': 3.12.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@react-aria/interactions': 3.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@react-aria/live-announcer': 3.4.2
|
||||
|
|
@ -7574,7 +7577,7 @@ snapshots:
|
|||
|
||||
'@react-aria/datepicker@3.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.1
|
||||
'@internationalized/number': 3.6.1
|
||||
'@internationalized/string': 3.2.6
|
||||
'@react-aria/focus': 3.20.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
|
@ -7666,7 +7669,7 @@ snapshots:
|
|||
|
||||
'@react-aria/i18n@3.12.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.8.0
|
||||
'@internationalized/date': 3.8.1
|
||||
'@internationalized/message': 3.1.7
|
||||
'@internationalized/number': 3.6.1
|
||||
'@internationalized/string': 3.2.6
|
||||
|
|
@ -7679,7 +7682,7 @@ snapshots:
|
|||
|
||||
'@react-aria/i18n@3.12.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.8.0
|
||||
'@internationalized/date': 3.8.1
|
||||
'@internationalized/message': 3.1.7
|
||||
'@internationalized/number': 3.6.1
|
||||
'@internationalized/string': 3.2.6
|
||||
|
|
@ -8115,7 +8118,7 @@ snapshots:
|
|||
|
||||
'@react-stately/calendar@3.7.1(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.1
|
||||
'@react-stately/utils': 3.10.5(react@18.3.1)
|
||||
'@react-types/calendar': 3.6.1(react@18.3.1)
|
||||
'@react-types/shared': 3.28.0(react@18.3.1)
|
||||
|
|
@ -8164,7 +8167,7 @@ snapshots:
|
|||
|
||||
'@react-stately/datepicker@3.13.0(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.1
|
||||
'@internationalized/string': 3.2.6
|
||||
'@react-stately/form': 3.1.3(react@18.3.1)
|
||||
'@react-stately/overlays': 3.6.15(react@18.3.1)
|
||||
|
|
@ -8400,13 +8403,13 @@ snapshots:
|
|||
|
||||
'@react-types/calendar@3.6.1(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.1
|
||||
'@react-types/shared': 3.28.0(react@18.3.1)
|
||||
react: 18.3.1
|
||||
|
||||
'@react-types/calendar@3.7.0(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.8.0
|
||||
'@internationalized/date': 3.8.1
|
||||
'@react-types/shared': 3.29.0(react@18.3.1)
|
||||
react: 18.3.1
|
||||
|
||||
|
|
@ -8427,7 +8430,7 @@ snapshots:
|
|||
|
||||
'@react-types/datepicker@3.11.0(react@18.3.1)':
|
||||
dependencies:
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/date': 3.8.1
|
||||
'@react-types/calendar': 3.7.0(react@18.3.1)
|
||||
'@react-types/overlays': 3.8.14(react@18.3.1)
|
||||
'@react-types/shared': 3.29.0(react@18.3.1)
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ export const EventActionCommit = (props: EventActionProps) => {
|
|||
variant="flat"
|
||||
color="primary"
|
||||
isLoading={props.isLoading === "commit"}
|
||||
onPress={() => onSubmit()}
|
||||
onPress={onSubmit}
|
||||
>
|
||||
提交
|
||||
</Button>
|
||||
|
|
@ -175,7 +175,7 @@ export const EventActionAlterCommit = (props: EventActionProps) => {
|
|||
<Button
|
||||
variant="flat"
|
||||
isLoading={props.isLoading === "commit"}
|
||||
onPress={() => onSubmit()}
|
||||
onPress={onSubmit}
|
||||
>
|
||||
修改提交
|
||||
</Button>
|
||||
|
|
|
|||
107
src/pages/repair/ExportEventDialog.tsx
Normal file
107
src/pages/repair/ExportEventDialog.tsx
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import { useState } from "react"
|
||||
import {
|
||||
Modal,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Button,
|
||||
DateRangePicker,
|
||||
} from "@heroui/react"
|
||||
import { parseDate } from "@internationalized/date"
|
||||
import { saturdayApiBaseUrl } from "../../utils/client"
|
||||
import { makeLogtoClient } from "../../utils/auth"
|
||||
|
||||
export function ExportExcelModal() {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [dateRange, setDateRange] = useState({
|
||||
start: parseDate("2025-01-01"),
|
||||
end: parseDate("2025-05-16"),
|
||||
})
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const openModal = () => setIsOpen(true)
|
||||
const closeModal = () => setIsOpen(false)
|
||||
|
||||
const downloadExcel = async () => {
|
||||
if (!dateRange.start || !dateRange.end) return
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
const start = dateRange.start.toString() // Format: 'YYYY-MM-DD'
|
||||
const end = dateRange.end.toString()
|
||||
const url = `${saturdayApiBaseUrl}/events/xlsx?start_time=${start}&end_time=${end}`
|
||||
|
||||
const token = await makeLogtoClient().getAccessToken()
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
if (!response.ok) throw new Error("Download failed")
|
||||
|
||||
// Extract filename from Content-Disposition header
|
||||
const disposition = response.headers.get("Content-Disposition")
|
||||
let filename = "export.xlsx" // Default filename
|
||||
if (disposition && disposition.includes("filename=")) {
|
||||
const filenameMatch = disposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)
|
||||
if (filenameMatch != null && filenameMatch[1]) {
|
||||
filename = filenameMatch[1].replace(/['"]/g, "")
|
||||
}
|
||||
}
|
||||
|
||||
const blob = await response.blob()
|
||||
const downloadUrl = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement("a")
|
||||
link.href = downloadUrl
|
||||
link.setAttribute("download", filename)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
link.remove()
|
||||
window.URL.revokeObjectURL(downloadUrl) // Clean up
|
||||
}
|
||||
catch (error) {
|
||||
alert("Failed to download Excel file: " + error.message)
|
||||
}
|
||||
finally {
|
||||
setLoading(false)
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onPress={openModal} color="primary">
|
||||
导出为Excel
|
||||
</Button>
|
||||
|
||||
<Modal isOpen={isOpen} onClose={closeModal}>
|
||||
<ModalContent>
|
||||
<ModalHeader>导出为Excel</ModalHeader>
|
||||
<ModalBody>
|
||||
<DateRangePicker
|
||||
label="选择日期范围"
|
||||
value={dateRange}
|
||||
onChange={setDateRange}
|
||||
granularity="day"
|
||||
visibleMonths={2}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="ghost" onClick={closeModal}>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={downloadExcel}
|
||||
isLoading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "导出中..." : "导出"}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ import { makeLogtoClient } from "../../utils/auth"
|
|||
import type { PublicMember } from "../../store/member"
|
||||
import type { UserInfoResponse } from "@logto/browser"
|
||||
import { getAvailableEventActions, type EventAction, type IdentityContext } from "./EventAction"
|
||||
import { ExportExcelModal } from "./ExportEventDialog"
|
||||
|
||||
type PublicEvent = components["schemas"]["PublicEvent"]
|
||||
|
||||
|
|
@ -385,11 +386,15 @@ export default function App() {
|
|||
return cellValue
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<section className="box-border mb-24">
|
||||
<div className="section-content mt-6">
|
||||
<div className="section-content mt-6 flex justify-between items-center">
|
||||
<h2 className="text-2xl font-bold">维修管理</h2>
|
||||
{
|
||||
userInfo?.roles?.find(v => v.toLowerCase() == "repair admin")
|
||||
? <ExportExcelModal></ExportExcelModal>
|
||||
: <></>
|
||||
}
|
||||
</div>
|
||||
<div className="section-content my-8 flex flex-col gap-4">
|
||||
<Table
|
||||
|
|
|
|||
Loading…
Reference in a new issue