From 7b1547864070b9e1a0a17fbe7c02c12dbbe6bfa2 Mon Sep 17 00:00:00 2001 From: eddie <1150970484@qq.com> Date: Wed, 27 Nov 2024 10:42:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E6=A8=A1=E7=89=88=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + .../products/Print/AgencyContract_v1120.jsx | 819 +++++++++++++----- 2 files changed, 622 insertions(+), 198 deletions(-) diff --git a/package.json b/package.json index 8a99205..b1a8a61 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "i18next": "^23.11.5", "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^2.5.2", + "lodash": "^4.17.21", "react": "^18.3.1", "react-dom": "^18.3.1", "react-i18next": "^14.1.2", diff --git a/src/views/products/Print/AgencyContract_v1120.jsx b/src/views/products/Print/AgencyContract_v1120.jsx index e092cba..daa24a8 100644 --- a/src/views/products/Print/AgencyContract_v1120.jsx +++ b/src/views/products/Print/AgencyContract_v1120.jsx @@ -1,28 +1,25 @@ +import { flush, groupBy, isEmpty, isNotEmpty, unique, uniqWith } from '@/utils/commons' +import dayjs from 'dayjs' import { AlignmentType, + BorderStyle, Document, - Header, Footer, + Header, HeadingLevel, + LevelFormat, + NumberFormat, + PageNumber, Paragraph, - TabStopType, + Tab, Table, TableCell, TableRow, + TabStopType, TextRun, - BorderStyle, - NumberFormat, - PageNumber, WidthType, - LevelFormat, - Tab, - PositionalTab, - PositionalTabAlignment, - PositionalTabRelativeTo, - PositionalTabLeader, } from 'docx' -import dayjs from 'dayjs' -import { cloneDeep, flush, groupBy, isEmpty, isNotEmpty, sortBy, unique, uniqWith } from '@/utils/commons' +import _ from 'lodash' // Shoulder Season 平季; peak season 旺季 const isFullYearOrLonger = (year, startDate, endDate) => { @@ -66,6 +63,7 @@ const chunkCarSets = (numbers) => { { lt5: [], lt7: [], lt10: [], gt10: [] }, ) } + const uniqueBySub = (arr) => arr.filter((subArr1, _, self) => { return !self.some((subArr2) => { @@ -76,6 +74,7 @@ const uniqueBySub = (arr) => return [...set1].every((value) => set2.has(value)) }) }) + const isFlatUnique = (bigArray) => { let flattenedArray = bigArray.flat() return flattenedArray.every((i) => { @@ -83,6 +82,7 @@ const isFlatUnique = (bigArray) => { return count === 1 }) } + const chunkBy = (use_year, dataList = [], by = []) => { const dataRollSS = dataList.map((rowp, ii) => { const quotation = rowp.quotation.map((quoteItem) => { @@ -93,35 +93,33 @@ const chunkBy = (use_year, dataList = [], by = []) => { }) return { ...rowp, quotation } }) - // console.log('\ndataRollSS', dataRollSS); - // console.log('\n\n', dataList[0].info.product_type_id, dataRollSS); // 人等分组只取平季, 因为产品只一行 const allQuotesSS = dataRollSS.reduce( (acc, rowp) => acc.concat(rowp.quotation.filter((q) => q.quote_season === 'SS')), [], ) + const allQuotesPS = dataRollSS.reduce( (acc, rowp) => acc.concat(rowp.quotation.filter((q) => q.quote_season === 'PS')), [], ) const allQuotesSSS = isEmpty(allQuotesSS) ? allQuotesPS : allQuotesSS - // console.log('allQuotesSSS', allQuotesSS, '\n', allQuotesPS, '\n', allQuotesSSS); - // const groupSizeSS = (allQuotesSS).reduce((aq, cq) => aq.concat([[cq.group_size_min, cq.group_size_max]]), []); const PGroupSizeSS = allQuotesSSS.reduce((aq, cq) => { aq[cq.WPI_SN] = aq[cq.WPI_SN] || [] aq[cq.WPI_SN].push(cq.group_size_min) aq[cq.WPI_SN] = unique(aq[cq.WPI_SN]) return aq }, {}) + const maxGroupSize = Math.max(...allQuotesSSS.map((q) => q.group_size_max)) const maxSet = maxGroupSize === 1000 ? Infinity : maxGroupSize - // console.log('PGroupSizeSS', structuredClone(PGroupSizeSS)); + const _SSMinSet = uniqWith(Object.values(PGroupSizeSS), (a, b) => a.join(',') === b.join(',')) - // console.log('SSMinSet', _SSMinSet, maxGroupSize); + const uSSsizeSetArr = uniqueBySub(_SSMinSet) - // console.log('uSSsizeSetArr', uSSsizeSetArr); + for (const key in PGroupSizeSS) { if (Object.prototype.hasOwnProperty.call(PGroupSizeSS, key)) { const element = PGroupSizeSS[key] @@ -129,7 +127,7 @@ const chunkBy = (use_year, dataList = [], by = []) => { PGroupSizeSS[key] = findSet } } - // console.log('PGroupSizeSS aaa', PGroupSizeSS); + const [SSsizeSets, PSsizeSets] = [uSSsizeSetArr, []].map((arr) => { const _arr = structuredClone(arr) const arrSets = _arr.map((keyMins) => @@ -141,17 +139,14 @@ const chunkBy = (use_year, dataList = [], by = []) => { ) return arrSets }) - // console.log('PGroupSizeSS fff', SSsizeSets); + const compactSizeSets = { SSsizeSetKey: uSSsizeSetArr.map((s) => s.join(',')).filter(isNotEmpty), sizeSets: SSsizeSets, } - // console.log(compactSizeSets); - const chunkSS = structuredClone(dataRollSS).map((rowp) => { - // console.log(rowp.info.id, rowp.info.product_title); + const chunkSS = structuredClone(dataRollSS).map((rowp) => { const pkey = (PGroupSizeSS[rowp.info.id] || []).join(',') || compactSizeSets.SSsizeSetKey[0] // todo: - // console.log('pkey', pkey); const thisRange = (PGroupSizeSS[rowp.info.id] || []).reduce((acc, curr, idx, minsArr) => { const _max = idx === minsArr.length - 1 ? maxSet : Number(minsArr[idx + 1]) - 1 @@ -160,25 +155,14 @@ const chunkBy = (use_year, dataList = [], by = []) => { }, []) const _quotation = rowp.quotation.map((quoteItem) => { const ssSets = isEmpty(thisRange) ? SSsizeSets[0] : structuredClone(thisRange).reverse() - // if (isEmpty(ssSets)) { - // console.group('quotation item'); - // console.log(rowp.info.id, rowp.info.product_title, rowp); - // console.log(ssSets); - // console.log([quoteItem.group_size_min, quoteItem.group_size_max]); - // console.groupEnd(); - // } - // console.group('quotation item'); - // console.log(ssSets); - // console.log([quoteItem.group_size_min, quoteItem.group_size_max]); + const matchRange = ssSets.find((ss) => quoteItem.group_size_min >= ss[0] && quoteItem.group_size_max <= ss[1]) const findEnd = matchRange || ssSets.find((ss) => quoteItem.group_size_max > ss[0] && quoteItem.group_size_max <= ss[1] && ss[1] !== Infinity) const findStart = findEnd || ssSets.find((ss) => quoteItem.group_size_min >= ss[0]) const finalRange = findStart || ssSets[0] - // console.log('find mmm', matchRange, findEnd, findStart, finalRange); - // console.log('matchRange ', 'find', [quoteItem.group_size_min, quoteItem.group_size_max], 'matched', finalRange); - // console.groupEnd(); + quoteItem.quote_size = finalRange.join('-') return quoteItem }) @@ -199,7 +183,7 @@ const chunkBy = (use_year, dataList = [], by = []) => { quote_chunk, } }) - // console.log('quotation', chunkSS); + const allquotation = chunkSS.reduce((a, c) => a.concat(c.quotation), []) // 取出两季相应的时效区间 const SSRange = unique( @@ -213,7 +197,13 @@ const chunkBy = (use_year, dataList = [], by = []) => { .map((qr) => `${qr.use_dates_start}~${qr.use_dates_end}`), ) - return { chunk: chunkSS, dataSource: chunkSS, SSRange, PSRange, ...compactSizeSets } + return { + chunk: chunkSS, + dataSource: chunkSS, + SSRange, + PSRange, + ...compactSizeSets, + } } const DOC_TITLE = '地接合同' @@ -223,6 +213,7 @@ const pageMargins = { left: `10mm`, right: `10mm`, } + const tableBorderNone = { top: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' }, bottom: { style: BorderStyle.NONE, size: 0, color: 'FFFFFF' }, @@ -240,21 +231,17 @@ export default class AgencyContract { #remarkList = [] constructor(remarkList) { this.#remarkList = remarkList - console.log(this.#remarkList) } create([headerParams, activeAgency, agencyProducts, agencyExtras]) { 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') - // console.log(agencyExtras); - // console.log('*********************************************************************************************'); const document = new Document({ creator: 'China Highlights', title: h1, subject: '2025桂林海纳价格合同模板', - // description: '', styles: { paragraphStyles: [ { @@ -345,23 +332,18 @@ export default class AgencyContract { }, }, }, - // { - // id: "MySpectacularStyle", - // name: "My Spectacular Style", - // basedOn: "Heading1", - // next: "Heading1", - // quickFormat: true, - // run: { - // italics: true, - // color: "990000", - // }, - // }, ], }, numbering: { config: [ - { reference: 'products-type', levels: [{ level: 0, text: '%1、', format: LevelFormat.CHINESE_COUNTING }] }, - { reference: 'terms', levels: [{ level: 0, text: '%1.', format: LevelFormat.DECIMAL }] }, + { + reference: 'products-type', + levels: [{ level: 0, text: '%1、', format: LevelFormat.CHINESE_COUNTING }], + }, + { + reference: 'terms', + levels: [{ level: 0, text: '%1.', format: LevelFormat.DECIMAL }], + }, ], }, sections: [ @@ -378,8 +360,12 @@ export default class AgencyContract { headers: { default: new Header({ children: [ - this.createPageHeaderText(`Email: xeniayou@hainatravel.com`, { italics: false }), - this.createPageHeaderText(`https://www.chinahighlights.com`, { italics: false }), + this.createPageHeaderText(`Email: xeniayou@hainatravel.com`, { + italics: false, + }), + this.createPageHeaderText(`https://www.chinahighlights.com`, { + italics: false, + }), ], }), }, @@ -419,25 +405,33 @@ export default class AgencyContract { ...(isEmpty(agencyProducts['6']) ? [] : [ - this.createSubHeading(`综费(元/人/天)`, { numbering: { reference: 'products-type', level: 0 } }), + this.createSubHeading(`综费(元/人/天)`, { + numbering: { reference: 'products-type', level: 0 }, + }), this.createTable_6(use_year, agencyProducts['6']), ]), ...(isEmpty(agencyProducts['B']) ? [] : [ - this.createSubHeading(`超公里(元/人)`, { numbering: { reference: 'products-type', level: 0 } }), + this.createSubHeading(`超公里(元/人)`, { + numbering: { reference: 'products-type', level: 0 }, + }), ...this.createTable_B(use_year, agencyProducts['B']), ]), ...(isEmpty(agencyProducts['J']) ? [] : [ - this.createSubHeading(`车费(元/团)`, { numbering: { reference: 'products-type', level: 0 } }), - ...this.createTable_JD(use_year, agencyProducts['J']), + this.createSubHeading(`车费(元/团)`, { + numbering: { reference: 'products-type', level: 0 }, + }), + ...this.createTable_J(use_year, agencyProducts['J']), ]), ...(isEmpty(agencyProducts['Q']) ? [] : [ - this.createSubHeading(`导游`, { numbering: { reference: 'products-type', level: 0 } }), + this.createSubHeading(`导游`, { + numbering: { reference: 'products-type', level: 0 }, + }), this.createTable_Q(use_year, agencyProducts['Q']), ]), ...(isEmpty(agencyProducts['7']) @@ -451,7 +445,9 @@ export default class AgencyContract { ...(isEmpty(agencyProducts['R']) ? [] : [ - this.createSubHeading(`餐费(元/人/餐)`, { numbering: { reference: 'products-type', level: 0 } }), + this.createSubHeading(`餐费(元/人/餐)`, { + numbering: { reference: 'products-type', level: 0 }, + }), ...this.createTable_R(use_year, agencyProducts['R'], agencyExtras), ]), ...(isEmpty(agencyProducts['D']) @@ -460,13 +456,19 @@ export default class AgencyContract { this.createSubHeading(`包价线路(元/团,部分价格为元/人单独注明)`, { numbering: { reference: 'products-type', level: 0 }, }), - ...this.createTable_JD(use_year, agencyProducts['D']), + ...this.createTable_D(use_year, agencyProducts['D']), ]), - this.createSubHeading(`公司银行账户`, { numbering: { reference: 'products-type', level: 0 } }), + this.createSubHeading(`公司银行账户`, { + numbering: { reference: 'products-type', level: 0 }, + }), this.createInfo_Bank(), - this.createSubHeading(`联系资料`, { numbering: { reference: 'products-type', level: 0 } }), + this.createSubHeading(`联系资料`, { + numbering: { reference: 'products-type', level: 0 }, + }), this.createInfo_Contact(), - this.createSubHeading(`补充条款`, { numbering: { reference: 'products-type', level: 0 } }), + this.createSubHeading(`补充条款`, { + numbering: { reference: 'products-type', level: 0 }, + }), ...this.createInfo_Terms(), ], }, @@ -475,13 +477,12 @@ export default class AgencyContract { return document } - /** * 综费 */ createTable_6(use_year, dataList, style = {}) { const { chunk, SSRange, sizeSets } = chunkBy(use_year, dataList, ['quote_season']) - // console.log('\n\n\n6', chunk, '\n',sizeSets); + //当前表格的备注信息 const [remarkItem] = this.#remarkList.filter((i) => i.product_type_id == '6') @@ -538,7 +539,9 @@ export default class AgencyContract { children: [ this.createTableHeader('超公里项目', 20), this.createTableHeader('往返公里数', 20), - this.createTableHeader([...SSRange], 60, { columnSpan: size.length }), + this.createTableHeader([...SSRange], 60, { + columnSpan: size.length, + }), ], }), new TableRow({ @@ -560,7 +563,6 @@ export default class AgencyContract { verticalAlign: AlignmentType.CENTER, children: [ new Paragraph({ - // text: `${ss[0]}`, text: ss[1] === Infinity ? `${ss[0]}人以上` : `${unique(ss.filter((x) => x)).join('-')}人`, alignment: AlignmentType.CENTER, }), @@ -573,10 +575,15 @@ export default class AgencyContract { .filter((p) => p.sizeSetsSS === size.map((mr) => mr[0]).join(',')) .reduce( (acc, row, ri) => - acc.concat(this.createDetailRowSize(size, row, ri, { infoCol, withSize: false, defaultUnit: '0' })), + acc.concat( + this.createDetailRowSize(size, row, ri, { + infoCol, + withSize: false, + defaultUnit: '0', + }), + ), [], ), - // .reduce((a, c) => a.concat(c), []), this.createTable_Memo(2 + size.length, remarkItem.Memo), ], @@ -590,16 +597,116 @@ export default class AgencyContract { /** * 车费 - * 包价线路 */ - createTable_JD(use_year, dataList) { - const chunk_By = ['quote_size'] // dataList[0].info.product_type_id === 'J' ? ['quote_size'] : ['quote_season', 'quote_size']; - const showPSCol = false // dataList[0].info.product_type_id === 'J' ? false : true; - const defaultUnit = '1' // dataList[0].info.product_type_id === 'J' ? '' : ''; + createTable_J(use_year, dataList) { + const chunk_By = ['quote_size'] + const showPSCol = false + const defaultUnit = '1' const { dataSource, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, chunk_By) //当前表格的备注信息 const [remarkItem] = this.#remarkList.filter((i) => i.product_type_id == 'J') + const tableHeader = this.createTableHeader + const tableS = sizeSets.reduce((rt, setItem) => { + const subTable = new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [ + tableHeader('项目', 15), + tableHeader('', 15), + tableHeader([...SSRange], 30, { columnSpan: setItem.length + 1 }), + showPSCol + ? tableHeader(['旺季', ...PSRange], 40, { + columnSpan: setItem.length, + }) + : undefined, + ], + }), + //人数区间行 + new TableRow({ + children: [ + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [new Paragraph({ text: '' })], + }), + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [new Paragraph({ text: '时段', alignment: 'center' })], + }), + ...setItem.map((ss) => { + return new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + width: { size: 2000, type: WidthType.DXA }, + children: [ + new Paragraph({ + text: ss[1] === Infinity ? `${ss[0]}人以上` : `${unique(ss.filter((x) => x)).join('-')}人`, + alignment: AlignmentType.CENTER, + }), + ], + }) + }), + ...(showPSCol + ? setItem.map( + (ss) => + new TableCell({ + borders: tableBorderOne, + width: { size: 2000, type: WidthType.DXA }, + verticalAlign: AlignmentType.CENTER, + columnSpan: sizeSets.length + 1, + children: [ + new Paragraph({ + text: ss[1] === Infinity ? `${ss[0]}人以上` : `${unique(ss.filter((x) => x)).join('-')}人`, + alignment: AlignmentType.CENTER, + }), + ], + }), + ) + : []), + ], + }), + //动态生成数据行 + ...dataSource + .filter((p) => p.sizeSetsSS === setItem.map((mr) => mr[0]).join(',')) + .reduce((acc, row, ri) => { + return acc.concat( + this.createDetailRowSize_J(setItem, row, ri, { + withSize: false, + defaultUnit, + withSeason: showPSCol, + }), + ) + }, []), + //备注行 + this.createTable_Memo(1 + setItem.length * 2, remarkItem.Memo), + ], + }) + rt.push(subTable) + rt.push(new Paragraph({ text: `` })) + return rt + }, []) + return tableS + } + + /** + * 包价线路 + */ + createTable_D(use_year, dataList) { + const chunk_By = ['quote_size'] + const showPSCol = false + const defaultUnit = '1' + const { dataSource, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, chunk_By) + // console.log(sizeSets, dataSource) + //当前表格的备注信息 + const [remarkItem] = this.#remarkList.filter((i) => i.product_type_id == 'D') + const tableHeader = this.createTableHeader const tableS = sizeSets.reduce((rt, setItem) => { const subTable = new Table({ @@ -612,9 +719,12 @@ export default class AgencyContract { new TableRow({ children: [ tableHeader('项目', 20), - // tableHeader('', 20), tableHeader([...SSRange], 40, { columnSpan: setItem.length }), - showPSCol ? tableHeader(['旺季', ...PSRange], 40, { columnSpan: setItem.length }) : undefined, + showPSCol + ? tableHeader(['旺季', ...PSRange], 40, { + columnSpan: setItem.length, + }) + : undefined, ], }), new TableRow({ @@ -624,11 +734,6 @@ export default class AgencyContract { verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })], }), - // new TableCell({ - // borders: tableBorderOne, - // verticalAlign: AlignmentType.CENTER, - // children: [new Paragraph({ text: '时段' })], - // }), ...setItem.map( (ss) => new TableCell({ @@ -666,12 +771,15 @@ export default class AgencyContract { .reduce( (acc, row, ri) => acc.concat( - this.createDetailRowSize(setItem, row, ri, { withSize: false, defaultUnit, withSeason: showPSCol }), + this.createDetailRowSize_D(setItem, row, ri, { + withSize: false, + defaultUnit, + withSeason: showPSCol, + }), ), [], ), - // .reduce((a, c) => a.concat(c), []), - + //备注行 this.createTable_Memo(1 + setItem.length * 2, remarkItem.Memo), ], }) @@ -699,8 +807,8 @@ export default class AgencyContract { new TableRow({ children: [ this.createTableHeader('项目', 30), - this.createTableHeader(['平季(除去旺季以外的时间)', ...SSRange], 35), - this.createTableHeader(['旺季', ...PSRange], 35), + this.createTableHeader(['平季(除去旺季以外)'], 35), + this.createTableHeader(['旺季'], 35), ], }), ...chunk @@ -715,7 +823,10 @@ export default class AgencyContract { * 景点 */ createTable_7(use_year, dataList, agencyExtras) { - const withExtras = dataList.map((row) => ({ ...row, extras: agencyExtras[row.info.id] || [] })) + const withExtras = dataList.map((row) => ({ + ...row, + extras: agencyExtras[row.info.id] || [], + })) const { chunk, sizeSets, SSRange, PSRange } = chunkBy(use_year, withExtras, ['quote_season']) //当前表格的备注信息 const [remarkItem] = this.#remarkList.filter((i) => i.product_type_id == '7') @@ -739,7 +850,12 @@ export default class AgencyContract { ) // console.log(compactQuotation); const thisE = new Paragraph({ - children: [new TextRun({ text: `${extra.info.product_title}`, bold: true })], + children: [ + new TextRun({ + text: `${extra.info.product_title}`, + bold: true, + }), + ], }) const thisP = Object.entries(compactQuotation).map(([key, [firstQ, ...others]]) => { const date1 = shortDate(firstQ.use_dates_start) @@ -781,18 +897,23 @@ export default class AgencyContract { new TableRow({ children: [ this.createTableHeader('景点', 25), - this.createTableHeader(['平季', ...SSRange], 25), - this.createTableHeader(['旺季', ...PSRange], 25), + this.createTableHeader(['平季', '(除特殊时段以外)'], 25), + this.createTableHeader(['旺季', '(淡季/旺季/特定时段)'], 25), this.createTableHeader('附加费用', 25), ], }), ...chunk.reduce( (acc, row, ri) => - acc.concat(this.createDetailRowSeason2(row, ri, { withSize: true, defaultUnit: '0', extraCol })), + acc.concat( + this.createDetailRowSeason2(row, ri, { + withSize: true, + defaultUnit: '0', + extraCol, + }), + ), [], ), - // ...chunk.map((row, ri) => this.createDetailRowSeason2(row, ri, { withSize: true, defaultUnit: '0', extraCol })).reduce((a, c) => a.concat(c), []), - + //备注行 this.createTable_Memo(1 + sizeSets.length * 2 + 1, remarkItem.Memo), ], }) @@ -801,7 +922,10 @@ export default class AgencyContract { * 餐费 */ createTable_R(use_year, dataList, agencyExtras) { - const withExtras = dataList.map((row) => ({ ...row, extras: agencyExtras[row.info.id] || [] })) + const withExtras = dataList.map((row) => ({ + ...row, + extras: agencyExtras[row.info.id] || [], + })) const { chunk, sizeSets, SSRange, PSRange } = chunkBy(use_year, withExtras, ['quote_size']) //当前表格的备注信息 const [remarkItem] = this.#remarkList.filter((i) => i.product_type_id == 'R') @@ -844,8 +968,10 @@ export default class AgencyContract { new TableRow({ children: [ this.createTableHeader('社会餐厅午餐/晚餐', 25), - this.createTableHeader([...SSRange, ...PSRange], 50, { columnSpan: size.length }), - this.createTableHeader('司陪餐补', 25), + this.createTableHeader([...SSRange, ...PSRange], 50, { + columnSpan: size.length, + }), + this.createTableHeader('司陪餐补(元/团/餐)', 25), ], }), new TableRow({ @@ -862,7 +988,6 @@ export default class AgencyContract { verticalAlign: AlignmentType.CENTER, children: [ new Paragraph({ - // text: `${ss[0]}`, text: ss[1] === Infinity ? `${ss[0]}人以上` : `${unique(ss.filter((x) => x)).join('-')}人`, alignment: AlignmentType.CENTER, }), @@ -871,11 +996,14 @@ export default class AgencyContract { ), new TableCell({ borders: tableBorderOne, - verticalAlign: AlignmentType.CENTER, rowSpan: thisChunk.length + 1, - children: mergedUniqueExtra, - }), // [new Paragraph({ text: `${thisChunk.length+1}` })] - // new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: '不分人等', + }), + ], + }), ], }), ...thisChunk.reduce( @@ -892,11 +1020,6 @@ export default class AgencyContract { }, []) return tableS } - /** - * 包价线路 - * @deprecated - */ - createTable_D(use_year, dataList) {} createDetailRowSizeSS = ( sizeSets, @@ -915,15 +1038,8 @@ export default class AgencyContract { }, {}) const data = thisSizeCol.map((sizeRow, ri) => { - return new TableRow({ children: flush([ - // new TableCell({ - // borders: tableBorderOne, - // rowSpan: 3, - // verticalAlign: AlignmentType.CENTER, - // children: [new Paragraph(String(ii + 1))], - // }), ...(ri === 0 ? [ new TableCell({ @@ -965,11 +1081,129 @@ export default class AgencyContract { ]), }) }) - console.log(data) return data } - createDetailRowSize( + createDetailRowSize_J( + sizeSets, + rowp, + ii, + { withSeason = false, withSize = true, infoCol = () => {}, extraCol = () => {}, defaultUnit = '0' } = {}, + ) { + const sizeCol = sizeSets.map((ss) => ss.join('-')) + + const ranges = rowp['quotation'].map((i) => + Object.assign(i, { + range: ((quote_season) => { + const range = `${dayjs(i.use_dates_start).format('M-D').replace(/-/gi, '.')}-${dayjs(i.use_dates_end) + .format('M-D') + .replace(/-/gi, '.')}` + if (quote_season == 'SS') return `平季(除特殊时段以外)` + else return `特殊时段(${range})` + })(i.quote_season), + }), + ) + + //每个项目(产品)有多少个行(不同的时间段) + const product_rows = [...new Map(ranges.map((item) => [item.range, item])).values()] + + const rows = product_rows.reduce((acc, i) => { + const row = [] + sizeCol.forEach((j) => { + const target = rowp['quotation'] + .filter( + (item) => + item.use_dates_start == i.use_dates_start && + item.use_dates_end == i.use_dates_end && + item.quote_size == j, + ) + .pop() + if (target) row.push(target) + else row.push(``) + }) + + return Object.assign(acc, { [i.range]: row }) + }, {}) + + return [ + ...Object.entries(rows).map(([rangeStr, arr], index) => { + return new TableRow({ + children: flush([ + //合并相同项目的第一列 + ...(index == 0 + ? [ + new TableCell({ + rowSpan: index == 0 ? Object.values(rows).length : 0, + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + new Paragraph({ + text: index == 0 ? `${rowp?.info?.product_title}` : '', + alignment: AlignmentType.CENTER, + }), + ], + }), + ] + : []), + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + new Paragraph({ + text: Object.values(rows).length > 1 ? rangeStr : '', + alignment: AlignmentType.CENTER, + }), + ], + }), + infoCol(rowp), + ...arr.map((quoteItem) => { + return new TableCell({ + borders: tableBorderOne, + width: { size: 2000, type: WidthType.DXA }, + verticalAlign: AlignmentType.CENTER, + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: isEmpty(quoteItem) + ? '' + : withSize && String(quoteItem.unit_id) !== defaultUnit + ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}, ${quoteItem.adult_cost}` + : `${quoteItem.adult_cost}`, + }), + ], + }) + }), + ...(withSeason + ? sizeCol.map( + (ss) => + new TableCell({ + borders: tableBorderOne, + width: { size: 2000, type: WidthType.DXA }, + verticalAlign: AlignmentType.CENTER, + children: [ + ...(rowp.quote_chunk?.['PS']?.[ss] || []).map( + (quoteItem, ii) => + new Paragraph({ + alignment: AlignmentType.CENTER, + // text: `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}${quoteItem.unit_name}`, + text: + withSize && String(quoteItem.unit_id) !== defaultUnit + ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}, ${quoteItem.adult_cost}` + : `${quoteItem.adult_cost}`, + }), + ), + ], + }), + ) + : []), + extraCol(rowp, ii), + ]), + }) + }), + ] + } + + createDetailRowSize_D( sizeSets, rowp, ii, @@ -977,15 +1211,13 @@ export default class AgencyContract { ) { const sizeCol = sizeSets.map((ss) => ss.join('-')) + //默认时间 全年 + const firstDayOfYear = dayjs().startOf('year').format('YYYY-MM-DD') + const lastDayOfYear = dayjs().endOf('year').format('YYYY-MM-DD') + return [ new TableRow({ children: flush([ - // new TableCell({ - // borders: tableBorderOne, - // rowSpan: 3, - // verticalAlign: AlignmentType.CENTER, - // children: [new Paragraph(String(ii + 1))], - // }), new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, @@ -997,29 +1229,129 @@ export default class AgencyContract { ], }), infoCol(rowp), - ...sizeCol.map( - (ss) =>{ - return new TableCell({ + ...sizeCol.map((ss) => { + const subItems = (withSeason ? rowp.quote_chunk?.['SS']?.[ss] : rowp.quote_chunk[ss]) || [] + return new TableCell({ + borders: tableBorderOne, + width: { size: 2000, type: WidthType.DXA }, + verticalAlign: AlignmentType.CENTER, + children: [ + ...subItems.reduce((acc, quoteItem, index) => { + const start = dayjs(quoteItem.use_dates_start).format('M-D').replace('-', '.') + const end = dayjs(quoteItem.use_dates_end).format('M-D').replace('-', '.') + // new Paragraph({ + // alignment: AlignmentType.CENTER, + // text: isEmpty(quoteItem) + // ? '' + // : quoteItem.use_dates_start != firstDayOfYear || quoteItem.use_dates_end != lastDayOfYear + // ? `(${start}-${end})` + // : '', + // }), + return acc.concat( + ...[ + new Paragraph({ + alignment: AlignmentType.CENTER, + text: isEmpty(quoteItem) + ? '' + : withSize && String(quoteItem.unit_id) !== defaultUnit + ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}, ${quoteItem.adult_cost}` + : `${quoteItem.adult_cost}`, + }), + new Paragraph({ + alignment: AlignmentType.CENTER, + text: isEmpty(quoteItem) + ? '' + : quoteItem.use_dates_start != firstDayOfYear || quoteItem.use_dates_end != lastDayOfYear + ? `(${start}-${end})` + : '', + }), + ], + ) + }, []), + // ...((withSeason ? rowp.quote_chunk?.['SS']?.[ss] : rowp.quote_chunk[ss]) || []).map( + // (quoteItem, ii) => + // new Paragraph({ + // alignment: AlignmentType.CENTER, + // text: isEmpty(quoteItem) + // ? '' + // : withSize && String(quoteItem.unit_id) !== defaultUnit + // ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}, ${quoteItem.adult_cost}` + // : `${quoteItem.adult_cost}`, + // }), + // ), + ], + }) + }), + ...(withSeason + ? sizeCol.map( + (ss) => + new TableCell({ borders: tableBorderOne, - width: { size: 1500, type: WidthType.DXA }, + width: { size: 2000, type: WidthType.DXA }, verticalAlign: AlignmentType.CENTER, children: [ - ...((withSeason ? rowp.quote_chunk?.['SS']?.[ss] : rowp.quote_chunk[ss]) || []).map( + ...(rowp.quote_chunk?.['PS']?.[ss] || []).map( (quoteItem, ii) => new Paragraph({ alignment: AlignmentType.CENTER, - text: isEmpty(quoteItem) - ? '' - : withSize && String(quoteItem.unit_id) !== defaultUnit - ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}, ${quoteItem.adult_cost}` - : `${quoteItem.adult_cost}`, + text: + withSize && String(quoteItem.unit_id) !== defaultUnit + ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}, ${quoteItem.adult_cost}` + : `${quoteItem.adult_cost}`, }), ), - // new Paragraph({ text: '-'+ii }) ], - }) - } - ), + }), + ) + : []), + extraCol(rowp, ii), + ]), + }), + ] + } + + createDetailRowSize( + sizeSets, + rowp, + ii, + { withSeason = false, withSize = true, infoCol = () => {}, extraCol = () => {}, defaultUnit = '0' } = {}, + ) { + const sizeCol = sizeSets.map((ss) => ss.join('-')) + + return [ + new TableRow({ + children: flush([ + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + new Paragraph({ + text: `${rowp?.info?.product_title}`, + alignment: AlignmentType.LEFT, + }), + ], + }), + infoCol(rowp), + ...sizeCol.map((ss) => { + return new TableCell({ + borders: tableBorderOne, + width: { size: 2000, type: WidthType.DXA }, + verticalAlign: AlignmentType.CENTER, + children: [ + ...((withSeason ? rowp.quote_chunk?.['SS']?.[ss] : rowp.quote_chunk[ss]) || []).map( + (quoteItem, ii) => + new Paragraph({ + alignment: AlignmentType.CENTER, + text: isEmpty(quoteItem) + ? '' + : withSize && String(quoteItem.unit_id) !== defaultUnit + ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}, ${quoteItem.adult_cost}` + : `${quoteItem.adult_cost}`, + }), + ), + ], + }) + }), ...(withSeason ? sizeCol.map( (ss) => @@ -1048,16 +1380,13 @@ export default class AgencyContract { }), ] } + createDetailRowSeason2(rowp, ii, { withSize = true, defaultUnit = '0', extraCol = () => {} } = {}) { + // console.log(rowp) return [ new TableRow({ children: [ - // new TableCell({ - // borders: tableBorderOne, - // rowSpan: 3, - // verticalAlign: AlignmentType.CENTER, - // children: [new Paragraph(String(ii + 1))], - // }), + //标题列 new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, @@ -1068,6 +1397,7 @@ export default class AgencyContract { }), ], }), + //SS new TableCell({ borders: tableBorderOne, width: { size: 2000, type: WidthType.DXA }, @@ -1077,7 +1407,6 @@ export default class AgencyContract { (quoteItem, ii) => new Paragraph({ alignment: AlignmentType.CENTER, - // text: `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}`, text: (withSize && String(quoteItem.unit_id) !== defaultUnit ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name},` @@ -1086,22 +1415,71 @@ export default class AgencyContract { ), ], }), + //PS new TableCell({ borders: tableBorderOne, width: { size: 2000, type: WidthType.DXA }, verticalAlign: AlignmentType.CENTER, children: [ - ...(rowp.quote_chunk['PS'] || []).map( - (quoteItem, ii) => - new Paragraph({ - alignment: AlignmentType.CENTER, - // text: `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}`, - text: - (withSize && String(quoteItem.unit_id) !== defaultUnit - ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name},` - : '') + `${quoteItem.adult_cost}`, - }), - ), + //一个立即执行函数 + ...(() => { + //存储价格的数组 + const helper = [] + //PS 是所有旺季的数据 + const PS = rowp.quote_chunk['PS'] || [] + //第一次迭代获取所有价格和时间区间的数组 + const AllDatas = PS.reduce((acc, quoteItem, ii) => { + //价格文本 + const text = + (withSize && String(quoteItem.unit_id) === defaultUnit + ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name},` + : '') + `${quoteItem.adult_cost}` + + //计算开始日期和结束日期 + const start = dayjs(quoteItem.use_dates_start).format('M-D').replace('-', '.') + const end = dayjs(quoteItem.use_dates_end).format('M-D').replace('-', '.') + //添加到数组的元素 + // const group = { ...quoteItem, text, range:start_year == end_year ? `${start}-${end}; ` : `${start_year}.${start}-${end_year}.${end}; ` } + const group = { ...quoteItem, text, range: `${start}-${end}; ` } + acc.push(group) + return acc + }, []) + + //第二次迭代对重复价格的元素进行时间区间字符串拼接 + const data = AllDatas.reduce((acc, obj) => { + const { text } = obj + //判断数组中是否已经有当前价格的元素 + if (acc.some((i) => i.text == text)) { + acc = acc.map((j) => { + //如果数组中已存在当前价格的元素,合并他们的时间区间字符串 + if (j.text == text) return { text, range: j.range + obj.range } + else return j + }) + } else acc.push(obj) + + return acc + }, []) + + for (let index = 0; index < data.length; index++) { + const element = data[index] + //添加价格 + helper.push( + new Paragraph({ + alignment: AlignmentType.CENTER, + text: element.text, + }), + ) + //添加时间 + helper.push( + new Paragraph({ + alignment: AlignmentType.CENTER, + text: `(${element.range})`, + }), + ) + } + + return helper + })(), ], }), extraCol(rowp, ii), @@ -1140,20 +1518,45 @@ export default class AgencyContract { }, rows: [ new TableRow({ - children: [Col1('帐户:'), new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] })], + children: [ + Col1('帐户:'), + new TableCell({ + borders: tableBorderOne, + children: [new Paragraph(``)], + }), + ], }), new TableRow({ - children: [Col1('银行:'), new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] })], + children: [ + Col1('银行:'), + new TableCell({ + borders: tableBorderOne, + children: [new Paragraph(``)], + }), + ], }), new TableRow({ - children: [Col1('帐号:'), new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] })], + children: [ + Col1('帐号:'), + new TableCell({ + borders: tableBorderOne, + children: [new Paragraph(``)], + }), + ], }), new TableRow({ - children: [Col1('结账方式:'), new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] })], + children: [ + Col1('结账方式:'), + new TableCell({ + borders: tableBorderOne, + children: [new Paragraph(``)], + }), + ], }), ], }) } + createInfo_Contact() { const Col1 = (label, opt = {}) => new TableCell({ @@ -1182,7 +1585,11 @@ export default class AgencyContract { children: [ Col1('经理:'), ...[null, null, null].map( - (col) => new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] }), + (col) => + new TableCell({ + borders: tableBorderOne, + children: [new Paragraph(``)], + }), ), ], }), @@ -1190,7 +1597,11 @@ export default class AgencyContract { children: [ Col1('计调:'), ...[null, null, null].map( - (col) => new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] }), + (col) => + new TableCell({ + borders: tableBorderOne, + children: [new Paragraph(``)], + }), ), ], }), @@ -1198,7 +1609,11 @@ export default class AgencyContract { children: [ Col1('财务(发票):'), ...[null, null, null].map( - (col) => new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] }), + (col) => + new TableCell({ + borders: tableBorderOne, + children: [new Paragraph(``)], + }), ), ], }), @@ -1268,53 +1683,61 @@ export default class AgencyContract { text: '签名盖章', spacing: { before: 600, after: 200 }, children: [ - new TextRun({ break: 2, children: ['甲方:', new Tab(), '乙方:桂林海纳国际旅行社有限公司'] }), - new TextRun({ break: 1, children: ['负责人签字盖章', new Tab(), '负责人签字盖章'] }), - new TextRun({ break: 3, children: ['联系人: ', new Tab(), '联系人:游佳佳'] }), - new TextRun({ break: 1, children: ['电话: ', new Tab(), '电话:13507831547'] }), - new TextRun({ break: 1, children: ['电子邮箱: ', new Tab(), '电子邮箱:xeniayou@hainatravel.com'] }), - new TextRun({ break: 1, children: [' 年 月 日', new Tab(), ' 年 月 日'] }), + new TextRun({ + break: 2, + children: ['甲方:', new Tab(), '乙方:桂林海纳国际旅行社有限公司'], + }), + new TextRun({ + break: 1, + children: ['负责人签字盖章', new Tab(), '负责人签字盖章'], + }), + new TextRun({ + break: 3, + children: ['联系人: ', new Tab(), '联系人:游佳佳'], + }), + new TextRun({ + break: 1, + children: ['电话: ', new Tab(), '电话:13507831547'], + }), + new TextRun({ + break: 1, + children: ['电子邮箱: ', new Tab(), '电子邮箱:xeniayou@hainatravel.com'], + }), + new TextRun({ + break: 1, + children: [' 年 月 日', new Tab(), ' 年 月 日'], + }), ], tabStops: [{ type: TabStopType.LEFT, position: 4800 }], }), - // new Paragraph({ - // tabStops: [{ type: TabStopType.RIGHT, position: 1000 }], - // children: [ - // new TextRun({ - // children: [ - // '\t年', - // '\t\t月', - // '\t\t日', - // new PositionalTab({ - // alignment: PositionalTabAlignment.CENTER, - // relativeTo: PositionalTabRelativeTo.MARGIN, - // leader: PositionalTabLeader.NONE, - // }), - // // new Tab(), - // '\t年', - // '\t\t月', - // '\t\t日', - // ], - // }), - // ], - // }), ] } + createTableHeader(text, w = null, opt = {}) { const textP = typeof text === 'string' ? [text] : text return new TableCell({ borders: tableBorderOne, - width: { size: w || `8mm`, type: w ? WidthType.PERCENTAGE : WidthType.AUTO }, + width: { + size: w || `8mm`, + type: w ? WidthType.PERCENTAGE : WidthType.AUTO, + }, verticalAlign: AlignmentType.CENTER, - children: textP.map((t) => new Paragraph({ text: t, alignment: AlignmentType.CENTER, style: 'TableHeader' })), + children: textP.map( + (t) => + new Paragraph({ + text: t, + alignment: AlignmentType.CENTER, + style: 'TableHeader', + }), + ), ...opt, }) } + createHeading(text) { return new Paragraph({ text: text, heading: HeadingLevel.HEADING_1, - // thematicBreak: true, alignment: AlignmentType.CENTER, }) } @@ -1323,7 +1746,6 @@ export default class AgencyContract { return new Paragraph({ text: text, heading: HeadingLevel.TITLE, - // thematicBreak: true, alignment: AlignmentType.CENTER, }) } @@ -1335,6 +1757,7 @@ export default class AgencyContract { ...style, }) } + createPageHeaderText(text, style = {}) { return new Paragraph({ alignment: AlignmentType.RIGHT,