diff --git a/src/components/TemplateLetter2025/ExportDocxBtn.jsx b/src/components/TemplateLetter2025/ExportDocxBtn.jsx index 1579505..c84057f 100644 --- a/src/components/TemplateLetter2025/ExportDocxBtn.jsx +++ b/src/components/TemplateLetter2025/ExportDocxBtn.jsx @@ -1,60 +1,16 @@ import { Button } from 'antd'; -// import { useProductsAuditStatesMapVal, useProductsTypesMapVal } from '@/hooks/useProductsSets'; -// import { useTranslation } from 'react-i18next'; -// import useProductsStore, { getAgencyAllExtrasAction } from '@/stores/Products/Index'; -// import RequireAuth from '@/components/RequireAuth'; -// import { PERM_PRODUCTS_OFFER_AUDIT } from '@/config'; -// import dayjs from 'dayjs'; +import { exportDoc } from './Index'; -// import AgencyContract from '../Print/AgencyContract'; -import { saveAs } from 'file-saver'; -import { Packer } from 'docx'; -// import { isEmpty } from '@/utils/commons'; - -const ExportDocxBtn = ({ ...props }) => { - // const { t } = useTranslation(); - // const [agencyProducts] = useProductsStore((state) => [state.agencyProducts]); - // const [activeAgency] = useProductsStore((state) => [state.activeAgency]); - // const { travel_agency_id, use_year, audit_state } = params; - - // const auditStatesMap = useProductsAuditStatesMapVal(); - // const productsTypesMapVal = useProductsTypesMapVal(); - - // const { getRemarkList } = useProductsStore((selector) => ({ - // getRemarkList: selector.getRemarkList, - // })); - // const handleDownload = async () => { - // // await refresh(); - // const _agencyExtras = await getAgencyAllExtrasAction(params); - // const agencyExtras = Object.keys(_agencyExtras).reduce((acc, pid) => { - // const pitemExtras = _agencyExtras[pid]; - // const _pitem = (pitemExtras || []).map(eitem => ({ ...eitem, info: { ...eitem.info, product_type_name_txt: productsTypesMapVal[eitem.info.product_type_id]?.label || eitem.info.product_type_name } } )); - // return { ...acc, [pid]: _pitem }; - // }, {}); - // const remarks = await getRemarkList(); - // const remarksMappedByType = remarks.reduce((r, v) => ({ ...r, [v.product_type_id]: v }), {}); - // const documentCreator = new AgencyContract(); - // const doc = documentCreator.create([ - // params, - // activeAgency, - // agencyProducts, - // agencyExtras, - // // remarks, - // remarksMappedByType, - // ]); - - // const _d = dayjs().format('YYYYMMDD_HH.mm.ss.SSS'); // Date.now().toString(32) - // // console.log(params); - // const _state = isEmpty(audit_state) ? '' : auditStatesMap[audit_state].label; - // Packer.toBlob(doc).then((blob) => { - // saveAs(blob, `${activeAgency.travel_agency_name}${use_year}年地接合同-${_state}-${_d}.docx`); - // }); - // }; +const ExportDocxBtn = ({ subject, title, sectionsData, ...props }) => { return ( <> - + > ); }; diff --git a/src/components/TemplateLetter2025/Index.jsx b/src/components/TemplateLetter2025/Index.jsx index 1595ce6..d7fdf2f 100644 --- a/src/components/TemplateLetter2025/Index.jsx +++ b/src/components/TemplateLetter2025/Index.jsx @@ -220,7 +220,7 @@ const createHeaderRight = () => ], }); // Template function -const createDoc = async (agencyName, title, sectionsData) => { +const createDoc = async ({title, subject, sectionsData}) => { const logoArrayBuffer = await getLogoArrayBuffer(logoPath); const image = new docx.ImageRun({ @@ -475,13 +475,14 @@ const createDoc = async (agencyName, title, sectionsData) => { }), }, children: [ - createTitle(agencyName), + createTitle(title), new docx.TableOfContents('toc', { hyperlink: true, headingStyleRange: '1-5', + useAppliedParagraphOutlineLevel: true, }), - createTitle(title), + createTitle(subject), ...sectionsData.reduce((arr, { tableTitle, tableColumns, tableData }) => { const _tableTitle = new Paragraph({ text: tableTitle, @@ -500,8 +501,8 @@ const createDoc = async (agencyName, title, sectionsData) => { }; // Export function -export async function exportDoc(agencyName, title, sectionsData) { - const doc = await createDoc(agencyName, title, sectionsData); +export async function exportDoc({title, subject, sectionsData}) { + const doc = await createDoc({title, subject, sectionsData}); const blob = await Packer.toBlob(doc); - saveAs(blob, `${title}.${agencyName}.docx`); + saveAs(blob, `${subject}.${title}.docx`); } diff --git a/src/components/TemplateLetter2025/TemplateLetter2025.jsx b/src/components/TemplateLetter2025/TemplateLetter2025.jsx deleted file mode 100644 index 3501e87..0000000 --- a/src/components/TemplateLetter2025/TemplateLetter2025.jsx +++ /dev/null @@ -1,308 +0,0 @@ -import { isEmpty } from '@/utils/commons'; -import dayjs from 'dayjs'; -import { - AlignmentType, - BorderStyle, - Document, - Footer, - Header, - HeadingLevel, - LevelFormat, - NumberFormat, - PageNumber, - Paragraph, - Tab, - Table, - TableCell, - TableRow, - TabStopType, - TextRun, - WidthType, - Media, -} from 'docx'; -// import { splitTable_6, splitTable_7, splitTable_B, splitTable_D, splitTable_J, splitTable_Q, splitTable_R, splitTable_8 } from '@/hooks/useProductsQuotationFormat'; -// import { formatGroupSize } from '@/hooks/useProductsSets'; -import logo from './cht letter header logo.png'; - -const unitMap = { - '0': '人', - '1': '团', -}; - -const DOC_TITLE = '地接合同'; -const pageMargins = { - top: `15mm`, - bottom: `15mm`, - left: `10mm`, - right: `10mm`, -}; - -const tableBorderNone = { - top: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' }, - bottom: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' }, - left: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' }, - right: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' }, -}; -const tableBorderOne = { - top: { style: BorderStyle.SINGLE, space: 0, size: 6, color: 'auto' }, - bottom: { style: BorderStyle.SINGLE, space: 0, size: 6, color: 'auto' }, - left: { style: BorderStyle.SINGLE, space: 0, size: 6, color: 'auto' }, - right: { style: BorderStyle.SINGLE, space: 0, size: 6, color: 'auto' }, -}; -const tableBorderInner = { - top: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - bottom: { style: BorderStyle.INSET, space: 0, size: 6, color: 'auto' }, - left: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - right: { style: BorderStyle.INSET, space: 0, size: 6, color: 'auto' }, -}; -const tableBorderInnerB = { - top: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - bottom: { style: BorderStyle.INSET, space: 0, size: 6, color: 'auto' }, - left: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - right: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, -}; -const tableBorderInnerDashB = { - top: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - bottom: { style: BorderStyle.DASHED, space: 0, size: 6, color: 'auto' }, - left: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - right: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, -}; -const tableBorderInnerT = { - top: { style: BorderStyle.INSET, space: 0, size: 6, color: 'auto' }, - bottom: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - left: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - right: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, -}; -const tableBorderInnerR = { - top: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - bottom: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - left: { style: BorderStyle.NONE, space: 0, size: 6, color: 'auto' }, - right: { style: BorderStyle.INSET, space: 0, size: 6, color: 'auto' }, -}; -/** - * @version - * @date 2025-08-20 - */ -export default class TemplateLetter2025 { - #remarkList = {}; - create([headerParams, activeAgency, agencyProducts, agencyExtras, remarks]) { - this.#remarkList = remarks; - const { use_year } = headerParams; - const h1 = `${activeAgency.travel_agency_name}${use_year}年${DOC_TITLE}`; - const yearStart = dayjs(`${use_year}-01-01`).format('YYYY.MM.DD'); - const yearEnd = dayjs(`${use_year}-12-31`).format('YYYY.MM.DD'); - - const document = new Document({ - creator: 'China Highlights', - title: h1, - subject: 'CH信笺2025', - styles: { - paragraphStyles: [ - { - id: 'Normal', - name: 'Normal', - quickFormat: true, - run: { - size: 22, - font: { name: '宋体' }, - color: '000000', - }, - paragraph: { - spacing: { - after: 10, - }, - }, - }, - { - id: 'Title', - name: 'Title', - basedOn: 'Normal', - next: 'Normal', - quickFormat: true, - run: { - size: 44, - font: { name: '宋体' }, - color: '000000', - }, - paragraph: { - spacing: { - before: 200, - after: 200, - }, - }, - }, - { - id: 'Heading1', - name: 'Heading 1', - basedOn: 'Normal', - next: 'Normal', - quickFormat: true, - run: { - size: 32, - font: { name: '宋体' }, - color: '000000', - }, - paragraph: { - spacing: { - before: 200, - after: 200, - }, - }, - }, - { - id: 'Heading2', - name: 'Heading 2', - basedOn: 'Normal', - next: 'Normal', - quickFormat: true, - run: { - size: 28, - font: { name: '宋体' }, - color: '000000', - }, - paragraph: { - spacing: { - before: 120, - after: 120, - }, - }, - }, - { - id: 'TableHeader', - name: 'Table Header', - basedOn: 'Normal', - next: 'Normal', - quickFormat: true, - run: { - size: 22, - font: { name: '宋体' }, - color: '000000', - bold: true, - }, - paragraph: { - spacing: { - before: 80, - after: 80, - }, - }, - }, - ], - }, - numbering: { - config: [ - { - reference: 'products-type', - levels: [{ level: 0, text: '%1、', format: LevelFormat.CHINESE_COUNTING }], - }, - { - reference: 'terms', - levels: [{ level: 0, text: '%1.', format: LevelFormat.DECIMAL }], - }, - ], - }, - sections: [ - { - properties: { - page: { - pageNumbers: { - start: 1, - formatType: NumberFormat.DECIMAL, - }, - margin: pageMargins, - }, - }, - headers: { - default: new Header({ - children: [ - this.createPageHeaderRightText(`Tel: 86-773-2885311`, { italics: false }), - this.createPageHeaderRightText(`Fax: 86-773-2827424`, { italics: false }), - this.createPageHeaderRightText(`E-mail: products@chinahighlights.com`, { italics: false }), - this.createPageHeaderRightText(`Web site: https://www.chinahighlights.com`, { italics: false }), - ], - }), - }, - footers: { - default: new Footer({ - children: [ - new Paragraph({ - alignment: AlignmentType.CENTER, - children: [ - new TextRun({ - children: ['第', PageNumber.CURRENT, '页'], - size: 20, - }), - new TextRun({ - children: [', 共 ', PageNumber.TOTAL_PAGES, '页'], - size: 20, - }), - ], - }), - new Paragraph({ - alignment: AlignmentType.RIGHT, - children: [ - new TextRun({ - text: `${new Date().toLocaleString()}系统生成`, - italics: true, - size: 20, - }), - ], - }), - ], - }), - }, - children: [], - }, - ], - }); - - return document; - } - - createHeading(text) { - return new Paragraph({ - text: text, - heading: HeadingLevel.HEADING_1, - alignment: AlignmentType.CENTER, - }); - } - - createTitle(text) { - return new Paragraph({ - text: text, - heading: HeadingLevel.TITLE, - alignment: AlignmentType.CENTER, - }); - } - - createSubHeading(text, style = {}) { - return new Paragraph({ - text: text, - heading: HeadingLevel.HEADING_1, - ...style, - }); - } - - async createPageHeaderLeftLogo(doc) { - const response = await fetch(logo); - const imageBuffer = await response.arrayBuffer(); - return new Paragraph({ - children: [ - Media.addImage(doc, imageBuffer, 120, 60), // width, height - ], - }); - } - - createPageHeaderRightText(text, style = {}) { - return new Paragraph({ - alignment: AlignmentType.RIGHT, - children: [ - new TextRun({ - text: text, - italics: true, - size: 20, - ...style, - }), - ], - }); - } -} diff --git a/src/views/HostCaseReport.jsx b/src/views/HostCaseReport.jsx index 44ba47b..45cb5d5 100644 --- a/src/views/HostCaseReport.jsx +++ b/src/views/HostCaseReport.jsx @@ -7,6 +7,7 @@ import SearchForm from '../components/search/SearchForm'; import useHostCaseStore from '../zustand/HostCase'; import { useShallow } from 'zustand/shallow'; import { exportDoc } from '../components/TemplateLetter2025/Index'; +import ExportDocxBtn from '../components/TemplateLetter2025/ExportDocxBtn'; const HostCaseReport = ({ ...props }) => { const { date_picker_store } = useContext(stores_Context); @@ -26,6 +27,7 @@ const HostCaseReport = ({ ...props }) => { const summaryCols = [ { title: '接团数', dataIndex: 'group_count', width: '6rem' }, + { title: 'feedback团数', dataIndex: 'feedbak_group', width: '6rem' }, { title: '东道主团数', dataIndex: 'group_count_dongdaozhu', width1: '8rem' }, { title: '东道主个数', dataIndex: 'case_count_dongdaozhu', width1: '8rem' }, { title: '东道主实施比例', dataIndex: 'dongdaozhu_rate' }, @@ -75,17 +77,15 @@ const HostCaseReport = ({ ...props }) => {