From 59427a1a3ab850c6d78810a60f0d12f29e8ca7f5 Mon Sep 17 00:00:00 2001 From: Ycc Date: Thu, 19 Sep 2024 17:05:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AF=BC=E5=87=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E9=87=8D=E5=A4=8D=E6=B7=BB=E5=8A=A0=E8=B4=B9?= =?UTF-8?q?=E7=94=A8=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +- src/components/Data.jsx | 94 +++++++++++++++++++++++++++++++++ src/views/airticket/Index.jsx | 14 +++-- src/views/airticket/Invoice.jsx | 5 +- src/views/airticket/Plan.jsx | 80 ++++++++++++++++++++-------- 5 files changed, 167 insertions(+), 29 deletions(-) create mode 100644 src/components/Data.jsx diff --git a/package.json b/package.json index 6848b2c..0a0dd25 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "react-i18next": "^14.1.2", "react-router-dom": "^6.10.0", "react-to-pdf": "^1.0.1", - "zustand": "^4.5.2" + "zustand": "^4.5.2", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.18.11/xlsx-0.18.11.tgz" }, "devDependencies": { "@types/react": "^18.0.28", diff --git a/src/components/Data.jsx b/src/components/Data.jsx new file mode 100644 index 0000000..d067a63 --- /dev/null +++ b/src/components/Data.jsx @@ -0,0 +1,94 @@ +import React, { useState, useEffect } from "react"; +import { Tag, Button, message } from 'antd'; +import { CaretUpOutlined, CaretDownOutlined, DownloadOutlined } from '@ant-design/icons'; +import { utils, writeFile } from "xlsx"; +import { isEmpty, getNestedValue } from "../utils/commons"; + +/** + * @property diffPercent + * @property diffData + * @property data1 + * @property data2 + */ +export const VSTag = (props) => { + const { diffPercent, diffData, data1, data2 } = props; + const CaretIcon = parseInt(diffPercent) < 0 ? CaretDownOutlined : CaretUpOutlined; + const tagColor = parseInt(diffPercent) < 0 ? 'gold' : 'lime'; + return parseInt(diffPercent) === 0 ? ( + '-' + ) : ( + + {/*
+ {data1} vs {data2} +
*/} + } color={tagColor}> + {diffPercent}%{' '}{diffData} + +
+ ); +}; + +/** + * 导出表格数据存为xlsx + */ +export const TableExportBtn = (props) => { + const output_name = `${props.label}`; + const [columnsMap, setColumnsMap] = useState([]); + const [summaryRow, setSummaryRow] = useState({}); + useEffect(() => { + const r1 = props.columns.reduce((r, v) => ({ + ...r, + ...(v.children ? v.children.reduce((rc, vc, ci) => ({ + ...rc, + ...(vc?.titleX ? {[`${v?.titleX || v.title},${vc.titleX}`]: vc.titleX } : {[(v?.titleX || v.title) + (ci || '')]: `${vc?.titleX || vc?.title || ''}`}), + }), {}) : {}) + }), {}); + const flatCols = props.columns.flatMap((v, k) => + v.children ? v.children.map((vc, ci) => ({ ...vc, title: `${v?.titleX || v.title}` + (vc?.titleX ? `,${vc.titleX}` : (ci || '')) })) : {...v, title: `${v?.titleX || v.title}`} + ); + // .filter((c) => c.dataIndex) + // !['string', 'number'].includes(typeof vc.title) ? `${v?.titleX || v.title}` : `${v?.titleX || v.title}-${vc.title || ''}` + ; + setColumnsMap(flatCols); + // console.log('flatCols', flatCols); + + setSummaryRow(r1); + // console.log('summaryRow', r1); + + return () => {}; + }, [props.columns]); + + const onExport = () => { + if (isEmpty(props.dataSource)) { + message.warning('无结果.'); + return false; + } + const data = props.dataSource.map((item) => { + const itemMapped = columnsMap.reduce((sv, kset) => { + const render_val = typeof kset?.render === 'function' ? kset.render('', item) : null; + const data_val = kset?.dataIndex ? (Array.isArray(kset.dataIndex) ? getNestedValue(item, kset.dataIndex) : item[kset.dataIndex]) : undefined; + const x_val = item[`${kset.dataIndex}_X`]; + // const _title = kset.title.replace('-[object Object]', ''); + const v = { [kset.title]: x_val || data_val || render_val }; + return { ...sv, ...v }; + }, {}); + return itemMapped; + }); + const ws = utils.json_to_sheet([].concat(isEmpty(summaryRow) ? [] : [summaryRow], data), { header: columnsMap.filter((r) => r.dataIndex).map((r) => r.title) }); + const wb = utils.book_new(); + utils.book_append_sheet(wb, ws, 'sheet'); + writeFile(wb, `${output_name}.xlsx`); + }; + + return ( + + ); +}; diff --git a/src/views/airticket/Index.jsx b/src/views/airticket/Index.jsx index 16c4287..334207e 100644 --- a/src/views/airticket/Index.jsx +++ b/src/views/airticket/Index.jsx @@ -1,11 +1,12 @@ import { useState, useEffect } from "react"; import { Grid, Divider, Layout, Spin, Input, Col, Row, Space, List, Table, Button } from "antd"; -import { PhoneOutlined, CustomerServiceOutlined, AudioOutlined,AuditOutlined } from "@ant-design/icons"; +import { PhoneOutlined, CustomerServiceOutlined, AudioOutlined, AuditOutlined } from "@ant-design/icons"; import { useParams, useHref, useNavigate, NavLink } from "react-router-dom"; import { isEmpty, formatColonTime } from "@/utils/commons"; import dayjs from "dayjs"; import SearchForm from "@/components/SearchForm"; - +import { DATE_FORMAT } from "@/config"; +import { TableExportBtn } from "@/components/Data"; import airTicketStore from "@/stores/Airticket"; import { usingStorage } from "@/hooks/usingStorage"; @@ -89,7 +90,9 @@ const Airticket = props => { const [getPlanList, planList, loading] = airTicketStore(state => [state.getPlanList, state.planList, state.loading]); const showTotal = total => `合计 ${total} `; - useEffect(() => {}, []); + useEffect(() => { + getPlanList(travelAgencyId, "", dayjs().startOf("M").format(DATE_FORMAT), dayjs().endOf("M").format(DATE_FORMAT)); + }, []); return ( @@ -103,7 +106,7 @@ const Airticket = props => { shows: ["referenceNo", "dates"], fieldProps: { referenceNo: { label: "搜索计划" }, - dates: { label: "出发日期" ,col:8}, + dates: { label: "出发日期", col: 8 }, }, }} onSubmit={(err, formVal, filedsVal) => { @@ -112,7 +115,7 @@ const Airticket = props => { /> - @@ -121,6 +124,7 @@ const Airticket = props => { + diff --git a/src/views/airticket/Invoice.jsx b/src/views/airticket/Invoice.jsx index 254a752..8fa5f30 100644 --- a/src/views/airticket/Invoice.jsx +++ b/src/views/airticket/Invoice.jsx @@ -6,6 +6,7 @@ import { isEmpty, formatColonTime } from "@/utils/commons"; import dayjs from "dayjs"; import SearchForm from "@/components/SearchForm"; import BackBtn from "@/components/BackBtn"; +import { TableExportBtn } from "@/components/Data"; import airTicketStore from "@/stores/Airticket"; import { usingStorage } from "@/hooks/usingStorage"; @@ -82,7 +83,7 @@ const Invoice = props => { key: "Cost", }, { - title: "手续费", + title: "服务费", children: [ { title: vEIFlightBill && vEIFlightBill.reduce((acc, curr) => acc + curr.ServiceFee, 0), @@ -199,7 +200,9 @@ const Invoice = props => {
+ + diff --git a/src/views/airticket/Plan.jsx b/src/views/airticket/Plan.jsx index 254f519..4635de9 100644 --- a/src/views/airticket/Plan.jsx +++ b/src/views/airticket/Plan.jsx @@ -80,7 +80,7 @@ const AirticketPlan = props => { render: (text, record) => (record.CostType == "出票" ? text : "-"), }, { - title: "手续费", + title: "服务费", key: "ServiceFee", dataIndex: "ServiceFee", }, @@ -131,7 +131,7 @@ const AirticketPlan = props => { wrapperCol={{ span: 16, }} - initialValues={{...airInfo,StartDate:dayjs(airInfo.StartDate)}} + initialValues={{ ...airInfo, StartDate: dayjs(airInfo.StartDate) }} onFinish={values => { postFlightDetail(airInfo.CLF_SN, airInfo.GRI_SN, airInfo.VEI_SN, airInfo, values) .then(() => { @@ -308,17 +308,21 @@ const AirticketPlan = props => { const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen_confirmInfo, setisModalOpen_confirmInfo] = useState(false); const [isTicketType, setisTicketType] = useState(true); + const [isAddNew, setisAddNew] = useState(true); //是否是在新增费用信息 const [ticket_form] = Form.useForm(); const [confirmInfo_form] = Form.useForm(); const showModal = ticket => { setIsModalOpen(true); + ticket_form.resetFields(); if (isEmpty(ticket.CostType)) ticket.CostType = "出票"; ticket.CostType == "出票" ? setisTicketType(true) : setisTicketType(false); //如果是出票类型,显示票号、折扣等选项 + isEmpty(ticket.CLC_SN) ? setisAddNew(true) : setisAddNew(false); //如果是新增窗口 ticket_form.setFieldsValue(ticket); + if (isEmpty(ticket.Memo)) ticket_form.setFieldsValue({ Memo: "" }); }; - const handleOk = () => { + const handleOk = (close_modal = true) => { ticket_form .validateFields() .then(values => { @@ -344,8 +348,7 @@ const AirticketPlan = props => { icon: , }); }); - ticket_form.resetFields(); - setIsModalOpen(false); + if (close_modal) setIsModalOpen(false); }) .catch(info => { console.log("Validate Failed:", info); @@ -391,7 +394,30 @@ const AirticketPlan = props => { const TicketModal = () => { return ( <> - + ( + <> + + {isAddNew ? ( + <> + {" "} + + + ) : ( + + )} + + )}>
{ ]} /> - - - + {isTicketType && ( <> - + - - + + - + { }} /> - + + )} + + + + {isTicketType && ( + <> + { /> - guestList_OnChange(value)} options={guestList_select()} placeholder="如果列表里面没有客人信息,请手动录到备注里" /> )} +