Эх сурвалжийг харах

reactor:对接中心统计报表并新增导出word;

zhangchong 1 жил өмнө
parent
commit
8bb56a5305

+ 3 - 0
package.json

@@ -24,6 +24,9 @@
 		"axios": "^1.4.0",
 		"dayjs": "^1.11.9",
 		"element-plus": "~2.4.4",
+		"file-saver": "^2.0.5",
+		"html-docx-js-typescript": "^0.1.5",
+		"html2canvas": "^1.4.1",
 		"js-cookie": "^3.0.1",
 		"jsencrypt": "^3.3.2",
 		"mitt": "^3.0.0",

+ 317 - 0
src/utils/exportAsWord.ts

@@ -0,0 +1,317 @@
+import Html2canvas from "html2canvas";
+import { asBlob } from "html-docx-js-typescript";
+import { saveAs } from "file-saver";
+
+const enum A4_PAPER_SIZE_ENUM {
+  'width' = 595.28,
+  'height' = 841.89,
+}
+
+// 图片处理
+const handleImage = (ele: HTMLElement, cloneEle: HTMLElement) => {
+  const imgClass =
+    '.ProseMirror img:not(.ProseMirror-separator):not(.resizer-img):not(.image-err)[src]';
+  const a4Width = A4_PAPER_SIZE_ENUM.width - 140; // word 默认A4纸宽度 - 页边距
+  const imgs = ele.querySelectorAll(imgClass);
+  const cloneImgs = cloneEle.querySelectorAll(imgClass);
+
+  cloneEle.querySelectorAll('.ProseMirror img').forEach((item) => {
+    if (
+      !['ProseMirror-separator', 'image-err', 'resizer-img'].includes(
+        item.className
+      )
+    )
+      return;
+    item.parentElement?.removeChild(item);
+  });
+
+  imgs.forEach((img, index: number) => {
+    if (!cloneImgs[index]) return;
+    if (img.clientWidth >= a4Width) {
+      cloneImgs[index].setAttribute('width', a4Width.toString());
+      const a4ImgHeight = (a4Width / img.clientWidth) * img.clientHeight;
+      cloneImgs[index].setAttribute('height', a4ImgHeight.toString());
+    } else {
+      cloneImgs[index].setAttribute('width', img.clientWidth.toString());
+      cloneImgs[index].setAttribute('height', img.clientHeight.toString());
+    }
+  });
+};
+// word中图片下面会出现一个白框
+const removeWhiteBox = (cloneEle: HTMLElement) => {
+  const separators: NodeListOf<Element> = cloneEle.querySelectorAll(
+    '.ProseMirror-separator'
+  );
+  separators.forEach((separator) =>
+    separator.parentElement?.removeChild(separator)
+  );
+};
+// 表格虚线 向右偏移10px左右
+const handleTableStyle = (cloneEle: HTMLElement) => {
+  cloneEle.querySelectorAll('table').forEach((table) => {
+    table.style.borderCollapse = table.style.borderCollapse || 'collapse';
+    table.style.border = table.style.border || '1';
+    table.style.marginLeft = '10px';
+  });
+};
+// 标题高度和字体失效 需要设置lineHeight和fontWeight
+// eslint-disable-next-line
+const handleLevelStyle = (cloneEle: HTMLElement) => {
+  Array.from({ length: 6 }).forEach((_, index) =>
+    (
+      cloneEle.querySelectorAll(`h${index + 1}`) as unknown as HTMLElement[]
+    ).forEach((h) => {
+      h.innerText = (h.children[0] as HTMLElement).innerText;
+      h.style.fontSize = '';
+    })
+  );
+};
+// 处理脑图 脑图导出word有问题 直接通过html2canvas将脑图转换为图片 导出图片即可
+const handleMind = async (ele: HTMLElement, cloneEle: HTMLElement) => {
+  const mindDOM = ele.querySelectorAll(
+    `.mind-snapshot`
+  ) as unknown as HTMLElement[];
+  const promise = [...mindDOM].map((mind) =>
+    Html2canvas(mind.firstChild as HTMLElement)
+  );
+  const result = await Promise.allSettled(promise);
+  const copyMindDOM = cloneEle.querySelectorAll(
+    `.ProseMirror-mind-node-wrapper`
+  ) as unknown as HTMLElement[];
+  result.forEach((item, index) => {
+    const { status, value } = item as any;
+    if (status === 'fulfilled') {
+      const img = document.createElement('img');
+      img.src = value.toDataURL('image/jpg');
+      const parent = copyMindDOM[index].parentElement as HTMLElement;
+      copyMindDOM[index].insertAdjacentElement('beforebegin', img);
+      parent.removeChild(copyMindDOM[index]);
+    }
+  });
+};
+// 列表处理
+const handleUlStyle = (cloneEle: HTMLElement, uiLevel: number) => {
+  const changeTask2P = (
+    div: HTMLElement | Element,
+    parent?: HTMLElement | Element
+  ) => {
+    const p = document.createElement('p');
+    p.innerHTML = div.innerHTML;
+    parent
+      ? parent.insertAdjacentElement('afterend', p)
+      : div.insertAdjacentElement('afterend', p);
+    return p;
+  };
+  const changeDiv2Ul = (
+    div: HTMLElement | Element,
+    uiLevel: number,
+    parent?: HTMLElement | Element
+  ) => {
+    const kind = div.getAttribute('data-list-kind');
+    let liOrp: HTMLParagraphElement | HTMLLIElement | null = null;
+    // 根据checkbox或列表生成不同的标签
+    if (kind === 'task') {
+      liOrp = changeTask2P(div, parent);
+    } else {
+      const ul =
+        kind === 'ordered'
+          ? document.createElement('ol')
+          : document.createElement('ul');
+      liOrp = document.createElement('li');
+      !parent && (ul.style.margin = '0');
+      liOrp.innerHTML = div.innerHTML;
+      ul.appendChild(liOrp);
+      // 将ul添加到后面
+      parent
+        ? parent.insertAdjacentElement('beforebegin', ul)
+        : div.insertAdjacentElement('beforebegin', ul);
+    }
+    div.parentElement?.removeChild(div);
+
+    liOrp.querySelectorAll('.list-marker').forEach((marker) => {
+      if (kind === 'task') {
+        const span = document.createElement('span');
+        span.innerHTML = `<span style="color:#333333; font-family:'Wingdings 2',serif; font-size:11pt"></span>`;
+        marker.insertAdjacentElement('beforebegin', span);
+      }
+      marker.parentElement?.removeChild(marker);
+    });
+    liOrp.querySelectorAll('.list-content').forEach((content) => {
+      const span = document.createElement('span');
+      span.innerHTML =
+        (content.firstChild as HTMLElement)?.innerHTML || '<li></li>';
+      liOrp &&
+      (liOrp.style.textAlign = (
+        content.firstChild as HTMLElement
+      )?.style?.textAlign);
+      content.insertAdjacentElement('beforebegin', span);
+
+      const innerFlatList = content.querySelectorAll(
+        `.prosemirror-flat-list[data-list-level="${uiLevel}"]`
+      );
+      if (innerFlatList.length) {
+        uiLevel++;
+        innerFlatList.forEach((div) => changeDiv2Ul(div, uiLevel, content));
+      }
+      content.parentElement?.removeChild(content);
+    });
+  };
+  const flatList = cloneEle.querySelectorAll(
+    `.prosemirror-flat-list[data-list-level="${uiLevel}"]`
+  );
+  uiLevel++;
+  flatList.forEach((div) => changeDiv2Ul(div, uiLevel));
+};
+// 处理附件 直接改为链接跳转到对应的段落
+const handleAttachStyle = (cloneEle: HTMLElement) => {
+  cloneEle.querySelectorAll('.attachment-node-wrap').forEach((attach) => {
+    const title = `请至One文档查看附件《${attach.getAttribute('name')}》`;
+    const anchorId = attach.parentElement?.getAttribute('data-id');
+    const a = document.createElement('a');
+    a.target = '_blank';
+    a.href = `${location.href}&anchor=${anchorId}`;
+    a.innerHTML = `<span>${title}</span>`;
+
+    attach.insertAdjacentElement('beforebegin', a);
+    attach.parentElement?.removeChild(attach);
+  });
+};
+// 处理附件 直接改为链接跳转到对应的段落
+const handleIframeStyle = (cloneEle: HTMLElement) => {
+  cloneEle.querySelectorAll('.iframe-container').forEach((iframe) => {
+    const anchorId = iframe?.getAttribute('data-id');
+    const a = document.createElement('a');
+    a.target = '_blank';
+    a.href = `${location.href}&anchor=${anchorId}`;
+    a.innerHTML = '<span>请至One文档查看多维表</span>';
+
+    iframe.insertAdjacentElement('beforebegin', a);
+    iframe.parentElement?.removeChild(iframe);
+  });
+};
+// 获取远程css资源 转为text文本
+const handleCssStream = async (result: Response) => {
+  if (!result.body) return '';
+  const reader = result.body.getReader();
+  const stream = await new ReadableStream({
+    start(controller) {
+      // The following function handles each data chunk
+      function push() {
+        // "done" is a Boolean and value a "Uint8Array"
+        reader.read().then(({ done, value }) => {
+          // If there is no more data to read
+          if (done) {
+            controller.close();
+            return;
+          }
+          // Get the data and send it to the browser via the controller
+          controller.enqueue(value);
+          // Check chunks by logging to the console
+          push();
+        });
+      }
+      push();
+    },
+  });
+  return await new Response(stream, {
+    headers: { 'Content-Type': 'text/html' },
+  }).text();
+};
+/**
+ * 处理css
+ * 线上环境 <link rel="stylesheet" type="text/css" href="/css/365.f542e1fc.css">
+ * 本地环境 <style type="text/css">
+ */
+const handleCss = async () => {
+  const styles = document.head.querySelectorAll('style');
+  const links = document.head.querySelectorAll('link[type="text/css"]');
+  const remoteCSSPromise = [...links].map((link) => fetch((link as any).href));
+  const remoteCSSResult = await Promise.allSettled(remoteCSSPromise);
+
+  const remoteCSSStreamPromise = remoteCSSResult.map((item) => {
+    const { status, value } = item as any;
+    if (status === 'fulfilled') return handleCssStream(value);
+  });
+  const remoteCSSStreamResult = await Promise.allSettled(
+    remoteCSSStreamPromise
+  );
+  const cssText = remoteCSSStreamResult.map((item) => {
+    const { status, value } = item as any;
+    if (status === 'fulfilled') return value;
+  });
+  styles.forEach((css) => cssText.push(css.innerHTML));
+  return cssText;
+};
+const handleStyle = async (ele: HTMLElement, cloneEle: HTMLElement) => {
+  const uiLevel = 1;
+  handleImage(ele, cloneEle);
+  removeWhiteBox(cloneEle);
+  handleTableStyle(cloneEle);
+  handleLevelStyle(cloneEle);
+  await handleMind(ele, cloneEle);
+
+  handleUlStyle(cloneEle, uiLevel);
+
+  handleAttachStyle(cloneEle);
+  handleIframeStyle(cloneEle);
+
+  const cssText = await handleCss();
+  const cssString = cssText
+    .join('')
+    // 过滤UI原来的样式
+    .replace(/li:before/g, 'xxx_li:before')
+    .replace(/\.ul/g, '.xxx_ul')
+    .replace(/\.li/g, '.xxx_li')
+    .replace(/\.ol/g, '.xxx_ol')
+    .replace(/\.ProseMirror ul/g, '.xxx_ul')
+    .replace(/\.ProseMirror ol/g, '.xxx_ol')
+    .replace(/\.ProseMirror li/g, '.xxx_li');
+  const innerHtml = cloneEle.innerHTML
+    // strong在word中不生效问题
+    .replace(/<strong>/g, '<b>')
+    .replace(/<\/strong>/g, '</b>')
+    // 背景色不生效问题
+    .replace(/<mark/g, '<span')
+    .replace(/<\/mark>/g, '</span>')
+    // 将上面生成的多个ul/ol组成一个
+    .replace(/<\/ol><ol.*?>/g, '')
+    .replace(/<\/ul><ul.*?>/g, '');
+  // 最终生成的html字符串
+  return `<!DOCTYPE html>
+      <html lang="en">
+      <head>
+      <style>${cssString}</style>
+      <title></title> 
+      </head>
+      <body>
+      <div id="q-editor">
+      ${innerHtml}
+      </div>
+      </body>
+      </html>`;
+};
+/**
+ * 导出word
+ * @param selector 导出word对应的元素
+ * @param filename 导出后文件名称
+ * @returns Promise
+ */
+export function exportAsDocx(selector: HTMLElement | string, filename: string) {
+  const ele =
+    typeof selector === 'string'
+      ? (document.querySelector(selector) as HTMLElement)
+      : selector;
+  if (!ele) return Promise.reject();
+
+  // eslint-disable-next-line no-async-promise-executor
+  return new Promise<void>(async (resolve) => {
+    const cloneEle = ele.cloneNode(true) as HTMLElement;
+    const htmlString = await handleStyle(ele, cloneEle);
+    const margins = { top: 1440 };
+
+    asBlob(htmlString, { margins }).then((data:any) => {
+      saveAs(data as Blob, filename + '.docx');
+      resolve();
+    });
+  });
+}

+ 1 - 1
src/utils/other.ts

@@ -40,7 +40,7 @@ export function useTitle() {
 		} else {
 			webTitle = setTagsViewNameI18n(router.currentRoute.value);
 		}
-		document.title =  ` ${globalTitle}-${webTitle}` || globalTitle;
+		document.title =  ` ${globalTitle}【${webTitle}】` || globalTitle;
 	}).then(() => {});
 }
 

+ 325 - 113
src/views/statistics/order/centerReport.vue

@@ -1,134 +1,346 @@
 <template>
-  <div class="statistics-order-center-report-container layout-pd">
-    <!-- 搜索  -->
-    <el-card shadow="never">
-      <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent  inline>
-        <el-form-item label="时间段" prop="crTime">
-          <el-date-picker
-            v-model="state.queryParams.crTime"
-            type="datetimerange"
-            unlink-panels
-            range-separator="至"
-            start-placeholder="开始时间"
-            end-placeholder="结束时间"
-            :shortcuts="shortcuts"
-            @change="queryList"
-            value-format="YYYY-MM-DD[T]HH:mm:ss"
-          />
-        </el-form-item>
-        <el-form-item>
-          <el-button type="primary" @click="queryList" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
-          <el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
-            <SvgIcon name="ele-Refresh" class="mr5" />重置
-          </el-button>
-        </el-form-item>
-      </el-form>
-    </el-card>
-    <el-card shadow="never">
-      <!-- 表格 -->
-      <el-table
-        :data="state.tableData"
-        v-loading="state.loading"
-        show-summary
-        row-key="userId"
-        ref="multipleTableRef"
-        @selection-change="handleSelectionChange"
-        @sort-change="sortChange"
-      >
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column prop="userName" label="姓名" show-overflow-tooltip align="center"></el-table-column>
-        <el-table-column label="有效件" align="center">
-          <el-table-column prop="centreArchive" label="中心归档件" show-overflow-tooltip align="center" sortable="custom"></el-table-column>
-          <el-table-column prop="centreCareOf" label="转办信件" show-overflow-tooltip align="center" sortable="custom"></el-table-column>
-          <!--          <el-table-column prop="addNum" label="待转办信件" show-overflow-tooltip align="center" sortable="custom"></el-table-column>-->
-        </el-table-column>
-        <el-table-column prop="invalid" label="无效信件" show-overflow-tooltip align="center" sortable="custom"></el-table-column>
-        <el-table-column prop="repeat" label="重复信件" show-overflow-tooltip align="center" sortable="custom"></el-table-column>
-        <el-table-column prop="subtotal" label="所有信件" show-overflow-tooltip align="center" sortable="custom"></el-table-column>
-        <template #empty>
-          <Empty />
-        </template>
-      </el-table>
-      <!-- 分页 -->
-      <pagination
-        :total="state.total"
-        v-model:page="state.queryParams.PageIndex"
-        v-model:limit="state.queryParams.PageSize"
-        @pagination="queryList"
-      />
-    </el-card>
-  </div>
+	<div class="statistics-order-center-report-container layout-pd">
+		<!-- 搜索  -->
+		<el-card shadow="never">
+			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
+				<el-form-item prop="dateType" label="时间单位">
+					<el-select v-model="state.queryParams.dateType" placeholder="部门名称" @change="changeDateType">
+            <el-option label="日" value="date" />
+						<el-option label="周" value="week" />
+						<el-option label="月" value="month" />
+						<el-option label="自定义" value="datetimerange" />
+					</el-select>
+				</el-form-item>
+				<el-form-item prop="crTime" v-if="state.queryParams.dateType === 'datetimerange'" label="时间">
+					<el-date-picker
+						v-model="state.queryParams.crTime"
+						type="datetimerange"
+						unlink-panels
+						range-separator="至"
+						start-placeholder="开始时间"
+						end-placeholder="结束时间"
+						:shortcuts="shortcuts"
+						@change="
+							() => {
+								queryList();
+								changeDateType('datetimerange');
+							}
+						"
+						value-format="YYYY-MM-DD[T]HH:mm:ss"
+						:clearable="false"
+					/>
+				</el-form-item>
+				<el-form-item prop="time" v-else label="时间">
+					<el-date-picker
+						v-model="state.queryParams.time"
+						:type="state.queryParams.dateType"
+						placeholder="选择时间"
+						@change="
+							() => {
+								queryList();
+								changeDateType(state.queryParams.dateType);
+							}
+						"
+						:value-format="valueFormat"
+						:format="formats"
+						:clearable="false"
+					/>
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" @click="queryList" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
+					<el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
+						<SvgIcon name="ele-Refresh" class="mr5" />重置
+					</el-button>
+          <el-button class="default-button" :loading="state.loading" @click="exportWord"><SvgIcon name="iconfont icon-daochu"/> 导出</el-button>
+				</el-form-item>
+			</el-form>
+		</el-card>
+		<el-card shadow="never">
+			<table style="min-width: 80%;height: 100%; margin: 0 auto; text-align: center" v-loading="state.loading" ref="exportTable">
+				<tbody>
+					<tr style="height: 70px">
+						<td style="vertical-align: top; text-align: center">
+							<div style="font-size: 28px; font-weight: bold">{{ tableTitle }}</div>
+							<div style="margin-top: 10px; font-size: 22px">{{ dates }}</div>
+						</td>
+					</tr>
+					<tr>
+						<td>
+							<table style="width: 100%; border: 1px solid #000000; border-collapse: collapse; padding: 0">
+								<tbody>
+									<tr style="height: 43px">
+										<td colspan="4" style="font-size: 22px; font-weight: bold; border-bottom: 1px solid #000000">一、总体情况</td>
+									</tr>
+									<tr style="height: 43px">
+										<td colspan="2" style="font-size: 22px; text-align: left; border-bottom: 1px solid #000000">
+											电话总量:{{ centerReportCallData.allCallCount }}
+										</td>
+										<td colspan="2" style="font-size: 22px; text-align: left; border-bottom: 1px solid #000000">
+											居家养老电话总量:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;居家养老呼入接通量:
+										</td>
+									</tr>
+									<tr style="height: 43px">
+										<td style="width: 25%; font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">有效</td>
+										<td style="width: 25%; font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">
+											{{ centerReportCallData.effectiveCount }}(接通)
+										</td>
+										<td style="width: 25%; font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">无效</td>
+										<td style="width: 25%; font-size: 22px; border-bottom: 1px solid #000000">{{ centerReportCallData.invalidCount }}(未接通)</td>
+									</tr>
+									<tr style="height: 43px">
+										<td style="width: 25%; font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">在IVR中挂断</td>
+										<td style="width: 25%; font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">
+											{{ centerReportCallData.ivrByeCount }}
+										</td>
+										<td style="width: 25%; font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">在队列中挂断</td>
+										<td style="width: 25%; font-size: 22px; border-bottom: 1px solid #000000">{{ centerReportCallData.queueByeCount }}</td>
+									</tr>
+									<tr style="height: 43px">
+										<td colspan="4" style="font-size: 22px; text-align: left; border-bottom: 1px solid #000000">信件回访量</td>
+									</tr>
+									<tr style="height: 43px">
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">已回访量</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">
+											{{ centerReportVisitdData.visitd }}
+										</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">待回访量</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000">{{ centerReportVisitdData.waitVisitd }}</td>
+									</tr>
+									<tr style="height: 43px">
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">坐席满意度</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">
+											{{ centerReportVisitdData.seatsRate }}%
+										</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">部门满意度</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000">{{ centerReportVisitdData.orgRate }}%</td>
+									</tr>
+									<tr style="height: 43px">
+										<td colspan="4" style="font-size: 22px; text-align: left; border-bottom: 1px solid #000000">
+											来件总计:{{ centerReportOrderData.allCallCount }}
+										</td>
+									</tr>
+									<tr style="height: 43px">
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">有效</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">
+											{{ centerReportOrderData.effectiveCount }}
+										</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">无效</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000">{{ centerReportOrderData.invalidCount }}</td>
+									</tr>
+									<tr style="height: 43px">
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">已办结信件</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">
+											{{ centerReportOrderData.completedCount }}
+										</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">在办信件</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000">{{ centerReportOrderData.inProgressCount }}</td>
+									</tr>
+									<tr style="height: 43px">
+										<td colspan="4" style="font-size: 22px; border-bottom: 1px solid #000000">信件来源</td>
+									</tr>
+									<tr style="height: 43px">
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">来源方式</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">数量</td>
+										<td colspan="2" style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">备注</td>
+									</tr>
+									<tr style="height: 43px" v-for="(item, index) in centerReportOrderSourceChannelsData" :key="index">
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">{{ item.name }}:</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">{{ item.allCountNum }}</td>
+										<td colspan="2" style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">{{ item.remark }}</td>
+									</tr>
+									<tr style="height: 43px">
+										<td colspan="4" style="font-size: 22px; border-bottom: 1px solid #000000">信件分类</td>
+									</tr>
+									<tr style="height: 43px" v-for="(item, index) in centerReportOrderAcceptTypesData" :key="index">
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">{{ item.name }}</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">{{ item.allCountNum }}</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">所占百分比</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000">{{ item.proportionRate }}%</td>
+									</tr>
+								</tbody>
+							</table>
+						</td>
+					</tr>
+					<tr>
+						<td style="height: 30px">&nbsp;</td>
+					</tr>
+					<tr>
+						<td style="vertical-align: top">
+							<table style="width: 100%; border: 1px solid #000000; border-collapse: collapse; padding: 0px">
+								<tbody>
+									<tr style="height: 43px">
+										<td colspan="2" style="font-size: 22px; font-weight: bold; border-bottom: 1px solid #000000">二、信件分布情况</td>
+									</tr>
+									<tr style="height: 43px">
+										<td colspan="2" style="font-size: 22px; text-align: left; border-bottom: 1px solid #000000">市本级总计:{{orgStatisticsCityAllData.orgStatisticsCountAll}}件</td>
+									</tr>
+									<tr style="height: 43px">
+										<td style="font-size: 22px; width: 40%; border-bottom: 1px solid #000000; border-right: 1px solid #000000">部门(单位)</td>
+										<td style="font-size: 22px; width: 40%; border-bottom: 1px solid #000000; border-right: 1px solid #000000">数 量</td>
+									</tr>
+									<tr style="height: 43px" v-for="(item, index) in orgStatisticsCityAllData.orgStatistics" :key="index">
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">{{item.name}}</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">{{item.allCountNum}}</td>
+									</tr>
+									<tr style="height: 43px">
+										<td colspan="2" style="font-size: 22px; text-align: left; border-bottom: 1px solid #000000">县(区)总计:{{orgStatisticsAreaAllData.orgStatisticsCountAll}}件</td>
+									</tr>
+									<tr style="height: 43px">
+										<td style="font-size: 22px; width: 40%; border-bottom: 1px solid #000000; border-right: 1px solid #000000">县(区)</td>
+										<td style="font-size: 22px; width: 40%; border-bottom: 1px solid #000000; border-right: 1px solid #000000">数 量</td>
+									</tr>
+									<tr style="height: 43px" v-for="(item, index) in orgStatisticsAreaAllData.orgStatistics" :key="index">
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">{{item.name}}</td>
+										<td style="font-size: 22px; border-bottom: 1px solid #000000; border-right: 1px solid #000000">{{item.allCountNum}}</td>
+									</tr>
+								</tbody>
+							</table>
+						</td>
+					</tr>
+				</tbody>
+			</table>
+		</el-card>
+	</div>
 </template>
 <script setup lang="ts" name="statisticsOrderCenterReport">
-import { onMounted, reactive, ref } from 'vue';
-import { ElButton, FormInstance } from 'element-plus';
+import { onMounted, reactive, ref } from "vue";
+import { ElButton, ElMessageBox, FormInstance } from "element-plus";
 import { throttle } from '@/utils/tools';
 import { centerReport } from '@/api/statistics/order';
 import { shortcuts } from '@/utils/constants';
 import dayjs from 'dayjs';
+import { exportAsDocx } from "@/utils/exportAsWord";
 // 定义变量内容
 const ruleFormRef = ref<RefType>(); // 表单ref
 const state = reactive(<any>{
-  queryParams: {
-    // 查询条件
-    PageIndex: 1,
-    PageSize: 10,
-    crTime: [dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')], // 时间默认今天开始到今天结束
-  },
-  tableData: [], //表单
-  loading: false, // 加载
-  total: 0, // 总数
+	queryParams: {
+		dateType: 'date', //
+		time: dayjs(new Date()).format('YYYY-MM-DD'), // 时间默认今天
+		crTime: [dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')], // 时间默认今天开始到今天结束
+	},
+	loading: false, // 加载
 });
+const valueFormat = ref('YYYY-MM-DD');
+const formats = ref('YYYY-MM-DD');
+const tableTitle = ref('12345市民服务热线办理工作日统计');
+const dates = ref<string>(dayjs(state.queryParams.time).format('YYYY-MM-DD'));
+const changeDateType = (val: string) => {
+	switch (val) {
+		case 'date':
+			valueFormat.value = 'YYYY-MM-DD';
+			formats.value = 'YYYY-MM-DD';
+			dates.value = dayjs(state.queryParams.time).format('YYYY-MM-DD');
+			tableTitle.value = '12345市民服务热线办理工作日统计';
+			state.queryParams.crTime = [dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')];
+			queryList();
+			break;
+		case 'month':
+			valueFormat.value = 'YYYY-MM';
+			formats.value = 'YYYY-MM';
+			dates.value = `${dayjs(state.queryParams.time).startOf('month').format('YYYY-MM-DD')} 至 ${dayjs(state.queryParams.time)
+				.endOf('month')
+				.format('YYYY-MM-DD')}`;
+			tableTitle.value = '12345市民服务热线办理工作月统计';
+			state.queryParams.crTime = [dayjs().startOf('month').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('month').format('YYYY-MM-DD HH:mm:ss')];
+			queryList();
+			break;
+		case 'week':
+			valueFormat.value = 'YYYY-MM-DD';
+			formats.value = 'YYYY 第ww周';
+			dates.value = `${dayjs(state.queryParams.time).startOf('week').format('YYYY-MM-DD')} 至 ${dayjs(state.queryParams.time)
+				.endOf('week')
+				.format('YYYY-MM-DD')}`;
+			tableTitle.value = '12345市民服务热线办理工作周统计';
+			state.queryParams.crTime = [dayjs().startOf('week').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('week').format('YYYY-MM-DD HH:mm:ss')];
+			queryList();
+			break;
+		case 'datetimerange':
+			dates.value = `${dayjs(state.queryParams.crTime[0]).format('YYYY-MM-DD')} 至 ${dayjs(state.queryParams.crTime[1]).format('YYYY-MM-DD')}`;
+			tableTitle.value = '12345市民服务热线办理工作时间段统计';
+			queryList();
+			break;
+		default:
+			valueFormat.value = 'YYYY-MM-DD';
+			formats.value = 'YYYY-MM-DD';
+			dates.value = dayjs(state.queryParams.time).format('YYYY-MM-DD');
+			tableTitle.value = '12345市民服务热线办理工作日统计';
+			state.queryParams.crTime = [dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')];
+			queryList();
+			break;
+	}
+};
+const centerReportCallData = ref<EmptyObjectType>({}); // 电话统计
+const centerReportOrderData = ref<EmptyObjectType>({}); // 信件统计
+const centerReportOrderAcceptTypesData = ref<EmptyObjectType>([]); // 信件类型统计
+const centerReportOrderSourceChannelsData = ref<EmptyObjectType>([]); // 信件来源统计
+const centerReportVisitdData = ref<EmptyObjectType>({}); // 信件回访统计
+const orgStatisticsAreaAllData = ref<EmptyObjectType>({}); // 区域统计
+const orgStatisticsCityAllData = ref<EmptyObjectType>({}); // 城市统计
 /** 获取列表 */
 const queryList = throttle(() => {
-  state.loading = true;
-  let StartTime = null;
-  let EndTime = null;
-  if (state.queryParams?.crTime) {
-    StartTime = state.queryParams?.crTime[0];
-    EndTime = state.queryParams?.crTime[1];
-  }
-  const request = {
-    StartTime,
-    EndTime,
-    DelayState: state.queryParams.DelayState,
-    PageIndex: state.queryParams.PageIndex,
-    PageSize: state.queryParams.PageSize,
-    Keyword: state.queryParams.Keyword,
-    SortField: state.queryParams.SortField,
-    SortRule: state.queryParams.SortRule,
-  };
-  centerReport(request)
-    .then((res: any) => {
-      state.tableData = res.result?.items ?? [];
-      state.total = res.result?.total ?? 0;
-      state.loading = false;
-    })
-    .catch(() => {
-      state.loading = false;
-    });
+	state.loading = true;
+	let StartTime = null;
+	let EndTime = null;
+	if (state.queryParams?.crTime) {
+		StartTime = state.queryParams?.crTime[0];
+		EndTime = state.queryParams?.crTime[1];
+	}
+	const request = {
+		StartTime,
+		EndTime,
+	};
+	centerReport(request)
+		.then((res: any) => {
+			const {
+				centerReportCall,
+				centerReportOrder,
+				centerReportOrderAcceptTypes,
+				centerReportOrderSourceChannels,
+				centerReportVisitd,
+				orgStatisticsAreaAll,
+				orgStatisticsCityAll,
+			} = res.result;
+
+			centerReportCallData.value = centerReportCall;
+			centerReportOrderData.value = centerReportOrder;
+			centerReportOrderAcceptTypesData.value = centerReportOrderAcceptTypes;
+			centerReportOrderSourceChannelsData.value = centerReportOrderSourceChannels;
+			centerReportVisitdData.value = centerReportVisitd;
+			orgStatisticsAreaAllData.value = orgStatisticsAreaAll;
+			orgStatisticsCityAllData.value = orgStatisticsCityAll;
+			state.loading = false;
+		})
+		.catch(() => {
+			state.loading = false;
+		});
 }, 300);
-// 排序
-const sortChange = (val: any) => {
-  state.queryParams.SortField = val.order ? val.prop : null;
-  // 0 升序 1 降序
-  state.queryParams.SortRule = val.order ? (val.order == 'descending' ? 1 : 0) : null;
-  queryList();
-};
 /** 重置按钮操作 */
 const resetQuery = throttle((formEl: FormInstance | undefined) => {
-  if (!formEl) return;
-  formEl.resetFields();
-  queryList();
+	if (!formEl) return;
+	formEl.resetFields();
+	queryList();
+  valueFormat.value = 'YYYY-MM-DD';
+  formats.value = 'YYYY-MM-DD';
+  dates.value = dayjs(state.queryParams.time).format('YYYY-MM-DD');
+  tableTitle.value = '12345市民服务热线办理工作日统计';
+  state.queryParams.crTime = [dayjs().startOf('day').format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')];
 }, 300);
-// 表格多选
-const multipleTableRef = ref<RefType>();
-const multipleSelection = ref<any>([]);
-const handleSelectionChange = (val: any[]) => {
-  multipleSelection.value = val;
+const exportTable = ref<RefType>()
+const exportWord = () => {
+  ElMessageBox.confirm(`确定要导出(${tableTitle.value}【${dates.value}】),是否继续?`, '提示', {
+    confirmButtonText: '确认导出',
+    cancelButtonText: '取消',
+    type: 'warning',
+    draggable: true,
+    cancelButtonClass: 'default-button',
+    autofocus: false,
+  })
+    .then(() => {
+      exportAsDocx(exportTable.value, `${tableTitle.value}【${dates.value}】`);
+    })
+    .catch(() => {});
 };
-
 onMounted(() => {
-  queryList();
+	queryList();
 });
 </script>

+ 1 - 4
src/views/statistics/order/hotspotCount.vue

@@ -4,7 +4,7 @@
 		<el-card shadow="never">
 			<el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
 				<el-form-item prop="dateType" label="时间单位">
-					<el-select v-model="state.queryParams.dateType" placeholder="部门名称" @change="changeDateType">
+					<el-select v-model="state.queryParams.dateType" placeholder="部门名称" @change="queryList">
 						<el-option label="年" value="year" />
 						<el-option label="月" value="month" />
 						<el-option label="日" value="date" />
@@ -207,9 +207,6 @@ const load = (row: any, treeNode: unknown, resolve: (date: any[]) => void) => {
 		})
 		.catch(() => {});
 };
-const changeDateType = () => {
-	queryList();
-};
 // 合计
 const getSummaries = (param: any) => {
   const { columns } = param;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 396 - 155
yarn.lock


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно