From 866433419ce998b465bd3a5ad3733212e2c28501 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 4 Sep 2024 10:33:44 +0800 Subject: [PATCH 1/5] =?UTF-8?q?perf:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AF=BC=E5=87=BAdocx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Print/AgencyContract.jsx | 379 +++++++++++--------- 1 file changed, 217 insertions(+), 162 deletions(-) diff --git a/src/views/products/Print/AgencyContract.jsx b/src/views/products/Print/AgencyContract.jsx index 3aaa855..3ce91a0 100644 --- a/src/views/products/Print/AgencyContract.jsx +++ b/src/views/products/Print/AgencyContract.jsx @@ -22,7 +22,7 @@ import { PositionalTabLeader, } from 'docx'; import dayjs from 'dayjs'; -import { cloneDeep, groupBy, isEmpty, unique, uniqWith } from '@/utils/commons'; +import { cloneDeep, groupBy, isEmpty, isNotEmpty, sortBy, unique, uniqWith } from '@/utils/commons'; // Shoulder Season 平季; peak season 旺季 const isFullYearOrLonger = (year, startDate, endDate) => { @@ -66,69 +66,131 @@ const chunkCarSets = (numbers) => { { lt5: [], lt7: [], lt10: [], gt10: [] } ); }; +const uniqueBySub = (arr) => + arr.filter((subArr1, _, self) => { + return !self.some((subArr2) => { + if (subArr1 === subArr2) return false; // don't compare a subarray with itself + const set1 = new Set(subArr1); + const set2 = new Set(subArr2); + // check if subArr1 is a subset of subArr2 + return [...set1].every((value) => set2.has(value)); + }); + }); +const isFlatUnique = (bigArray) => { + let flattenedArray = bigArray.flat(); + return flattenedArray.every((i) => { + let count = flattenedArray.filter((j) => j === i).length; + return count === 1; + }); +}; const chunkBy = (use_year, dataList = [], by = []) => { - const groupSize = dataList.reduce((a, c) => a.concat((c.quotation || []).reduce((aq, cq) => aq.concat([[cq.group_size_min, cq.group_size_max]]), [])), []); - const allSizeCut = groupBy( - uniqWith(groupSize, (a, b) => a[0] === b[0] && a[1] === b[1]), - (c) => c[0] - ); - // 按max最大值 cut分组 - const sizeSets = Object.keys(allSizeCut).map((s1) => [Number(s1), Math.max(...allSizeCut[s1].map((c) => c[1]))]); - const sizeSetsByMax = uniqWith(sizeSets, (a, b) => a[1] === b[1]); - // // const sizeSetsByMax = [[1,4], [5,6], [7, 9], [10, Infinity]]; // test: 固定车辆座次 - // 按max最小值 cut分组 - // const sizeSets = Object.keys(allSizeCut).map((s1) => [Number(s1), Math.min(...allSizeCut[s1].map((c) => c[1]))]); - // const sizeSetsByMax = uniqWith(sizeSets, (a, b) => a[1] === b[1]); - - const dataRoll = dataList.map((rowp, ii) => { + const dataRollSS = dataList.map((rowp, ii) => { const quotation = rowp.quotation.map((quoteItem) => { - const matchRange = cloneDeep(sizeSetsByMax) - .reverse() - .find((ss) => quoteItem.group_size_min >= ss[0] && quoteItem.group_size_max <= ss[1]); - const inSet = matchRange; - // const matchMax = - // matchRange || - // cloneDeep(sizeSetsByMax) - // // .reverse() - // .find((ss) => quoteItem.group_size_max <= ss[1]); - // const matchMin = - // matchMax || - // cloneDeep(sizeSetsByMax) - // // .reverse() - // .find((ss) => quoteItem.group_size_min >= ss[0]); - // const inSet = matchMin; return { ...quoteItem, quote_season: isFullYearOrLonger(use_year, quoteItem.use_dates_start, quoteItem.use_dates_end) ? 'SS' : 'PS', - quote_size: inSet ? inSet.join('-') : '10-Infinity', }; }); - return { ...rowp, quotation }; + return { ...rowp, quotation, }; }); - - const chunk = dataRoll.map((rowp) => { - const quote_chunk = groupBy(rowp.quotation, (quoteItem) => by.map((key) => quoteItem[key]).join('@')); - return { ...rowp, quote_chunk }; + // 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; + }, {}); + // console.log('PGroupSizeSS', structuredClone(PGroupSizeSS)); + const _SSMinSet = uniqWith(Object.values(PGroupSizeSS), (a, b) => a.join(',') === b.join(',')); + // console.log('SSMinSet', _SSMinSet); + const uSSsizeSetArr = uniqueBySub(_SSMinSet); + // console.log('uSSsizeSetArr', uSSsizeSetArr); + for (const key in PGroupSizeSS) { + if (Object.prototype.hasOwnProperty.call(PGroupSizeSS, key)) { + const element = PGroupSizeSS[key]; + const findSet = uSSsizeSetArr.find(minCut => element.every(v => minCut.includes(v))); + PGroupSizeSS[key] = findSet; + } + } + // console.log('PGroupSizeSS aaa', PGroupSizeSS); + const [SSsizeSets, PSsizeSets] = [uSSsizeSetArr, []].map((arr) => { + const _arr = structuredClone(arr); + const arrSets = _arr.map(keyMins => keyMins.reduce((acc, curr, idx, minsArr) => { + const _max = idx === minsArr.length - 1 ? Infinity : Number(minsArr[idx + 1])-1; + acc.push([Number(curr), _max]); + return acc; + }, [])); + return arrSets; }); - const dataSource = chunk.map((rowp, ri) => { - const thisChunk = Object.keys(rowp.quote_chunk).reduce((qc, ckey) => { + // 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 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 ? Infinity : Number(minsArr[idx + 1])-1; + acc.push([Number(curr), _max]); + return acc; + }, []); + 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; + // 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; + }); + const quote_chunk_flat = groupBy(_quotation, (quoteItem2) => by.map((key) => quoteItem2[key]).join('@')); + const quote_chunk = Object.keys(quote_chunk_flat).reduce((qc, ckey) => { const ckeyArr = ckey.split('@'); if (isEmpty(qc[ckeyArr[0]])) { - qc[ckeyArr[0]] = ckeyArr[1] ? { [ckeyArr[1]]: rowp.quote_chunk[ckey] } : rowp.quote_chunk[ckey]; + qc[ckeyArr[0]] = ckeyArr[1] ? { [ckeyArr[1]]: quote_chunk_flat[ckey] } : quote_chunk_flat[ckey]; } else { - qc[ckeyArr[0]][ckeyArr[1]] = (qc[ckeyArr[0]][ckeyArr[1]] || []).concat(rowp.quote_chunk[ckey]); + qc[ckeyArr[0]][ckeyArr[1]] = (qc[ckeyArr[0]][ckeyArr[1]] || []).concat(quote_chunk_flat[ckey]); } return qc; }, {}); - - return { ...rowp, quote_chunk: thisChunk }; + return { + ...rowp, + sizeSetsSS: pkey, + quotation: _quotation, + quote_chunk, + } }); - const allquotation = chunk.reduce((a, c) => a.concat(c.quotation), []); + // console.log('quotation', chunkSS); + const allquotation = chunkSS.reduce((a, c) => a.concat(c.quotation), []); // 取出两季相应的时效区间 const SSRange = unique((allquotation || []).filter((q) => q.quote_season === 'SS').map((qr) => `${qr.use_dates_start}~${qr.use_dates_end}`)); const PSRange = unique((allquotation || []).filter((q) => q.quote_season === 'PS').map((qr) => `${qr.use_dates_start}~${qr.use_dates_end}`)); - // console.log('chunk', chunk, dataSource, sizeSets, sizeSetsByMax); - return { chunk, dataSource, sizeSets: sizeSetsByMax, SSRange, PSRange }; + + return { chunk: chunkSS, dataSource: chunkSS, SSRange, PSRange, ...compactSizeSets }; }; const DOC_TITLE = '地接合同'; @@ -158,7 +220,7 @@ export default class AgencyContract { 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('*********************************************************************************************'); + // console.log('*********************************************************************************************'); const document = new Document({ creator: 'China Highlights', @@ -331,23 +393,22 @@ export default class AgencyContract { : [this.createSubHeading(`综费`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_6Q(use_year, agencyProducts['6'])]), ...(isEmpty(agencyProducts['B']) ? [] - : [this.createSubHeading(`超公里`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_B(use_year, agencyProducts['B'])]), + : [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_JD(use_year, agencyProducts['J'])]), ...(isEmpty(agencyProducts['Q']) ? [] : [this.createSubHeading(`导游`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_6Q(use_year, agencyProducts['Q'])]), - // this.createTable_Q(use_year, agencyProducts['Q']), ...(isEmpty(agencyProducts['7']) ? [] : [this.createSubHeading(`景点`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_7(use_year, agencyProducts['7'], agencyExtras)]), ...(isEmpty(agencyProducts['R']) ? [] - : [this.createSubHeading(`餐费`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_R(use_year, agencyProducts['R'], agencyExtras)]), + : [this.createSubHeading(`餐费`, { numbering: { reference: 'products-type', level: 0 } }), ...this.createTable_R(use_year, agencyProducts['R'], agencyExtras)]), ...(isEmpty(agencyProducts['D']) ? [] - : [this.createSubHeading(`包价线路`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_JD(use_year, agencyProducts['D'])]), + : [this.createSubHeading(`包价线路`, { numbering: { reference: 'products-type', level: 0 } }), ...this.createTable_JD(use_year, agencyProducts['D'])]), this.createSubHeading(`公司银行账户`, { numbering: { reference: 'products-type', level: 0 } }), this.createInfo_Bank(), this.createSubHeading(`联系资料`, { numbering: { reference: 'products-type', level: 0 } }), @@ -389,7 +450,8 @@ export default class AgencyContract { * 超公里 */ createTable_B(use_year, dataList) { - const { chunk: dataSource, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_size']); + // console.log('*********************************************************************************************'); + const { chunk, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_size']); const infoCol = (rowp) => new TableCell({ borders: tableBorderOne, @@ -401,32 +463,41 @@ export default class AgencyContract { }), ], }); + const tableS = sizeSets.reduce((acc, size) => { + const subTable = new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [ + this.createTableHeader('超公里项目', 20), + this.createTableHeader('往返公里数', 20), + this.createTableHeader(['全年', ...SSRange, ...PSRange], 60, { columnSpan: size.length }), + ], + }), + 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: '' })] }), + ...size.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + ], + }), + ...chunk + .filter((p) => p.sizeSetsSS === size.map((mr) => mr[0]).join(',')) + .map((row, ri) => this.createDetailRowSize(size, row, ri, { infoCol })) + .reduce((a, c) => a.concat(c), []), - return new Table({ - borders: tableBorderNone, - width: { - size: 100, - type: WidthType.PERCENTAGE, - }, - rows: [ - new TableRow({ - children: [ - this.createTableHeader('超公里项目', 20), - this.createTableHeader('往返公里数', 20), - this.createTableHeader(['全年', ...SSRange, ...PSRange], 60, { columnSpan: sizeSets.length }), - ], - }), - 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: '' })] }), - ...sizeSets.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: ss.join('-') })] })), - ], - }), - ...dataSource.map((row, ri) => this.createDetailRowSize(sizeSets, row, ri, { infoCol })).reduce((a, c) => a.concat(c), []), - this.createTable_Memo(2 + sizeSets.length), - ], - }); + this.createTable_Memo(2 + size.length), + ], + }); + acc.push(subTable); + acc.push(new Paragraph({ text: `` })); + return acc; + }, []); + return tableS; } /** @@ -434,35 +505,42 @@ export default class AgencyContract { * 包价线路 */ createTable_JD(use_year, dataList) { - const { chunk, dataSource, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_season', 'quote_size']); - // console.log(chunk, dataSource, sizeSets); - - return new Table({ - borders: tableBorderNone, - width: { - size: 100, - type: WidthType.PERCENTAGE, - }, - rows: [ - new TableRow({ - children: [ - this.createTableHeader('项目', 20), - this.createTableHeader(['平季', ...SSRange], 40, { columnSpan: sizeSets.length }), - this.createTableHeader(['旺季', ...PSRange], 40, { columnSpan: sizeSets.length }), - ], - }), - new TableRow({ - children: [ - new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), - ...sizeSets.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: ss.join('-') })] })), - ...sizeSets.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: ss.join('-') })] })), - ], - }), - ...dataSource.map((row, ri) => this.createDetailRowSize(sizeSets, row, ri, { withSeason: true })).reduce((a, c) => a.concat(c), []), + const { dataSource, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_season', 'quote_size']); + + 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('项目', 20), + tableHeader(['平季', ...SSRange], 40, { columnSpan: setItem.length }), + tableHeader(['旺季', ...PSRange], 40, { columnSpan: setItem.length }), + ], + }), + new TableRow({ + children: [ + new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), + ...setItem.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + ...setItem.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + ], + }), + ...dataSource.filter((p) => p.sizeSetsSS === setItem.map((mr) => mr[0]).join(',')) + .map((row, ri) => this.createDetailRowSize(setItem, row, ri, { withSeason: true })).reduce((a, c) => a.concat(c), []), - this.createTable_Memo(1 + sizeSets.length * 2), - ], - }); + this.createTable_Memo(1 + setItem.length * 2), + ], + }); + rt.push(subTable); + rt.push(new Paragraph({ text: `` })); + return rt; + }, []); + return tableS; } /** * 导游 @@ -559,69 +637,46 @@ export default class AgencyContract { ], }); - return new Table({ - borders: tableBorderNone, - width: { - size: 100, - type: WidthType.PERCENTAGE, - }, - rows: [ - new TableRow({ - children: [ - this.createTableHeader('社会餐厅午餐/晚餐', 25), - this.createTableHeader(['价格', ...SSRange, ...PSRange], 50, { columnSpan: sizeSets.length }), - this.createTableHeader('司陪餐补', 25), - ], - }), - new TableRow({ - children: [ - new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), - ...sizeSets.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: ss.join('-') })] })), - // new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, rowSpan: dataList.length+1, children: [new Paragraph({ text: '' })] }), - new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), - ], - }), - ...chunk.map((row, ri) => this.createDetailRowSize(sizeSets, row, ri, { withSize: false, extraCol })).reduce((a, c) => a.concat(c), []), - // ...dataSource.map((row, ri) => this.createDetailRowSize(sizeSets, row, ri, true)).reduce((a, c) => a.concat(c), []), + const tableS = sizeSets.reduce((acc, size) => { + const subTable = new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [ + this.createTableHeader('社会餐厅午餐/晚餐', 25), + this.createTableHeader(['价格', ...SSRange, ...PSRange], 50, { columnSpan: size.length }), + this.createTableHeader('司陪餐补', 25), + ], + }), + new TableRow({ + children: [ + new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), + ...size.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + // new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, rowSpan: dataList.length+1, children: [new Paragraph({ text: '' })] }), + new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), + ], + }), + ...chunk.filter((p) => p.sizeSetsSS === size.map((mr) => mr[0]).join(',')) + .map((row, ri) => this.createDetailRowSize(size, row, ri, { withSize: false, extraCol })).reduce((a, c) => a.concat(c), []), - this.createTable_Memo(1 + sizeSets.length + 1), - ], - }); + this.createTable_Memo(1 + size.length + 1), + ], + }); + acc.push(subTable); + acc.push(new Paragraph({ text: `` })); + return acc; + }, []); + return tableS; } /** * 包价线路 * @deprecated */ - createTable_D(use_year, dataList) { - const { chunk, dataSource, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_season', 'quote_size']); - - return new Table({ - borders: tableBorderNone, - width: { - size: 100, - type: WidthType.PERCENTAGE, - }, - rows: [ - new TableRow({ - children: [ - this.createTableHeader('项目', 30), - this.createTableHeader(['平季', ...SSRange], 35, { columnSpan: sizeSets.length }), - this.createTableHeader(['旺季', ...PSRange], 35, { columnSpan: sizeSets.length }), - ], - }), - new TableRow({ - children: [ - new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), - ...sizeSets.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: ss.join('-') })] })), - ...sizeSets.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: ss.join('-') })] })), - ], - }), - ...dataSource.map((row, ri) => this.createDetailRowSize(sizeSets, row, ri, { withSeason: true })).reduce((a, c) => a.concat(c), []), - - this.createTable_Memo(1 + sizeSets.length * 2), - ], - }); - } + createTable_D(use_year, dataList) {} createDetailRowSize(sizeSets, rowp, ii, { withSeason = false, withSize = true, infoCol = () => {}, extraCol = () => {} } = {}) { const sizeCol = sizeSets.map((ss) => ss.join('-')); From df64cc306a5619658c84d0ae237b97d5f219d961 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 4 Sep 2024 10:34:39 +0800 Subject: [PATCH 2/5] =?UTF-8?q?todo:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AF=BC=E5=87=BAdocx:=20+v0903?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Detail/Header.jsx | 2 +- ...yContract.jsx => AgencyContract_v0903.jsx} | 0 .../products/Print/AgencyContract_v3.jsx | 1013 +++++++++++++++++ 3 files changed, 1014 insertions(+), 1 deletion(-) rename src/views/products/Print/{AgencyContract.jsx => AgencyContract_v0903.jsx} (100%) create mode 100644 src/views/products/Print/AgencyContract_v3.jsx diff --git a/src/views/products/Detail/Header.jsx b/src/views/products/Detail/Header.jsx index 796ec99..0fe5e40 100644 --- a/src/views/products/Detail/Header.jsx +++ b/src/views/products/Detail/Header.jsx @@ -15,7 +15,7 @@ import VendorSelector from '@/components/VendorSelector'; import AuditStateSelector from '@/components/AuditStateSelector'; import NewProductModal from './NewProductModal'; -import AgencyContract from './../Print/AgencyContract'; +import AgencyContract from '../Print/AgencyContract_v3'; import { saveAs } from "file-saver"; import { Packer } from "docx"; diff --git a/src/views/products/Print/AgencyContract.jsx b/src/views/products/Print/AgencyContract_v0903.jsx similarity index 100% rename from src/views/products/Print/AgencyContract.jsx rename to src/views/products/Print/AgencyContract_v0903.jsx diff --git a/src/views/products/Print/AgencyContract_v3.jsx b/src/views/products/Print/AgencyContract_v3.jsx new file mode 100644 index 0000000..3ce91a0 --- /dev/null +++ b/src/views/products/Print/AgencyContract_v3.jsx @@ -0,0 +1,1013 @@ +import { + AlignmentType, + Document, + Header, + Footer, + HeadingLevel, + Paragraph, + TabStopType, + Table, + TableCell, + TableRow, + TextRun, + BorderStyle, + NumberFormat, + PageNumber, + WidthType, + LevelFormat, + Tab, + PositionalTab, + PositionalTabAlignment, + PositionalTabRelativeTo, + PositionalTabLeader, +} from 'docx'; +import dayjs from 'dayjs'; +import { cloneDeep, groupBy, isEmpty, isNotEmpty, sortBy, unique, uniqWith } from '@/utils/commons'; + +// Shoulder Season 平季; peak season 旺季 +const isFullYearOrLonger = (year, startDate, endDate) => { + // Parse the dates + const start = dayjs(startDate, 'YYYY-MM-DD'); + const end = dayjs(endDate, 'YYYY-MM-DD'); + + // Create the start and end dates for the year + const yearStart = dayjs(`${year}-01-01`, 'YYYY-MM-DD'); + const yearEnd = dayjs(`${year}-12-31`, 'YYYY-MM-DD'); + + // Check if start is '01-01' and end is '12-31' and the year matches + const isFullYear = start.isSame(yearStart, 'day') && end.isSame(yearEnd, 'day'); + + // Check if the range is longer than a year + const isLongerThanYear = end.diff(start, 'year') >= 1; + + return isFullYear || isLongerThanYear; +}; + +const shortDate = (date) => dayjs(date).format('MM.DD'); + +/** + * 车辆人等配置 + * 5座, 7座, 10座, 10以上 + */ +const chunkCarSets = (numbers) => { + return numbers.reduce( + (acc, number) => { + if (number < 5) { + acc.lt5.push(number); + } else if (number < 7) { + acc.lt7.push(number); + } else if (number < 10) { + acc.lt7.push(number); + } else { + acc.gt10.push(number); + } + return acc; + }, + { lt5: [], lt7: [], lt10: [], gt10: [] } + ); +}; +const uniqueBySub = (arr) => + arr.filter((subArr1, _, self) => { + return !self.some((subArr2) => { + if (subArr1 === subArr2) return false; // don't compare a subarray with itself + const set1 = new Set(subArr1); + const set2 = new Set(subArr2); + // check if subArr1 is a subset of subArr2 + return [...set1].every((value) => set2.has(value)); + }); + }); +const isFlatUnique = (bigArray) => { + let flattenedArray = bigArray.flat(); + return flattenedArray.every((i) => { + let count = flattenedArray.filter((j) => j === i).length; + return count === 1; + }); +}; +const chunkBy = (use_year, dataList = [], by = []) => { + const dataRollSS = dataList.map((rowp, ii) => { + const quotation = rowp.quotation.map((quoteItem) => { + return { + ...quoteItem, + quote_season: isFullYearOrLonger(use_year, quoteItem.use_dates_start, quoteItem.use_dates_end) ? 'SS' : 'PS', + }; + }); + 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; + }, {}); + // console.log('PGroupSizeSS', structuredClone(PGroupSizeSS)); + const _SSMinSet = uniqWith(Object.values(PGroupSizeSS), (a, b) => a.join(',') === b.join(',')); + // console.log('SSMinSet', _SSMinSet); + const uSSsizeSetArr = uniqueBySub(_SSMinSet); + // console.log('uSSsizeSetArr', uSSsizeSetArr); + for (const key in PGroupSizeSS) { + if (Object.prototype.hasOwnProperty.call(PGroupSizeSS, key)) { + const element = PGroupSizeSS[key]; + const findSet = uSSsizeSetArr.find(minCut => element.every(v => minCut.includes(v))); + PGroupSizeSS[key] = findSet; + } + } + // console.log('PGroupSizeSS aaa', PGroupSizeSS); + const [SSsizeSets, PSsizeSets] = [uSSsizeSetArr, []].map((arr) => { + const _arr = structuredClone(arr); + const arrSets = _arr.map(keyMins => keyMins.reduce((acc, curr, idx, minsArr) => { + const _max = idx === minsArr.length - 1 ? Infinity : Number(minsArr[idx + 1])-1; + acc.push([Number(curr), _max]); + return acc; + }, [])); + 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 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 ? Infinity : Number(minsArr[idx + 1])-1; + acc.push([Number(curr), _max]); + return acc; + }, []); + 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; + // 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; + }); + const quote_chunk_flat = groupBy(_quotation, (quoteItem2) => by.map((key) => quoteItem2[key]).join('@')); + const quote_chunk = Object.keys(quote_chunk_flat).reduce((qc, ckey) => { + const ckeyArr = ckey.split('@'); + if (isEmpty(qc[ckeyArr[0]])) { + qc[ckeyArr[0]] = ckeyArr[1] ? { [ckeyArr[1]]: quote_chunk_flat[ckey] } : quote_chunk_flat[ckey]; + } else { + qc[ckeyArr[0]][ckeyArr[1]] = (qc[ckeyArr[0]][ckeyArr[1]] || []).concat(quote_chunk_flat[ckey]); + } + return qc; + }, {}); + return { + ...rowp, + sizeSetsSS: pkey, + quotation: _quotation, + quote_chunk, + } + }); + // console.log('quotation', chunkSS); + const allquotation = chunkSS.reduce((a, c) => a.concat(c.quotation), []); + // 取出两季相应的时效区间 + const SSRange = unique((allquotation || []).filter((q) => q.quote_season === 'SS').map((qr) => `${qr.use_dates_start}~${qr.use_dates_end}`)); + const PSRange = unique((allquotation || []).filter((q) => q.quote_season === 'PS').map((qr) => `${qr.use_dates_start}~${qr.use_dates_end}`)); + + return { chunk: chunkSS, dataSource: chunkSS, SSRange, PSRange, ...compactSizeSets }; +}; + +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' }, +}; + +export default class AgencyContract { + 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: [ + { + 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, + }, + }, + }, + // { + // 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 }] }, + ], + }, + sections: [ + { + properties: { + page: { + pageNumbers: { + start: 1, + formatType: NumberFormat.DECIMAL, + }, + margin: pageMargins, + }, + }, + headers: { + default: new Header({ + children: [ + this.createPageHeaderText(`Email: xeniayou@hainatravel.com`, { italics: false }), + this.createPageHeaderText(`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: [ + this.createTitle(h1), + this.createSubHeading(`价格有效期: ${yearStart}-${yearEnd}`, { alignment: AlignmentType.CENTER }), + + ...(isEmpty(agencyProducts['6']) + ? [] + : [this.createSubHeading(`综费`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_6Q(use_year, agencyProducts['6'])]), + ...(isEmpty(agencyProducts['B']) + ? [] + : [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'])]), + ...(isEmpty(agencyProducts['Q']) + ? [] + : [this.createSubHeading(`导游`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_6Q(use_year, agencyProducts['Q'])]), + ...(isEmpty(agencyProducts['7']) + ? [] + : [this.createSubHeading(`景点`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_7(use_year, agencyProducts['7'], agencyExtras)]), + ...(isEmpty(agencyProducts['R']) + ? [] + : [this.createSubHeading(`餐费`, { numbering: { reference: 'products-type', level: 0 } }), ...this.createTable_R(use_year, agencyProducts['R'], agencyExtras)]), + ...(isEmpty(agencyProducts['D']) + ? [] + : [this.createSubHeading(`包价线路`, { numbering: { reference: 'products-type', level: 0 } }), ...this.createTable_JD(use_year, agencyProducts['D'])]), + this.createSubHeading(`公司银行账户`, { numbering: { reference: 'products-type', level: 0 } }), + this.createInfo_Bank(), + this.createSubHeading(`联系资料`, { numbering: { reference: 'products-type', level: 0 } }), + this.createInfo_Contact(), + this.createSubHeading(`补充条款`, { numbering: { reference: 'products-type', level: 0 } }), + ...this.createInfo_Terms(), + ], + }, + ], + }); + + return document; + } + + /** + * 综费 + * 导游 + */ + createTable_6Q(use_year, dataList, style = {}) { + const { chunk, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_season']); + + return new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [this.createTableHeader('项目', 30), this.createTableHeader(['平季', ...SSRange], 35), this.createTableHeader(['旺季', ...PSRange], 35)], + }), + ...chunk.map((row, ri) => this.createDetailRowSeason2(row, ri)).reduce((a, c) => a.concat(c), []), + + this.createTable_Memo(3), + ], + }); + } + /** + * 超公里 + */ + createTable_B(use_year, dataList) { + // console.log('*********************************************************************************************'); + const { chunk, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_size']); + const infoCol = (rowp) => + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + new Paragraph({ + text: `${rowp?.info?.km}`, + alignment: AlignmentType.CENTER, + }), + ], + }); + const tableS = sizeSets.reduce((acc, size) => { + const subTable = new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [ + this.createTableHeader('超公里项目', 20), + this.createTableHeader('往返公里数', 20), + this.createTableHeader(['全年', ...SSRange, ...PSRange], 60, { columnSpan: size.length }), + ], + }), + 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: '' })] }), + ...size.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + ], + }), + ...chunk + .filter((p) => p.sizeSetsSS === size.map((mr) => mr[0]).join(',')) + .map((row, ri) => this.createDetailRowSize(size, row, ri, { infoCol })) + .reduce((a, c) => a.concat(c), []), + + this.createTable_Memo(2 + size.length), + ], + }); + acc.push(subTable); + acc.push(new Paragraph({ text: `` })); + return acc; + }, []); + return tableS; + } + + /** + * 车费 + * 包价线路 + */ + createTable_JD(use_year, dataList) { + const { dataSource, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_season', 'quote_size']); + + 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('项目', 20), + tableHeader(['平季', ...SSRange], 40, { columnSpan: setItem.length }), + tableHeader(['旺季', ...PSRange], 40, { columnSpan: setItem.length }), + ], + }), + new TableRow({ + children: [ + new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), + ...setItem.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + ...setItem.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + ], + }), + ...dataSource.filter((p) => p.sizeSetsSS === setItem.map((mr) => mr[0]).join(',')) + .map((row, ri) => this.createDetailRowSize(setItem, row, ri, { withSeason: true })).reduce((a, c) => a.concat(c), []), + + this.createTable_Memo(1 + setItem.length * 2), + ], + }); + rt.push(subTable); + rt.push(new Paragraph({ text: `` })); + return rt; + }, []); + return tableS; + } + /** + * 导游 + * @ignore + * @deprecated + */ + createTable_Q(use_year, dataList) {} + /** + * 景点 + */ + createTable_7(use_year, dataList, agencyExtras) { + const withExtras = dataList.map((row) => ({ ...row, extras: agencyExtras[row.info.id] || [] })); + const { chunk, sizeSets, SSRange, PSRange } = chunkBy(use_year, withExtras, ['quote_season']); + const extraCol = (rowp, i) => { + return new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + ...(rowp.extras || []).reduce((ac, extra) => { + const allSize = unique(extra.quotation.map((eq) => `${eq.group_size_min}-${eq.group_size_max}`)); + const allDates = unique(extra.quotation.map((eq) => `${eq.use_dates_start}-${eq.use_dates_end}`)); + // const compactBy = allSize.length > 1 ? 'size' : allDates.length > 1 ? 'dates' : 'all'; + const compactBy = allSize.length < allDates.length ? 'size' : 'dates'; + const compactQuotation = groupBy( + extra.quotation, + (ExtraQuoteItem) => + `${ExtraQuoteItem.adult_cost}@${ExtraQuoteItem.unit_id}.` + + (compactBy === 'size' ? `[${ExtraQuoteItem.group_size_min}.${ExtraQuoteItem.group_size_max}]` : `${ExtraQuoteItem.use_dates_start},${ExtraQuoteItem.use_dates_end}`) + ); + // console.log(compactQuotation); + const thisE = new Paragraph({ + 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); + const date2 = shortDate(firstQ.use_dates_end); + const othersDates = others.map((oq) => `${shortDate(oq.use_dates_start)}~${shortDate(oq.use_dates_end)}`).join(', '); + const otherStr = isEmpty(othersDates) ? '' : `, ${othersDates}`; + return new Paragraph({ + children: [ + // new TextRun({ text: `${extra.info.product_title} ${firstQ.unit_name}`, bold: true }), + new TextRun({ + text: + `${firstQ.adult_cost}, ${firstQ.group_size_min}-${firstQ.group_size_max}人, ${firstQ.unit_name}` + + // + (`, ${firstQ.group_size_min}-${firstQ.group_size_max}人`) + (compactBy !== 'dates' ? `, ${date1}~${date2}${otherStr}` : ''), + }), + ], + }); + }); + return ac.concat([thisE, ...thisP]); + // return new Paragraph({ text: `${extra.info.product_title} ${extra.quotation?.[0]?.adult_cost || ''} ${extra.quotation?.[0]?.unit_name || ''}` }); + }, []), + ], + }); + }; + + return new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [ + this.createTableHeader('景点', 25), + this.createTableHeader(['平季', ...SSRange], 25), + this.createTableHeader(['旺季', ...PSRange], 25), + this.createTableHeader('附加费用', 25), + ], + }), + ...chunk.map((row, ri) => this.createDetailRowSeason2(row, ri, { extraCol })).reduce((a, c) => a.concat(c), []), + + this.createTable_Memo(1 + sizeSets.length * 2 + 1), + ], + }); + } + /** + * 餐费 + */ + createTable_R(use_year, dataList, agencyExtras) { + const withExtras = dataList.map((row) => ({ ...row, extras: agencyExtras[row.info.id] || [] })); + const { chunk, sizeSets, SSRange, PSRange } = chunkBy(use_year, withExtras, ['quote_size']); + const extraCol = (rowp, i) => + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + ...(rowp.extras || []).map( + (extra) => new Paragraph({ text: `${extra.info.product_title} ${extra.quotation?.[0]?.adult_cost || ''} ${extra.quotation?.[0]?.unit_name || ''}` }) + ), + // new Paragraph({ text: '' }) + ], + }); + + const tableS = sizeSets.reduce((acc, size) => { + const subTable = new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [ + this.createTableHeader('社会餐厅午餐/晚餐', 25), + this.createTableHeader(['价格', ...SSRange, ...PSRange], 50, { columnSpan: size.length }), + this.createTableHeader('司陪餐补', 25), + ], + }), + new TableRow({ + children: [ + new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), + ...size.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + // new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, rowSpan: dataList.length+1, children: [new Paragraph({ text: '' })] }), + new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), + ], + }), + ...chunk.filter((p) => p.sizeSetsSS === size.map((mr) => mr[0]).join(',')) + .map((row, ri) => this.createDetailRowSize(size, row, ri, { withSize: false, extraCol })).reduce((a, c) => a.concat(c), []), + + this.createTable_Memo(1 + size.length + 1), + ], + }); + acc.push(subTable); + acc.push(new Paragraph({ text: `` })); + return acc; + }, []); + return tableS; + } + /** + * 包价线路 + * @deprecated + */ + createTable_D(use_year, dataList) {} + + createDetailRowSize(sizeSets, rowp, ii, { withSeason = false, withSize = true, infoCol = () => {}, extraCol = () => {} } = {}) { + const sizeCol = sizeSets.map((ss) => ss.join('-')); + + 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, + children: [ + new Paragraph({ + text: `${rowp?.info?.product_title}`, + alignment: AlignmentType.LEFT, + }), + ], + }), + infoCol(rowp), + ...sizeCol.map( + (ss) => + 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.LEFT, + text: isEmpty(quoteItem) + ? '' + : withSize + ? `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}` + : `${quoteItem.adult_cost}`, + }) + ), + // new Paragraph({ text: '-'+ii }) + ], + }) + ), + ...(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.LEFT, + // text: `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}${quoteItem.unit_name}`, + text: withSize + ? `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}` + : `${quoteItem.adult_cost}`, + }) + ), + ], + }) + ) + : []), + extraCol(rowp, ii), + ], + }), + ]; + } + createDetailRowSeason2(rowp, ii, { extraCol = () => {} } = {}) { + 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, + children: [ + new Paragraph({ + text: `${rowp?.info?.product_title}`, + alignment: AlignmentType.LEFT, + }), + ], + }), + new TableCell({ + borders: tableBorderOne, + width: { size: 2000, type: WidthType.DXA }, + verticalAlign: AlignmentType.CENTER, + children: [ + ...(rowp.quote_chunk['SS'] || []).map( + (quoteItem, ii) => + new Paragraph({ + alignment: AlignmentType.LEFT, + text: `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}`, + }) + ), + ], + }), + 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.LEFT, + text: `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}`, + }) + ), + ], + }), + extraCol(rowp, ii), + ], + }), + ]; + } + + createTable_Memo(columnSpan) { + return new TableRow({ + children: [ + new TableCell({ + borders: tableBorderOne, + // verticalAlign: AlignmentType.CENTER, + columnSpan, + children: [new Paragraph(`备注: `), new Paragraph(``)], + }), + ], + }); + } + + createInfo_Bank() { + const Col1 = (label, opt = {}) => + new TableCell({ + borders: tableBorderOne, + width: { size: 2000, type: WidthType.DXA }, + // verticalAlign: AlignmentType.CENTER, + children: [new Paragraph(label)], + ...opt, + }); + return new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [Col1('帐户:'), new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] })], + }), + new TableRow({ + children: [Col1('银行:'), new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] })], + }), + new TableRow({ + children: [Col1('帐号:'), new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] })], + }), + new TableRow({ + children: [Col1('结账方式:'), new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] })], + }), + ], + }); + } + createInfo_Contact() { + const Col1 = (label, opt = {}) => + new TableCell({ + borders: tableBorderOne, + width: { size: 2000, type: WidthType.DXA }, + // verticalAlign: AlignmentType.CENTER, + children: [new Paragraph(label)], + ...opt, + }); + return new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [this.createTableHeader('联系人', 25), this.createTableHeader('电话', 25), this.createTableHeader('微信', 25), this.createTableHeader('手机', 25)], + }), + new TableRow({ + children: [Col1('经理:'), ...[null, null, null].map((col) => new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] }))], + }), + new TableRow({ + children: [Col1('计调:'), ...[null, null, null].map((col) => new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] }))], + }), + new TableRow({ + children: [Col1('财务(发票):'), ...[null, null, null].map((col) => new TableCell({ borders: tableBorderOne, children: [new Paragraph(``)] }))], + }), + ], + }); + } + createInfo_Terms() { + const subHeader = (text) => + new Paragraph({ + alignment: AlignmentType.CENTER, + outlineLevel: 1, + spacing: { after: 200, before: 200 }, + style: 'TableHeader', + children: [new TextRun({ text: `${text}`, bold: true, size: 28 })], + }); + const numberingContent = ([abstract, ...content], instance) => [ + new Paragraph({ text: abstract, indent: { firstLine: `2pc` } }), + ...content.map( + ([itemTitle, ...itemList]) => + new Paragraph({ + text: itemTitle, + numbering: { reference: 'terms', level: 0, instance }, + children: itemList.map((li) => new TextRun({ text: li, break: 1 })), + }) + ), + ]; + + return [ + subHeader('中华游专利产品接待权标准'), + ...numberingContent( + [ + '中华游是注重特色新产品开发的品牌,并希望通过开发新产品丰富中华游的网前线路,吸引更多的客户与中华游订购中国之旅。为了更好的鼓励各个地接社积极开发新产品、新线路,特制定中华游专利产品接待权标准,详情如下:', + ['新产品,新线路定义及专利期', '新产品,新线路非市场上现成的新产品,新体验,而是包含了独家设计或独家资源的产品和体验'], + ['中华游和地接社联合设计研发的新产品', '由中华游发起立意和产品框架', '地接社协助填充具体内容并落地', '专利期:6个月'], + ['地接社自主设计研发并落地(中华游或做细微调整)', '如:北京“民生探索”', '专利期:永久 (除非地接社已无法掌握产品或资源的独家性)'], + ], + 1 + ), + subHeader('关于价格变更'), + ...numberingContent( + [ + '中华游与各地接社签订年度地接协议后,将不认同中途涨价的要求。但考虑在实际合作中存在的不可控因素,为维护双方共同利益,两种价格变更情况处理方式如下', + [ + '常规价格变更:', + '一般景区和酒店会提前3个月通知价格变更事宜,要求地接社及时用聊天工具或邮件形式通知中华游产品采购,并以收到相同形式确认回执为准,否则中华游将有权拒付额外增加的一切费用。额外增加费用的承担规则为已确认团队计划保持原价,新价格仅适用于确认回复日期起的新发送团队计划。', + ], + [ + '临时价格变更:', + '一般属于政府行为或景区强制要求,如不立即交款则无法参观等紧急情况。因此种情况产生的额外费用要求第一时间通知中华游产品采购,并秉承维护双方共同利益与长期合作,共同协商友好解决方案。', + ], + ], + 2 + ), + new Paragraph({ + 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(), ' 年 月 日'] }), + ], + 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 }, + verticalAlign: AlignmentType.CENTER, + 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, + }); + } + + createTitle(text) { + return new Paragraph({ + text: text, + heading: HeadingLevel.TITLE, + // thematicBreak: true, + alignment: AlignmentType.CENTER, + }); + } + + createSubHeading(text, style = {}) { + return new Paragraph({ + text: text, + heading: HeadingLevel.HEADING_1, + ...style, + }); + } + createPageHeaderText(text, style = {}) { + return new Paragraph({ + alignment: AlignmentType.RIGHT, + children: [ + new TextRun({ + text: text, + italics: true, + size: 20, + ...style, + }), + ], + }); + } +} From 1f574acab3a0222e889b97117eed59b2d7133328 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 4 Sep 2024 10:52:29 +0800 Subject: [PATCH 3/5] =?UTF-8?q?perf:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20`=E5=90=8D=E7=A7=B0`=E7=BC=96=E8=BE=91=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=88=B0=E4=B8=AD=E6=96=87=E8=AF=AD=E7=A7=8D`?= =?UTF-8?q?=E5=90=8D=E7=A7=B0`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Detail/ProductInfoForm.jsx | 9 ++++++++- src/views/products/Detail/ProductInfoLgc.jsx | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/views/products/Detail/ProductInfoForm.jsx b/src/views/products/Detail/ProductInfoForm.jsx index 65e6e0d..b470b8a 100644 --- a/src/views/products/Detail/ProductInfoForm.jsx +++ b/src/views/products/Detail/ProductInfoForm.jsx @@ -29,6 +29,8 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editablePerm, infoEditabl const filedsets = useProductsTypesFieldsets(editingProduct?.info?.product_type_id); const shows = filedsets[0]; + const [pickEditedInfo, setPickEditedInfo] = useState({}); // 传递联动的字段 + // const [editable, setEditable] = useState(true); const [formEditable, setFormEditable] = useState(true); const [showSave, setShowSave] = useState(true); @@ -39,6 +41,7 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editablePerm, infoEditabl const lgc_details_mapped = (editingProduct?.lgc_details || []).reduce((r, c) => ({ ...r, [c.lgc]: c }), {}); form.setFieldValue('lgc_details_mapped', lgc_details_mapped); form.setFieldValue('quotation', editingProduct?.quotation); + setPickEditedInfo({ ...pickEditedInfo, product_title: editingProduct?.info?.product_title }); setFormEditable(infoEditable || priceEditable); @@ -78,6 +81,10 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editablePerm, infoEditabl const onIValuesChange = (changedValues, allValues) => { const dest = formValuesMapper(allValues); // console.log('form onValuesChange', Object.keys(changedValues), changedValues); + if ('product_title' in changedValues) { + const editTitle = (changedValues.product_title); + setPickEditedInfo({ ...pickEditedInfo, product_title: editTitle }); + } if (typeof onValuesChange === 'function') { onValuesChange(dest); } @@ -130,7 +137,7 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editablePerm, infoEditabl }, }), ]}> - + diff --git a/src/views/products/Detail/ProductInfoLgc.jsx b/src/views/products/Detail/ProductInfoLgc.jsx index 4fd4685..6beec47 100644 --- a/src/views/products/Detail/ProductInfoLgc.jsx +++ b/src/views/products/Detail/ProductInfoLgc.jsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'; import { useDefaultLgc } from '@/i18n/LanguageSwitcher'; import { cloneDeep, isEmpty, isNotEmpty } from '@/utils/commons'; -const ProductInfoLgc = ({ editable, formInstance, ...props }) => { +const ProductInfoLgc = ({ editable, formInstance, pickEditedInfo, ...props }) => { const { t } = useTranslation('products'); const { language: languageHT } = useDefaultLgc(); const HTLanguageSetsMapVal = useHTLanguageSetsMapVal(); @@ -15,6 +15,13 @@ const ProductInfoLgc = ({ editable, formInstance, ...props }) => { const [activeKey, setActiveKey] = useState(); const [items, setItems] = useState([]); + useEffect(() => { + formInstance.setFieldValue(['lgc_details_mapped', '2', 'title'], pickEditedInfo.product_title); + + return () => {}; + }, [pickEditedInfo.product_title]); + + useEffect(() => { const existsLgc = (editingProduct?.lgc_details || []).map((ele, li) => ({ ...ele, From 7045af0881d33da4a690922e58d2e385b2c94711 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 4 Sep 2024 14:43:29 +0800 Subject: [PATCH 4/5] =?UTF-8?q?perf:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AF=BC=E5=87=BAdocx:=20+v0903?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Detail/Header.jsx | 2 +- .../products/Print/AgencyContract_v0903.jsx | 234 ++++++++++++++---- 2 files changed, 185 insertions(+), 51 deletions(-) diff --git a/src/views/products/Detail/Header.jsx b/src/views/products/Detail/Header.jsx index 0fe5e40..ed1c218 100644 --- a/src/views/products/Detail/Header.jsx +++ b/src/views/products/Detail/Header.jsx @@ -15,7 +15,7 @@ import VendorSelector from '@/components/VendorSelector'; import AuditStateSelector from '@/components/AuditStateSelector'; import NewProductModal from './NewProductModal'; -import AgencyContract from '../Print/AgencyContract_v3'; +import AgencyContract from '../Print/AgencyContract_v0903'; import { saveAs } from "file-saver"; import { Packer } from "docx"; diff --git a/src/views/products/Print/AgencyContract_v0903.jsx b/src/views/products/Print/AgencyContract_v0903.jsx index 3ce91a0..11b7527 100644 --- a/src/views/products/Print/AgencyContract_v0903.jsx +++ b/src/views/products/Print/AgencyContract_v0903.jsx @@ -22,7 +22,7 @@ import { PositionalTabLeader, } from 'docx'; import dayjs from 'dayjs'; -import { cloneDeep, groupBy, isEmpty, isNotEmpty, sortBy, unique, uniqWith } from '@/utils/commons'; +import { cloneDeep, flush, groupBy, isEmpty, isNotEmpty, sortBy, unique, uniqWith } from '@/utils/commons'; // Shoulder Season 平季; peak season 旺季 const isFullYearOrLonger = (year, startDate, endDate) => { @@ -390,25 +390,34 @@ export default class AgencyContract { ...(isEmpty(agencyProducts['6']) ? [] - : [this.createSubHeading(`综费`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_6Q(use_year, agencyProducts['6'])]), + : [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.createTable_B(use_year, agencyProducts['B'])]), + : [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_JD(use_year, agencyProducts['J'])]), ...(isEmpty(agencyProducts['Q']) ? [] - : [this.createSubHeading(`导游`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_6Q(use_year, agencyProducts['Q'])]), + : [this.createSubHeading(`导游`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_Q(use_year, agencyProducts['Q'])]), ...(isEmpty(agencyProducts['7']) ? [] - : [this.createSubHeading(`景点`, { numbering: { reference: 'products-type', level: 0 } }), this.createTable_7(use_year, agencyProducts['7'], agencyExtras)]), + : [ + this.createSubHeading(`景点(默认元/人,部分价格为元/团单独注明)`, { numbering: { reference: 'products-type', level: 0 } }), + this.createTable_7(use_year, agencyProducts['7'], agencyExtras), + ]), ...(isEmpty(agencyProducts['R']) ? [] - : [this.createSubHeading(`餐费`, { numbering: { reference: 'products-type', level: 0 } }), ...this.createTable_R(use_year, agencyProducts['R'], agencyExtras)]), + : [ + this.createSubHeading(`餐费(元/人/餐)`, { numbering: { reference: 'products-type', level: 0 } }), + ...this.createTable_R(use_year, agencyProducts['R'], agencyExtras), + ]), ...(isEmpty(agencyProducts['D']) ? [] - : [this.createSubHeading(`包价线路`, { numbering: { reference: 'products-type', level: 0 } }), ...this.createTable_JD(use_year, agencyProducts['D'])]), + : [ + this.createSubHeading(`包价线路(元/团,部分价格为元/人单独注明)`, { numbering: { reference: 'products-type', level: 0 } }), + ...this.createTable_JD(use_year, agencyProducts['D']), + ]), this.createSubHeading(`公司银行账户`, { numbering: { reference: 'products-type', level: 0 } }), this.createInfo_Bank(), this.createSubHeading(`联系资料`, { numbering: { reference: 'products-type', level: 0 } }), @@ -425,11 +434,10 @@ export default class AgencyContract { /** * 综费 - * 导游 */ - createTable_6Q(use_year, dataList, style = {}) { - const { chunk, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_season']); - + createTable_6(use_year, dataList, style = {}) { + const { chunk, SSRange, sizeSets } = chunkBy(use_year, dataList, ['quote_season']); + // console.log('\n\n\n6', chunk, '\n',sizeSets); return new Table({ borders: tableBorderNone, width: { @@ -438,9 +446,9 @@ export default class AgencyContract { }, rows: [ new TableRow({ - children: [this.createTableHeader('项目', 30), this.createTableHeader(['平季', ...SSRange], 35), this.createTableHeader(['旺季', ...PSRange], 35)], + children: [this.createTableHeader('项目', 30), this.createTableHeader('人等', 20), this.createTableHeader([...SSRange], 50)], }), - ...chunk.map((row, ri) => this.createDetailRowSeason2(row, ri)).reduce((a, c) => a.concat(c), []), + ...chunk.reduce((acc, row, ri) => acc.concat(this.createDetailRowSizeSS(sizeSets, row, ri)), []), this.createTable_Memo(3), ], @@ -475,20 +483,24 @@ export default class AgencyContract { children: [ this.createTableHeader('超公里项目', 20), this.createTableHeader('往返公里数', 20), - this.createTableHeader(['全年', ...SSRange, ...PSRange], 60, { columnSpan: size.length }), + this.createTableHeader([...SSRange], 60, { columnSpan: size.length }), ], }), 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: '' })] }), - ...size.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + ...size.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ + // text: `${ss[0]}`, + text: ss[1] === Infinity ? `${ss[0]}人以上` : `${unique(ss.filter((x) => x)).join('-')}人`, + alignment: AlignmentType.CENTER, + })] })), ], }), ...chunk .filter((p) => p.sizeSetsSS === size.map((mr) => mr[0]).join(',')) - .map((row, ri) => this.createDetailRowSize(size, row, ri, { infoCol })) - .reduce((a, c) => a.concat(c), []), + .reduce((acc, row, ri) => acc.concat(this.createDetailRowSize(size, row, ri, { infoCol, withSize: false, defaultUnit: '0' })), []), + // .reduce((a, c) => a.concat(c), []), this.createTable_Memo(2 + size.length), ], @@ -505,7 +517,10 @@ export default class AgencyContract { * 包价线路 */ createTable_JD(use_year, dataList) { - const { dataSource, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_season', 'quote_size']); + 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' ? '' : ''; + const { dataSource, sizeSets, SSRange, PSRange } = chunkBy(use_year, dataList, chunk_By); const tableHeader = this.createTableHeader; const tableS = sizeSets.reduce((rt, setItem) => { @@ -519,19 +534,49 @@ export default class AgencyContract { new TableRow({ children: [ tableHeader('项目', 20), - tableHeader(['平季', ...SSRange], 40, { columnSpan: setItem.length }), - tableHeader(['旺季', ...PSRange], 40, { columnSpan: setItem.length }), + tableHeader([...SSRange], 40, { columnSpan: setItem.length }), + showPSCol ? tableHeader(['旺季', ...PSRange], 40, { columnSpan: setItem.length }) : undefined, ], }), new TableRow({ children: [ new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), - ...setItem.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), - ...setItem.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), + ...setItem.map( + (ss) => + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + new Paragraph({ + // text: `${ss[0]}`, + text: ss[1] === Infinity ? `${ss[0]}人以上` : `${unique(ss.filter((x) => x)).join('-')}人`, + alignment: AlignmentType.CENTER, + }), + ], + }) + ), + ...(showPSCol + ? setItem.map( + (ss) => + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + new Paragraph({ + // text: `${ss[0]}`, + 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(',')) - .map((row, ri) => this.createDetailRowSize(setItem, row, ri, { withSeason: true })).reduce((a, c) => a.concat(c), []), + ...dataSource + .filter((p) => p.sizeSetsSS === setItem.map((mr) => mr[0]).join(',')) + .reduce((acc, row, ri) => acc.concat(this.createDetailRowSize(setItem, row, ri, { withSize: false, defaultUnit, withSeason: showPSCol })), []), + // .reduce((a, c) => a.concat(c), []), this.createTable_Memo(1 + setItem.length * 2), ], @@ -544,10 +589,26 @@ export default class AgencyContract { } /** * 导游 - * @ignore - * @deprecated */ - createTable_Q(use_year, dataList) {} + createTable_Q(use_year, dataList) { + const { chunk, SSRange, PSRange } = chunkBy(use_year, dataList, ['quote_season']); + + return new Table({ + borders: tableBorderNone, + width: { + size: 100, + type: WidthType.PERCENTAGE, + }, + rows: [ + new TableRow({ + children: [this.createTableHeader('项目', 30), this.createTableHeader(['平季(除去旺季以外的时间)', ...SSRange], 35), this.createTableHeader(['旺季', ...PSRange], 35)], + }), + ...chunk.map((row, ri) => this.createDetailRowSeason2(row, ri, { withSize: false })).reduce((a, c) => a.concat(c), []), + + this.createTable_Memo(3), + ], + }); + } /** * 景点 */ @@ -584,7 +645,7 @@ export default class AgencyContract { // new TextRun({ text: `${extra.info.product_title} ${firstQ.unit_name}`, bold: true }), new TextRun({ text: - `${firstQ.adult_cost}, ${firstQ.group_size_min}-${firstQ.group_size_max}人, ${firstQ.unit_name}` + + `${firstQ.group_size_min}-${firstQ.group_size_max}人, ${firstQ.unit_name}, ${firstQ.adult_cost}` + // + (`, ${firstQ.group_size_min}-${firstQ.group_size_max}人`) (compactBy !== 'dates' ? `, ${date1}~${date2}${otherStr}` : ''), }), @@ -613,7 +674,8 @@ export default class AgencyContract { this.createTableHeader('附加费用', 25), ], }), - ...chunk.map((row, ri) => this.createDetailRowSeason2(row, ri, { extraCol })).reduce((a, c) => a.concat(c), []), + ...chunk.reduce((acc, row, ri) => 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), ], @@ -638,6 +700,12 @@ export default class AgencyContract { }); const tableS = sizeSets.reduce((acc, size) => { + const thisChunk = chunk.filter((p) => p.sizeSetsSS === size.map((mr) => mr[0]).join(',')); + const currentChunkExtras = uniqWith(thisChunk.map((p) => p.extras).flat(), (a, b) => a.info.id === b.info.id); + const mergedUniqueExtra = currentChunkExtras.map( + (extra) => new Paragraph({ text: `${extra.info.product_title} ${extra.quotation?.[0]?.adult_cost || ''} ${extra.quotation?.[0]?.unit_name || ''}` }) + ); + const subTable = new Table({ borders: tableBorderNone, width: { @@ -648,20 +716,24 @@ export default class AgencyContract { new TableRow({ children: [ this.createTableHeader('社会餐厅午餐/晚餐', 25), - this.createTableHeader(['价格', ...SSRange, ...PSRange], 50, { columnSpan: size.length }), + this.createTableHeader([...SSRange, ...PSRange], 50, { columnSpan: size.length }), this.createTableHeader('司陪餐补', 25), ], }), new TableRow({ children: [ new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), - ...size.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: `${ss[0]}` })] })), - // new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, rowSpan: dataList.length+1, children: [new Paragraph({ text: '' })] }), - new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ text: '' })] }), + ...size.map((ss) => new TableCell({ borders: tableBorderOne, verticalAlign: AlignmentType.CENTER, children: [new Paragraph({ + // text: `${ss[0]}`, + text: ss[1] === Infinity ? `${ss[0]}人以上` : `${unique(ss.filter((x) => x)).join('-')}人`, + alignment: AlignmentType.CENTER, + })] })), + 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: '' })] }), ], }), - ...chunk.filter((p) => p.sizeSetsSS === size.map((mr) => mr[0]).join(',')) - .map((row, ri) => this.createDetailRowSize(size, row, ri, { withSize: false, extraCol })).reduce((a, c) => a.concat(c), []), + ...thisChunk + .reduce((acc, row, ri) => acc.concat(this.createDetailRowSize(size, row, ri, { withSize: false, })), []), this.createTable_Memo(1 + size.length + 1), ], @@ -678,12 +750,71 @@ export default class AgencyContract { */ createTable_D(use_year, dataList) {} - createDetailRowSize(sizeSets, rowp, ii, { withSeason = false, withSize = true, infoCol = () => {}, extraCol = () => {} } = {}) { + createDetailRowSizeSS = (sizeSets, rowp, ii, { withSeason = false, withSize = true, infoCol = () => {}, extraCol = () => {} } = {}) => { + const thisSizeCol = rowp.sizeSetsSS.split(',').map((min, i, _arr) => [Number(min), _arr[i + 1] ? Number(_arr[i + 1]) - 1 : Infinity]); + const sizeLength = thisSizeCol.length; + const quote_chunk = (rowp.quote_chunk?.SS || []).reduce((acc, cur, i) => { + return { ...acc, [cur.quote_size]: cur.adult_cost }; + }, {}); + + return thisSizeCol.map( + (sizeRow, ri) => + new TableRow({ + children: flush([ + // new TableCell({ + // borders: tableBorderOne, + // rowSpan: 3, + // verticalAlign: AlignmentType.CENTER, + // children: [new Paragraph(String(ii + 1))], + // }), + ...(ri === 0 + ? [ + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + rowSpan: sizeLength, + children: [ + new Paragraph({ + text: `${rowp?.info?.product_title}`, + alignment: AlignmentType.LEFT, + }), + ], + }), + infoCol(rowp), + ] + : []), + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + new Paragraph({ + text: sizeRow[1] === Infinity ? `${sizeRow[0]}人以上` : `${unique(sizeRow.filter((x) => x)).join('-')}人`, + alignment: AlignmentType.CENTER, + }), + ], + }), + new TableCell({ + borders: tableBorderOne, + verticalAlign: AlignmentType.CENTER, + children: [ + new Paragraph({ + text: `${quote_chunk?.[sizeRow.join('-')] || ''}`, + alignment: AlignmentType.CENTER, + }), + ], + }), + 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: [ + children: flush([ // new TableCell({ // borders: tableBorderOne, // rowSpan: 3, @@ -711,11 +842,11 @@ export default class AgencyContract { ...((withSeason ? rowp.quote_chunk?.['SS']?.[ss] : rowp.quote_chunk[ss]) || []).map( (quoteItem, ii) => new Paragraph({ - alignment: AlignmentType.LEFT, + alignment: AlignmentType.CENTER, text: isEmpty(quoteItem) ? '' - : withSize - ? `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}` + : withSize && String(quoteItem.unit_id)!==defaultUnit + ? `${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}, ${quoteItem.adult_cost}` : `${quoteItem.adult_cost}`, }) ), @@ -734,10 +865,10 @@ export default class AgencyContract { ...(rowp.quote_chunk?.['PS']?.[ss] || []).map( (quoteItem, ii) => new Paragraph({ - alignment: AlignmentType.LEFT, + alignment: AlignmentType.CENTER, // text: `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}${quoteItem.unit_name}`, - text: withSize - ? `${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}`, }) ), @@ -746,11 +877,12 @@ export default class AgencyContract { ) : []), extraCol(rowp, ii), - ], + ]), }), ]; } - createDetailRowSeason2(rowp, ii, { extraCol = () => {} } = {}) { + createDetailRowSeason2(rowp, ii, { withSize = true, defaultUnit = '0', extraCol = () => {} } = {}) { + return [ new TableRow({ children: [ @@ -778,8 +910,9 @@ export default class AgencyContract { ...(rowp.quote_chunk['SS'] || []).map( (quoteItem, ii) => new Paragraph({ - alignment: AlignmentType.LEFT, - text: `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}`, + 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}`, }) ), ], @@ -792,8 +925,9 @@ export default class AgencyContract { ...(rowp.quote_chunk['PS'] || []).map( (quoteItem, ii) => new Paragraph({ - alignment: AlignmentType.LEFT, - text: `${quoteItem.adult_cost}, ${quoteItem.group_size_min}-${quoteItem.group_size_max}人,${quoteItem.unit_name}`, + 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}`, }) ), ], From fe58f01f228375162188ac11c879d9ab8c029c7c Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 4 Sep 2024 15:16:59 +0800 Subject: [PATCH 5/5] =?UTF-8?q?perf:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E7=BC=96=E8=BE=91=E4=BF=9D=E5=AD=98=E5=90=8E,=20?= =?UTF-8?q?=E4=B8=8D=E6=9B=B4=E6=96=B0=E6=A0=91=E7=9A=84=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Detail/ProductsTree.jsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/views/products/Detail/ProductsTree.jsx b/src/views/products/Detail/ProductsTree.jsx index c45b6e2..8ba536d 100644 --- a/src/views/products/Detail/ProductsTree.jsx +++ b/src/views/products/Detail/ProductsTree.jsx @@ -41,6 +41,7 @@ const getParentKey = (key, tree) => { const ProductsTree = ({ onNodeSelect, ...props }) => { const { t } = useTranslation(); const [agencyProducts, editingProduct, setEditingProduct] = useProductsStore((state) => [state.agencyProducts, state.editingProduct, state.setEditingProduct]); + const [activeAgency] = useProductsStore((state) => [state.activeAgency]); const productsTypes = useProductsTypes(); const [treeData, setTreeData] = useState([]); @@ -49,6 +50,11 @@ const ProductsTree = ({ onNodeSelect, ...props }) => { const [expandedKeys, setExpandedKeys] = useState([]); const [autoExpandParent, setAutoExpandParent] = useState(true); + useEffect(() => { + setExpandedKeys(productsTypes.map((item) => item.key)); // 全部展开 + return () => {} + }, [productsTypes, activeAgency]); + useEffect(() => { // 只显示有产品的类型; // const title = text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.title || ''; @@ -89,7 +95,7 @@ const ProductsTree = ({ onNodeSelect, ...props }) => { setRawTreeData(_show); setFlattenTreeData(flattenTreeFun(_show)); - setExpandedKeys(productsTypes.map((item) => item.key)); // 全部展开 + // setExpandedKeys(productsTypes.map((item) => item.key)); // 全部展开 // setActiveKey(isEmpty(_show) ? [] : [_show[0].key]); return () => {};