From 509acf8dac4d27c82f00695ef12948ec6f3c6634 Mon Sep 17 00:00:00 2001
From: chenyc <501753378@qq.com>
Date: 星期四, 06 三月 2025 12:03:37 +0800
Subject: [PATCH] gx

---
 src/i18n/lang/zh-cn.ts                      |    1 
 src/api/QC/index.ts                         |  207 ++++++++
 src/api/QC/type.ts                          |  159 ++++++
 src/layout/navBars/topBar/breadcrumb.vue    |   31 +
 package-lock.json                           |  167 ++++++
 src/router/route.ts                         |   15 
 src/views/zhikong/xueqingdanbai.vue         |   36 +
 package.json                                |    3 
 src/layout/navBars/topBar/index.vue         |    2 
 src/views/tongji/index.vue                  |   20 
 src/views/zhikong/components/components.vue |  617 +++++++++++++++++++++++++
 src/utils/Export2.ts                        |  174 +++++++
 12 files changed, 1,430 insertions(+), 2 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 60d4c02..acab0de 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -41,7 +41,8 @@
 				"vue-grid-layout": "^3.0.0-beta1",
 				"vue-i18n": "^9.10.2",
 				"vue-router": "^4.3.0",
-				"vue3-print-nb": "^0.1.4"
+				"vue3-print-nb": "^0.1.4",
+				"xlsx": "^0.18.5"
 			},
 			"devDependencies": {
 				"@types/node": "^20.11.28",
@@ -1695,6 +1696,15 @@
 				"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
 			}
 		},
+		"node_modules/adler-32": {
+			"version": "1.3.1",
+			"resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
+			"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
+			"license": "Apache-2.0",
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
 		"node_modules/ajv": {
 			"version": "6.12.6",
 			"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -1913,6 +1923,19 @@
 			"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
 			"optional": true
 		},
+		"node_modules/cfb": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
+			"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"adler-32": "~1.3.0",
+				"crc-32": "~1.2.0"
+			},
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
 		"node_modules/chalk": {
 			"version": "4.1.2",
 			"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -1983,6 +2006,15 @@
 				"tiny-emitter": "^2.0.0"
 			}
 		},
+		"node_modules/codepage": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
+			"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
+			"license": "Apache-2.0",
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
 		"node_modules/color-convert": {
 			"version": "2.0.1",
 			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -2038,6 +2070,18 @@
 			"version": "2.8.0",
 			"resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.8.0.tgz",
 			"integrity": "sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ=="
+		},
+		"node_modules/crc-32": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
+			"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+			"license": "Apache-2.0",
+			"bin": {
+				"crc32": "bin/crc32.njs"
+			},
+			"engines": {
+				"node": ">=0.8"
+			}
 		},
 		"node_modules/cropperjs": {
 			"version": "1.6.1",
@@ -2711,6 +2755,15 @@
 			},
 			"engines": {
 				"node": ">= 6"
+			}
+		},
+		"node_modules/frac": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz",
+			"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
+			"license": "Apache-2.0",
+			"engines": {
+				"node": ">=0.8"
 			}
 		},
 		"node_modules/fs-extra": {
@@ -4033,6 +4086,18 @@
 				"url": "https://github.com/sponsors/antoniandre"
 			}
 		},
+		"node_modules/ssf": {
+			"version": "0.11.2",
+			"resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz",
+			"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"frac": "~1.1.2"
+			},
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
 		"node_modules/ssr-window": {
 			"version": "3.0.0",
 			"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz",
@@ -4497,11 +4562,50 @@
 			"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz",
 			"integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng=="
 		},
+		"node_modules/wmf": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz",
+			"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
+			"license": "Apache-2.0",
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/word": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
+			"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
+			"license": "Apache-2.0",
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
 		"node_modules/wrappy": {
 			"version": "1.0.2",
 			"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
 			"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
 			"dev": true
+		},
+		"node_modules/xlsx": {
+			"version": "0.18.5",
+			"resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
+			"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+			"license": "Apache-2.0",
+			"dependencies": {
+				"adler-32": "~1.3.0",
+				"cfb": "~1.2.1",
+				"codepage": "~1.15.0",
+				"crc-32": "~1.2.1",
+				"ssf": "~0.11.2",
+				"wmf": "~1.0.1",
+				"word": "~0.3.0"
+			},
+			"bin": {
+				"xlsx": "bin/xlsx.njs"
+			},
+			"engines": {
+				"node": ">=0.8"
+			}
 		},
 		"node_modules/xml-name-validator": {
 			"version": "4.0.0",
@@ -5572,6 +5676,11 @@
 			"dev": true,
 			"requires": {}
 		},
+		"adler-32": {
+			"version": "1.3.1",
+			"resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
+			"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="
+		},
 		"ajv": {
 			"version": "6.12.6",
 			"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -5740,6 +5849,15 @@
 				}
 			}
 		},
+		"cfb": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
+			"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+			"requires": {
+				"adler-32": "~1.3.0",
+				"crc-32": "~1.2.0"
+			}
+		},
 		"chalk": {
 			"version": "4.1.2",
 			"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -5792,6 +5910,11 @@
 				"tiny-emitter": "^2.0.0"
 			}
 		},
+		"codepage": {
+			"version": "1.15.0",
+			"resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz",
+			"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA=="
+		},
 		"color-convert": {
 			"version": "2.0.1",
 			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -5836,6 +5959,11 @@
 			"version": "2.8.0",
 			"resolved": "https://registry.npmjs.org/countup.js/-/countup.js-2.8.0.tgz",
 			"integrity": "sha512-f7xEhX0awl4NOElHulrl4XRfKoNH3rB+qfNSZZyjSZhaAoUk6elvhH+MNxMmlmuUJ2/QNTWPSA7U4mNtIAKljQ=="
+		},
+		"crc-32": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
+			"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
 		},
 		"cropperjs": {
 			"version": "1.6.1",
@@ -6362,6 +6490,11 @@
 				"combined-stream": "^1.0.8",
 				"mime-types": "^2.1.12"
 			}
+		},
+		"frac": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz",
+			"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="
 		},
 		"fs-extra": {
 			"version": "10.1.0",
@@ -7311,6 +7444,14 @@
 			"resolved": "https://registry.npmjs.org/splitpanes/-/splitpanes-3.1.5.tgz",
 			"integrity": "sha512-r3Mq2ITFQ5a2VXLOy4/Sb2Ptp7OfEO8YIbhVJqJXoFc9hc5nTXXkCvtVDjIGbvC0vdE7tse+xTM9BMjsszP6bw=="
 		},
+		"ssf": {
+			"version": "0.11.2",
+			"resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz",
+			"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+			"requires": {
+				"frac": "~1.1.2"
+			}
+		},
 		"ssr-window": {
 			"version": "3.0.0",
 			"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz",
@@ -7622,12 +7763,36 @@
 			"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz",
 			"integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng=="
 		},
+		"wmf": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz",
+			"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw=="
+		},
+		"word": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
+			"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA=="
+		},
 		"wrappy": {
 			"version": "1.0.2",
 			"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
 			"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
 			"dev": true
 		},
+		"xlsx": {
+			"version": "0.18.5",
+			"resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz",
+			"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
+			"requires": {
+				"adler-32": "~1.3.0",
+				"cfb": "~1.2.1",
+				"codepage": "~1.15.0",
+				"crc-32": "~1.2.1",
+				"ssf": "~0.11.2",
+				"wmf": "~1.0.1",
+				"word": "~0.3.0"
+			}
+		},
 		"xml-name-validator": {
 			"version": "4.0.0",
 			"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
diff --git a/package.json b/package.json
index dd468b3..b5ef475 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,8 @@
 		"vue-grid-layout": "^3.0.0-beta1",
 		"vue-i18n": "^9.10.2",
 		"vue-router": "^4.3.0",
-		"vue3-print-nb": "^0.1.4"
+		"vue3-print-nb": "^0.1.4",
+		"xlsx": "^0.18.5"
 	},
 	"devDependencies": {
 		"@types/node": "^20.11.28",
diff --git a/src/api/QC/index.ts b/src/api/QC/index.ts
new file mode 100644
index 0000000..012a5d0
--- /dev/null
+++ b/src/api/QC/index.ts
@@ -0,0 +1,207 @@
+import request from "/@/utils/request";
+import type { IPatientDialysisFrequencyCountRes, IPatientDialysisFrequencyCountParams, ItKtvAndUrrListByCondition, IGetKtvAndUrrListByCondition, IWypertensionStatistics, IWypertensionStatisticsParams, IQualityControlReportParams, IQualityControlReportReponse, WeightControlRateDetail, WeightControlRateByYearParams, WeightControlRateByYear, WeightControlRateDetailstParams } from './types'
+import { AxiosPromise } from 'axios';
+
+
+export function listWorkStats(params: object) {
+	return request({
+		url: '/patient/hemo/med/record/listWorkStats',
+		method: 'post',
+		data: params,
+	});
+}
+
+export function listHemoNurseWorkStats(params: object) {
+	return request({
+		url: '/patient/hemo/med/record/listHemoNurseWorkStats',
+		method: 'post',
+		data: params,
+	});
+}
+export function listRecordsByToolUsage(params: object) {
+	return request({
+		url: '/patient/hemo/med/record/listRecordsByToolUsage',
+		method: 'post',
+		data: params,
+	});
+}
+export function listRecordsByMedicineUsage(params: object) {
+	return request({
+		url: '/patient/hemo/med/record/listRecordsByMedicineUsage',
+		method: 'post',
+		data: params,
+	});
+}
+
+export function listDetailsOfMedicineUsage(params: object) {
+	return request({
+		url: '/patient/hemo/med/record/listDetailsOfMedicineUsage',
+		method: 'post',
+		data: params,
+	});
+}
+export function doStatByResult(params: object) {
+	return request({
+		url: '/lis/stat/doStatByResult',
+		method: 'post',
+		data: params,
+	});
+}
+export function doClientStat4(params: object) {
+	return request({
+		url: '/lis/stat/doClientStat4',
+		method: 'post',
+		data: params,
+	});
+}
+export function doListResultsGroupByMonth(params: object) {
+	return request({
+		url: '/lis/stat/doListResultsGroupByMonth',
+		method: 'post',
+		data: params,
+	});
+}
+export function doListTestItems() {
+	return request({
+		url: '/lis/stat/doListTestItems',
+		method: 'post',
+	});
+}
+export function doTestItemPassedCount(params: object) {
+	return request({
+		url: '/lis/stat/doTestItemPassedCount',
+		method: 'post',
+		data: params,
+	});
+}
+export function listGroups() {
+	return request({
+		url: '/client/group/info/listGroups',
+		method: 'post',
+	});
+}
+export function listDetailsEachOne(params) {
+	return request({
+		url: '/client/group/info/listDetailsEachOne',
+		method: 'post',
+		// headers: {
+        //     'Content-Type': 'application/x-www-form-urlencoded'
+        // },
+		params,
+	});
+}
+
+
+/**
+ * 重新计算KTV与URR
+ * @param params 
+ * @returns 
+ */
+export function doCalcKtvOrUrrApi(params) {
+	return request({
+		url: '/lis/stat/doCalcKtvOrUrr',
+		method: 'post',
+		params
+	})
+}
+
+/**
+ * 统计体重增长控制率
+ * @param params 
+ * @returns 
+ */
+export function weightControlRateByYearApi(params: WeightControlRateByYearParams): AxiosPromise<WeightControlRateByYear> {
+	return request({
+		url: '/patient/stat/weight/increase/range/weightControlRateByYear',
+		method: 'post',
+		params
+	})
+}
+
+/** 统计体重增长控制率详情 */
+export function weightControlRateDetailsApi(data: WeightControlRateDetailstParams): AxiosPromise<{ list: WeightControlRateDetail[]; total: number }> {
+	return request({
+		url: '/patient/stat/weight/increase/range/weightControlRateDetails',
+		method: 'post',
+		data
+	})
+}
+
+/**
+ * 查询KTV Or URR接口
+ * @param data 
+ * @returns 
+ */
+// export function getKtvAndUrrListByConditionsApi(data: IGetKtvAndUrrListByCondition) :AxiosPromise<{ list: ItKtvAndUrrListByCondition[]; total: number; }> {
+// 	return request({
+// 		url: '/patient/ktv/listByConditions',
+// 		method: 'post',
+// 		data
+// 	})
+// }
+
+/**
+ * 查询KTV Or URR接口
+ * @param data 
+ * @returns 
+ */
+export function getKtvAndUrrListByConditionsApi(data: IGetKtvAndUrrListByCondition) :AxiosPromise<{ list: ItKtvAndUrrListByCondition[]; total: number; }> {
+	return request({
+		url: '/patient/ktv/listByConditions',
+		method: 'post',
+		data
+	})
+}
+
+/**
+ * 药物用量统计页面
+ */
+
+export function listGroupByDrug(param) {
+	return request({
+		url: '/patient/drug/order/vs/patient/listGroupByDrug',
+		method: 'post',
+		params:param
+	})
+}
+
+/**
+ * 获取高血压控制率
+ * @param params 
+ * @returns 
+ */
+export function hypertensionStatisticsApi(params: IWypertensionStatisticsParams) :AxiosPromise<IWypertensionStatistics> {
+	return request({
+		url: '/patient/hemo/med/body/state/pre/hypertensionStatistics',
+		method: 'post',
+		params
+	})
+}
+
+/**
+ * 获取质控报表
+ * @param params 
+ * @returns 
+ */
+export function qualityControlReportApi(params: IQualityControlReportParams): AxiosPromise<IQualityControlReportReponse> {
+	return request({
+		url: '/patient/info/qualityControlReport',
+		method: 'post',
+		params
+	})
+}
+
+
+
+/**
+ * 月度患者透析次数统计接口
+ * @param data 
+ * @returns 
+ */
+export function patientDialysisFrequencyCountApi(data: IPatientDialysisFrequencyCountParams) :AxiosPromise<IPatientDialysisFrequencyCountRes> {
+	return request({
+		url: '/patient/hemo/med/record/patientDialysisFrequencyCount',
+		method: 'post',
+		data
+	})
+}
\ No newline at end of file
diff --git a/src/api/QC/type.ts b/src/api/QC/type.ts
new file mode 100644
index 0000000..1198e06
--- /dev/null
+++ b/src/api/QC/type.ts
@@ -0,0 +1,159 @@
+import { IComminList } from '../common.type';
+
+export interface WeightControlRateByYearParams {
+	clientCode: string;
+	year: number;
+	quarter: number;
+	maxValue: number;
+	minValue: number;
+}
+
+export interface WeightControlRateByYear {
+	allcount: number;
+	conditionCount: number;
+}
+
+export interface WeightControlRateDetailstParams {
+	year: number;
+	quarter: number;
+	weightControlRateDetailsCondition: {
+		page: number;
+		size: number;
+		clientCode: string;
+		maxValue: number;
+		minValue: number;
+	};
+}
+
+export interface WeightControlRateDetail {
+	age: string;
+	code: string;
+	patientGender: number;
+	patientName: string;
+}
+
+export interface IGetKtvAndUrrListByCondition {
+	page: number;
+	size: number;
+	clientCode: string;
+	startTime: string;
+	endTime: string;
+	patientName: string;
+	patientCodes: string[];
+}
+
+export interface ItKtvAndUrrListByCondition {
+	code: string; //数据代码
+	dataKtv: number; //KTV
+	dataMedPeriod: number; //透析时间(小时)
+	dataNiaosu1: number; //透前尿素
+	dataNiaosu2: number; //透后尿素
+	dataUrr: number; //URR
+	dataWeight1: number; //透前体重
+	dataWeight2: number; //透后体重
+	id: number; //记录ID
+	patientCode: string; //患者编号
+	patientName: string; //患者姓名
+	recordCode: string; //透析单
+	sampleDate: string; //采样日期
+}
+
+
+export interface IPatientDialysisFrequencyCountParams {
+	page: number;
+	size: number;
+	clientCode: string;
+	patientCodes: string[];
+	scheduleTimeSlots: string[];
+	recordsStatus: string;
+	startTime: string;
+	endTime: string;
+}
+
+
+export interface IPatientDialysisFrequencyCount {
+	allCount: string;
+	patientCode: string;
+	patientName: string;
+	typeCounts: IPatientDialysisFrequencyCountTypeCount[];
+}
+
+export interface IPatientDialysisFrequencyCountTypeCount {
+	schemeName: string;
+	typeCount: string;
+}
+
+
+export interface IPatientDialysisFrequencyCountRes extends Omit<IComminList, 'list'> {
+    list: IPatientDialysisFrequencyCount[];
+}
+
+export interface IWypertensionStatisticsParams {
+    clientCode: string;
+    beginTime: string;
+    endTime: string;
+}
+
+export interface IQualityControlReportLisResults {
+	client_code: string;
+	item_name: string;
+	item_result: string;
+	item_result_flag: string;
+	item_result_ref: string;
+	item_result_unit: string;
+	patient_name: string;
+	sample_date: string;
+	sample_date_str: string;
+	最新报告日期: string;
+}
+
+
+export interface IWypertensionStatistics {
+    大于60岁患者: IWypertensionStatisticsObj;
+    小于等于60岁患者: IWypertensionStatisticsObj;
+    没有年龄的患者: IWypertensionStatisticsItem[];
+    透析总人数: number;
+}
+
+export interface IWypertensionStatisticsItem {
+    patientCode: string;
+    patientName: string;
+    preMbpH: string;
+    preMbpL: string;
+    signTime: string;
+}
+
+export interface IWypertensionStatisticsObj {
+    count: number;
+    eligiblePatientCount: number;
+    eligiblePatients: IWypertensionStatisticsItem[];
+    noEligiblePatientCount: number;
+    noEligiblePatients: IWypertensionStatisticsItem[];
+}
+
+export interface IQualityControlReportParams {
+    clientCode: string;
+    beginTime: string;
+    endTime: string;
+    page: number;
+    size: number;
+	systemItemNames: string;
+}
+
+export interface IQualityControlReport {
+    accessFirstUseDate: string;
+    age: number;
+    code: string;
+    dictText: string;
+    patientDiagnose: string;
+    patientName: string;
+    sex: string;
+    survivalTime: string;
+	lisResults: IQualityControlReportLisResults[];
+}
+
+export interface IQualityControlReportReponse {
+    total: number;
+    list: IQualityControlReport[];
+    [key: string]: any;
+}
\ No newline at end of file
diff --git a/src/i18n/lang/zh-cn.ts b/src/i18n/lang/zh-cn.ts
index 177f0d2..8ce86df 100644
--- a/src/i18n/lang/zh-cn.ts
+++ b/src/i18n/lang/zh-cn.ts
@@ -72,6 +72,7 @@
 		visualizingLinkDemo2: '数据可视化演示2',
 		personal: '个人中心',
 		tongji:'患者健康服务统计',
+		xueqingdanbai:'质控-血清蛋白控制率',
 		tools: '工具类集合',
 		layoutLinkView: '外链',
 		layoutIframeViewOne: '内嵌 iframe1',
diff --git a/src/layout/navBars/topBar/breadcrumb.vue b/src/layout/navBars/topBar/breadcrumb.vue
index 5c3680d..caaef9e 100644
--- a/src/layout/navBars/topBar/breadcrumb.vue
+++ b/src/layout/navBars/topBar/breadcrumb.vue
@@ -26,12 +26,33 @@
 		<el-avatar shape="square" style="width: 48px; height: 35px;background-color: #ffffff;margin-right: 10px;margin-left: 10px;" fit="contain" :src="logo" />
 
 		<div class="titleHome">营养管理系统</div>
+		<div style="margin-left: 20px;">
+			<el-dropdown  @command="onHandleCommandClick">
+    			<span class="el-dropdown-link">
+					统计帮助
+					<el-icon class="el-icon--right">
+						<arrow-down />
+					</el-icon>
+			</span>
+			<template #dropdown>
+			<el-dropdown-menu>
+				<el-dropdown-item command="/tongji">患者健康服务统计</el-dropdown-item>
+				<el-dropdown-item command="/zhikong/xueqingdanbai">质控-血清蛋白控制率</el-dropdown-item>
+				<!-- <el-dropdown-item>Action 3</el-dropdown-item>
+				<el-dropdown-item disabled>Action 4</el-dropdown-item>
+				<el-dropdown-item divided>Action 5</el-dropdown-item> -->
+			</el-dropdown-menu>
+			</template>
+		</el-dropdown>
+		</div>
 
 		
 	</div>
+	
 </template>
 
 <script setup lang="ts" name="layoutBreadcrumb">
+import { ArrowDown } from '@element-plus/icons-vue'
 import { reactive, computed, onMounted } from 'vue';
 import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
 import { Local } from '/@/utils/storage';
@@ -104,6 +125,10 @@
 	if (state.breadcrumbList.length > 0)
 		state.breadcrumbList[state.breadcrumbList.length - 1].meta.tagsViewName = other.setTagsViewNameI18n(<RouteToFrom>route);
 };
+// 下拉菜单点击时
+const onHandleCommandClick = (path: string) => {
+	router.push(path);
+};
 // 页面加载时
 onMounted(() => {
 	initRouteSplit(route.path);
@@ -115,6 +140,12 @@
 </script>
 
 <style scoped lang="scss">
+.example-showcase .el-dropdown-link {
+  cursor: pointer;
+  color: var(--el-color-primary);
+  display: flex;
+  align-items: center;
+}
 .layout-navbars-breadcrumb {
 	flex: 1;
 	height: inherit;
diff --git a/src/layout/navBars/topBar/index.vue b/src/layout/navBars/topBar/index.vue
index 0b9c973..c26ba25 100644
--- a/src/layout/navBars/topBar/index.vue
+++ b/src/layout/navBars/topBar/index.vue
@@ -5,6 +5,7 @@
 		
 		<Horizontal :menuList="state.menuList" v-if="isLayoutTransverse" />
 		<User />
+		
 	</div>
 </template>
 
@@ -21,6 +22,7 @@
 const User = defineAsyncComponent(() => import('/@/layout/navBars/topBar/user.vue'));
 const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue'));
 const Horizontal = defineAsyncComponent(() => import('/@/layout/navMenu/horizontal.vue'));
+const setings = defineAsyncComponent(() => import('/@/layout/navBars/topBar/setings.vue'));
 
 // 定义变量内容
 const stores = useRoutesList();
diff --git a/src/router/route.ts b/src/router/route.ts
index 407ec7c..c498f65 100644
--- a/src/router/route.ts
+++ b/src/router/route.ts
@@ -1135,6 +1135,21 @@
 				},
 			},
 			{
+				path: '/zhikong/xueqingdanbai',
+				name: 'xueqingdanbai',
+				component: () => import('/@/views/zhikong/xueqingdanbai.vue'),
+				meta: {
+					title: 'message.router.xueqingdanbai',
+					isLink: '',
+					isHide: false,
+					isKeepAlive: true,
+					isAffix: false,
+					isIframe: false,
+					roles: ['admin', 'common'],
+					icon: 'iconfont icon-gerenzhongxin',
+				},
+			},
+			{
 				path: '/tools',
 				name: 'tools',
 				component: () => import('/@/views/tools/index.vue'),
diff --git a/src/utils/Export2.ts b/src/utils/Export2.ts
new file mode 100644
index 0000000..18503d6
--- /dev/null
+++ b/src/utils/Export2.ts
@@ -0,0 +1,174 @@
+import FileSaver from 'file-saver'
+import * as XLSX from 'xlsx'
+// 自动计算col列宽
+function auto_width(ws, data) {
+  /*set worksheet max width per col*/
+  const colWidth = data.map(row => row.map(val => {
+    /*if null/undefined*/
+    if (val == null) {
+      return { 'wch': 10 }
+    }
+    /*if chinese*/
+    else if (val.toString().charCodeAt(0) > 255) {
+      return { 'wch': val.toString().length * 2 }
+    } else {
+      return { 'wch': val.toString().length }
+    }
+  }))
+  /*start in the first row*/
+  let result = colWidth[0]
+  for (let i = 1; i < colWidth.length; i++) {
+    for (let j = 0; j < colWidth[i].length; j++) {
+      if (result[j]['wch'] < colWidth[i][j]['wch']) {
+        result[j]['wch'] = colWidth[i][j]['wch']
+      }
+    }
+  }
+  ws['!cols'] = result
+}
+// 将json数据转换成数组
+function json_to_array(key, jsonData) {
+  return jsonData.map(v => key.map(j => {
+    return v[j]
+  }))
+}
+/**
+ * @param header Object,表头
+ * @param data Array,表体数据
+ * @param key Array,字段名
+ * @param title String,标题(会居中显示),即excel表格第一行
+ * @param filename String,文件名
+ * @param autoWidth Boolean,是否自动根据key自定义列宽度
+ */
+export const exportJsonToExcel = ({
+  header,
+  data,
+  key,
+  title,
+  filename,
+  autoWidth
+}) => {
+  const wb = XLSX.utils.book_new()
+  if (header) {
+    data.unshift(header)
+  }
+  if (title) {
+    data.unshift(title)
+  }
+  const ws = XLSX.utils.json_to_sheet(data, {
+    header: key,
+    skipHeader: true
+  })
+  // 合并单元格以覆盖整个第一行
+  // ws['!merges'] = [{ s: {r:0, c:0}, e: {r:0, c:header.length - 1} }];
+  if (autoWidth) {
+    const arr = json_to_array(key, data)
+    auto_width(ws, arr)
+  }
+  XLSX.utils.book_append_sheet(wb, ws, filename)
+  XLSX.writeFile(wb, filename + '.xlsx')
+}
+
+/**
+ * 自动计算并设置列宽
+ * @param {Object} ws - 工作表对象
+ * @param {Array} data - 表格数据(二维数组)
+ */
+function autoWidth(ws, data) {
+  // 初始化每列宽度为默认值10
+  const colWidths = Array(data[0].length).fill(10);
+
+  // 遍历数据以计算最大宽度
+  data.forEach(row => {
+    row.forEach((val, index) => {
+      let length = val ? (typeof val === 'string' ? getStrLen(val) : String(val).length) : 10;
+      if (colWidths[index] < length) {
+        colWidths[index] = length; // 更新最大宽度
+      }else{
+        colWidths[index] = 20; // 更新最大宽度
+      }
+    });
+  });
+
+  // 设置列宽
+  ws['!cols'] = colWidths.map(width => ({ wch: width }));
+}
+/**
+ * 计算字符串长度,考虑多字节字符(如中文)
+ * @param {string} str - 输入字符串
+ * @returns {number} 字符串长度
+ */
+function getStrLen(str) {
+  return [...str].reduce((len, char) => len + (char.charCodeAt(0) > 255 ? 2 : 1), 0);
+}
+export const exportTableToExcel=(divId:string,name:string)=>{
+  var wb = XLSX.utils.table_to_book(document.querySelector(divId));//关联dom节点
+  console.log(wb)
+  // 获取第一个工作表
+  const ws = wb.Sheets[wb.SheetNames[0]];
+  
+  // 获取表格数据作为二维数组
+  const data = XLSX.utils.sheet_to_json(ws, { header: 1 });
+
+  // 设置列宽
+  autoWidth(ws, data);
+  var wbout = XLSX.write(wb, {
+		bookType: 'xlsx',
+		bookSST: true,
+		type: 'array'
+	})
+  try {
+		FileSaver.saveAs(new Blob([wbout], {
+			type: 'application/octet-stream'
+		}), `${name}.xlsx`)//自定义文件名
+	} catch (e) {
+		if (typeof console !== 'undefined') console.log(e, wbout);
+	}
+	return wbout
+}
+
+export const exportTableToExcel2 = async (divId: string, name: string): Promise<Uint8Array | null> => {
+  try {
+    // 关联 DOM 节点
+    const tableElement = document.querySelector(divId);
+    if (!tableElement) {
+      console.error("Table element not found");
+      return null;
+    }
+
+    // 转换表格为工作簿
+    const wb = XLSX.utils.table_to_book(tableElement);
+    console.log(wb);
+
+    // 将工作簿写入文件
+    const wbout = XLSX.write(wb, {
+      bookType: 'xlsx',
+      bookSST: true,
+      type: 'array',
+    });
+
+    // 保存文件
+    await new Promise<void>((resolve, reject) => {
+      try {
+        FileSaver.saveAs(
+          new Blob([wbout], { type: 'application/octet-stream' }),
+          `${name}.xlsx`
+        );
+        resolve();
+      } catch (e) {
+        console.error("Error saving file:", e);
+        reject(e);
+      }
+    });
+
+    // 返回生成的二进制数组
+    return wbout;
+  } catch (error) {
+    console.error("Error exporting table to Excel:", error);
+    return null;
+  }
+};
+export default {
+  exportJsonToExcel,
+  exportTableToExcel
+}
\ No newline at end of file
diff --git a/src/views/tongji/index.vue b/src/views/tongji/index.vue
index b660912..5aedd47 100644
--- a/src/views/tongji/index.vue
+++ b/src/views/tongji/index.vue
@@ -17,6 +17,9 @@
 			<el-form-item>
 				<el-button type="primary" @click="onSubmit">查询</el-button>
 			</el-form-item>
+			<el-form-item>
+				<el-button type="success" @click="daochuExcel">导出</el-button>
+			</el-form-item>
 		</el-form>
 
 		<div class="scrollable-table" :style="{maxHeight:tableHe}">
@@ -130,6 +133,7 @@
 import {nutritionalSummary} from '/@/api/Patients'
 import {editUserInfo} from '/@/api/login'
 import { ElLoading } from 'element-plus';
+import * as XLSX from 'xlsx';
 import {formatDate} from '/@/utils/formatTime'
 const stores = useUserInfo();
 const { userInfos } = storeToRefs(stores);
@@ -289,6 +293,15 @@
     },
     dialogTableVisible:false
 })
+const daochuExcel=()=>{
+	let table = document.getElementById('tabledome');
+      if (table) {
+		const wb = XLSX.utils.table_to_book(table, { sheet: "Sheet1" });
+      	XLSX.writeFile(wb, "患者健康服务统计.xlsx"); // 文件名和扩展名可以根据需要更改
+      } else {
+        console.error("找不到指定的表格");
+      }
+}
 const onSubmit=()=>{
 	const pasm={
 		startTime:state.formInline.date[0]+ ' 00:00:00',
@@ -297,9 +310,16 @@
 		queryValue:''
 	}
 	console.log(pasm)
+	const loading = ElLoading.service({
+		lock: true,
+		text: 'Loading',
+		background: 'rgba(0, 0, 0, 0.7)',
+	})
 	nutritionalSummary(pasm).then(re=>{
 		console.log(re.data)
 		state.tableData=re.data
+	}).finally(()=>{
+		loading.close()
 	})
 
 }
diff --git a/src/views/zhikong/components/components.vue b/src/views/zhikong/components/components.vue
new file mode 100644
index 0000000..14168bc
--- /dev/null
+++ b/src/views/zhikong/components/components.vue
@@ -0,0 +1,617 @@
+<template>
+	<el-row class="control_rate_container">
+		<el-col :span="24" class="card_box card_box_search">
+			<el-form :model="searchForm" size="small" inline>
+				<el-form-item label="查询日期">
+					<el-date-picker
+						v-model="searchForm.dates"
+						type="daterange"
+						unlink-panels
+						range-separator="至"
+						start-placeholder="开始日期"
+						end-placeholder="结束日期"
+						:clearable="false"
+					/>
+				</el-form-item>
+				<el-form-item label="透析龄">
+					<el-select v-model="searchForm.medMonth" placeholder="透析龄">
+						<el-option label="全部" :value="0" />
+						<el-option label=">=三个月" :value="1" />
+						<el-option label="<三个月" :value="2" />
+					</el-select>
+				</el-form-item>
+				<el-form-item label="转归状态">
+					<el-select v-model="searchForm.isInState" placeholder="转归状态">
+						<el-option label="不限" :value="0" />
+						<el-option label="在院" :value="1" />
+						<el-option label="转出" :value="2" />
+					</el-select>
+				</el-form-item>
+				<el-form-item v-if="!isBloodPressure" label="标准值设定" prop="">
+					<el-row>
+						<span :span="8">
+							<el-input style="width: 150px" v-model="searchForm.limitBottom" @input="limitBottomInp"></el-input>
+						</span>
+						<span :span="8">≤标准值≤</span>
+						<span :span="8">
+							<el-input style="width: 150px" v-model="searchForm.limitTop" @input="limitTopInp"></el-input>
+						</span>
+					</el-row>
+				</el-form-item>
+				<template v-else>
+					<el-form-item label="收缩压标准值设定" prop="">
+						<el-row>
+							<span :span="8">
+								<el-input style="width: 150px" v-model="searchForm.limitBottom" @input="limitBottomInp"></el-input>
+							</span>
+							<span :span="8">≤标准值≤</span>
+							<span :span="8">
+								<el-input style="width: 150px" v-model="searchForm.limitTop" @input="limitTopInp"></el-input>
+							</span>
+						</el-row>
+					</el-form-item>
+					<el-form-item label="舒张压标准值设定" prop="">
+						<el-row>
+							<span :span="8">
+								<el-input style="width: 150px" v-model="searchForm.limitBplBottom" @input="limitBottomInp2"></el-input>
+							</span>
+							<span :span="8">≤标准值≤</span>
+							<span :span="8">
+								<el-input style="width: 150px" v-model="searchForm.limitBplTop" @input="limitTopInp2"></el-input>
+							</span>
+						</el-row>
+					</el-form-item>
+				</template>
+				<el-form-item label="年龄标准值设定" prop="">
+					<el-row>
+						<span :span="8">
+							<el-input style="width: 150px" v-model="searchForm.limitAgeBottom" type="number"></el-input>
+						</span>
+						<span :span="8">≤标准值≤</span>
+						<span :span="8">
+							<el-input style="width: 150px" v-model="searchForm.limitAgeTop" type="number"></el-input>
+						</span>
+					</el-row>
+				</el-form-item>
+				<el-form-item label="是否只展示最后一次结果">
+					<el-switch v-model="searchForm.是否只展示最后一次结果" :active-value="1" :inactive-value="0" />
+				</el-form-item>
+				<el-form-item>
+					<el-button size="small" :loading="loading" @click="onClickSearch" type="success">
+						<el-icon><Search /></el-icon>
+						查询</el-button
+					>
+					<slot name="right"></slot>
+				</el-form-item>
+				<el-form-item></el-form-item>
+			</el-form>
+		</el-col>
+		<el-col :span="24" v-loading="loading">
+			<el-row :gutter="20">
+				<el-col :span="12">
+					<div class="container_l" :style="{ height: tableHeight + 'px', 'min-height': '520px' }">
+						<div v-loading="loading" ref="myEchartsRef" style="height: 500px; width: 100%; padding-top: 10px"></div>
+					</div>
+				</el-col>
+				<el-col :span="12">
+					<div class="container_r" :style="{ height: tableHeight + 'px', 'min-height': '520px' }">
+						<div class="untested_patients_box" v-if="untestedPatients.length > 0">
+							<div class="untested_patients_header">未检测人员</div>
+							<div class="untested_patients_conter">
+								{{ untestedPatients.join('、') }}
+							</div>
+						</div>
+						<div class="status_type_nav">
+							<span
+								v-for="(item, index) in statusTypeOptions"
+								:key="index"
+								:class="[statusType === item.value ? 'select_type' : '']"
+								@click="onClickByStatusChange(item)"
+							>
+								{{ item.label }}
+							</span>
+						</div>
+						<el-table
+							v-if="tableHeight > 0"
+							v-loading="loading"
+							id="exportTbale"
+							:row-class-name="rowClassName"
+							:data="patTableData"
+							:height="untestedPatients.length > 0 ? tableHeight - 210 : tableHeight"
+							border
+							style="width: 100%"
+						>
+							<el-table-column prop="sampleDate" label="检验日期">
+								<template #default="scope">
+									{{ scope.row.sampleDate.substring(0, 11) }}
+								</template>
+							</el-table-column>
+							<el-table-column prop="patientName" label="患者姓名" />
+							<el-table-column prop="itemResult" :label="defaultNameAndUnit" />
+						</el-table>
+					</div>
+				</el-col>
+			</el-row>
+		</el-col>
+	</el-row>
+</template>
+
+<script lang="ts">
+import { ElMessage } from 'element-plus';
+import { defineComponent, reactive, toRefs, ref, onMounted, getCurrentInstance, watch } from 'vue';
+import { storeToRefs } from 'pinia';
+import { useUserInfo } from '/@/stores/userInfo';
+import { formatDate } from '/@/utils/formatTime';
+import { doStatByResult } from '/@/api/QC';
+import * as echarts from 'echarts';
+const stores = useUserInfo();
+const { userInfos } = storeToRefs(stores);
+const end = new Date();
+const start = new Date();
+start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+
+interface IDefDatas {
+	title: string;
+	itemName: string;
+	defaultNameAndUnit: string;
+	subTit: string;
+	whereFrom?: Partial<{
+		itemName: string;
+		medMonth: number;
+		isInState: number;
+		statType: number;
+		limitBottom: string | number;
+		limitTop: string | number;
+		filterStyle: number;
+		dates: [Date, Date];
+		attendingDoctor: '';
+		是否只展示最后一次结果: 0 | 1;
+	}>;
+	isBloodPressure?: boolean;
+}
+export default defineComponent({
+	name: 'ControlRate',
+	setup() {
+		const { proxy } = getCurrentInstance() as any;
+		const myEchartsRef = ref();
+
+		const state = reactive({
+			loading: false,
+			tableHeight: 0,
+			title: '',
+			itemName: '',
+			defaultNameAndUnit: '',
+			subTit: '',
+			patList: [],
+			untestedPatients: [],
+			dabaio: true,
+			budabaio: false,
+			searchForm: {
+				itemName: '',
+				medMonth: 0,
+				isInState: 1,
+				statType: 0,
+				limitBottom: '',
+				limitTop: '',
+				// filterStyle: 0,
+				dates: [start, end],
+				attendingDoctor: '', // 主治医师
+				是否只展示最后一次结果: 0,
+				limitBplBottom: null,
+				limitBplTop: null,
+				limitAgeBottom: null,
+				limitAgeTop: null,
+			},
+			patTableData: [],
+			statusType: 0,
+			statusTypeOptions: [
+				{ label: '全部', value: 0 },
+				{ label: '达标', value: 1 },
+				{ label: '不达标', value: 2 },
+			],
+			doctorOptions: [],
+			isBloodPressure: false, // 是否是血压
+		});
+
+		// 监听 searchForm.limitBottom 的变化,确保 Vue 的双向绑定正常工作
+		// watch(() => state.searchForm.limitBottom, (newValue) => {
+		// if (newValue === '' || Number(newValue) < 0) {
+		// 	state.searchForm.limitBottom = '';
+		// } else {
+		// 	// @ts-ignore
+		// 	state.searchForm.limitBottom = Number(newValue);
+		// }
+		// });
+
+		const onClickByStatusChange = (item: { label: string; value: number }) => {
+			state.statusType = item.value;
+			if (item.value === 0) {
+				state.patTableData = [...state.patList];
+			} else if (item.value === 1) {
+				state.patTableData = state.patList.filter((e) => e.是否达标);
+			} else if (item.value === 2) {
+				state.patTableData = state.patList.filter((e) => !e.是否达标);
+			}
+		};
+
+		/** 查询 */
+		const onClickSearch = async () => {
+			if (isNaN(+state.searchForm.limitBottom) || isNaN(+state.searchForm.limitTop)) return ElMessage.warning('标准值仅可为大于等于0的数值');
+			if (+state.searchForm.limitBottom < 0 || +state.searchForm.limitTop < 0) return ElMessage.warning('标准值不能小于0');
+			state.loading = true;
+			try {
+				await getDatas();
+			} finally {
+				state.loading = false;
+			}
+		};
+
+		// /** 导出 */
+		// const onClickExportExcel = () => {
+		// 	exportTableToExcel('#exportTbale', state.title);
+		// };
+
+		const setTableHeight = () => {
+			let height = document.documentElement.clientHeight;
+
+			const navDom = document.querySelector('.layout-header');
+			if (navDom) {
+				height -= navDom.scrollHeight;
+			}
+
+			const demoFormDom = document.querySelector('.card_box_search');
+
+			if (demoFormDom) {
+				height -= demoFormDom.scrollHeight;
+			}
+
+			height = height - 40;
+			state.tableHeight = height;
+		};
+
+		const initCompData = async (objData: IDefDatas) => {
+			state.loading = true;
+			try {
+				const { title, itemName, defaultNameAndUnit, subTit, whereFrom, isBloodPressure } = objData;
+				state.title = title;
+				state.itemName = itemName;
+				state.defaultNameAndUnit = defaultNameAndUnit;
+				state.subTit = subTit;
+				state.isBloodPressure = !!isBloodPressure;
+				if (whereFrom) {
+					Object.keys(whereFrom).forEach((k) => {
+						state.searchForm[k] = whereFrom[k];
+					});
+				}
+				const primaseFuns = [getDatas()];
+				await Promise.all(primaseFuns);
+				await getDatas();
+			} catch (error) {
+				// ElMessage.error('系统异常,请联系管理员:' + error);
+				console.error('xxxxxxxxxxx:', error);
+			} finally {
+				state.loading = false;
+			}
+		};
+
+		const getDatas = async () => {
+			const params = {
+				clientCode: userInfos.value.clientCode,
+				dateBegin: formatDate(state.searchForm.dates[0], 'YYYY-mm-dd') + ' 00:00:00',
+				dateEnd: formatDate(state.searchForm.dates[1], 'YYYY-mm-dd') + ' 23:59:59',
+				itemName: state.itemName,
+				medMonth: state.searchForm.medMonth,
+				isInState: state.searchForm.isInState,
+				statType: state.searchForm.statType,
+				limitBottom: state.searchForm.limitBottom,
+				limitTop: state.searchForm.limitTop,
+				// filterStyle: state.searchForm.filterStyle,
+				attendingDoctor: state.searchForm.attendingDoctor,
+				是否只展示最后一次结果: state.searchForm.是否只展示最后一次结果,
+				limitBplBottom: state.searchForm.limitBplBottom,
+				limitBplTop: state.searchForm.limitBplTop,
+				limitAgeBottom: state.searchForm.limitAgeBottom,
+				limitAgeTop: state.searchForm.limitAgeTop,
+			};
+			const { data } = await doStatByResult(params);
+			state.defaultNameAndUnit = data.itemUnit ? `${state.subTit}(${data.itemUnit})` : state.defaultNameAndUnit;
+			state.patList = data.statItemResults.slice().sort(compareDates);
+			if (state.statusType === 0) {
+				state.patTableData = [...state.patList];
+			} else if (state.statusType === 1) {
+				state.patTableData = state.patList.filter((e) => e.是否达标);
+			} else if (state.statusType === 2) {
+				state.patTableData = state.patList.filter((e) => !e.是否达标);
+			}
+			state.untestedPatients = data.未检测患者列表;
+			initEcharts(
+				data.statDetail.map((v) => {
+					v.tooltip = {
+						formatter: '{a}<br />{b}: ({c})({d}%)',
+					};
+					return v;
+				})
+			);
+		};
+
+		const initEcharts = (listdata: any) => {
+			//先获取Dom上的实例
+			if (echarts) {
+				let myChart = echarts?.getInstanceByDom(proxy.$refs.myEchartsRef as HTMLDivElement);
+				//然后判断实例是否存在,如果不存在,就创建新实例
+				if (myChart == null) {
+					myChart = echarts.init(proxy.$refs.myEchartsRef as HTMLDivElement);
+				}
+				const option = {
+					title: {
+						text: state.title,
+						subtext: `${formatDate(state.searchForm.dates[0], 'YYYY-mm-dd')}-${formatDate(state.searchForm.dates[1], 'YYYY-mm-dd')}`,
+						left: 'center',
+					},
+					tooltip: {
+						trigger: 'item',
+					},
+					legend: {
+						orient: 'vertical',
+						left: 'top',
+					},
+					color: ['#5470c6', '#fc8251', '#91cd77', '#ef6567', '#f9c956', '#75bedc'],
+					series: [
+						{
+							name: state.subTit,
+							type: 'pie',
+							radius: '70%',
+							// avoidLabelOverlap: false,
+							label: {
+								show: true,
+								overflow: 'break',
+								position: 'outer',
+								alignTo: 'none',
+								edgeDistance: '25%',
+								bleedMargin: 10,
+								distanceToLabelLine: 5,
+								formatter(param) {
+									return param.name + ' (' + param.value + ')' + ' (' + param.percent * 1 + '%)';
+								},
+							},
+							emphasis: {
+								itemStyle: {
+									shadowBlur: 10,
+									shadowOffsetX: 0,
+									shadowColor: 'rgba(0, 0, 0, 0.5)',
+								},
+							},
+							labelLine: {
+								show: false,
+							},
+							data: listdata,
+						},
+					],
+				};
+				myChart.setOption(option);
+				myChart.on('legendselectchanged', function (event) {
+					// @ts-ignore
+					if (event.name === '不达标的患者人数') {
+						state.dabaio = !state.dabaio;
+						// @ts-ignore
+					} else if (event.name === '达标患者人数') {
+						state.budabaio = !state.budabaio;
+					}
+					// 在这里可以根据需要处理点击图例的逻辑
+				});
+			}
+		};
+
+		const compareDates = (a, b) => {
+			const dateA = new Date(a.sampleDate);
+			const dateB = new Date(b.sampleDate);
+			// @ts-ignore
+			return dateA - dateB; // 升序排序
+		};
+
+		const rowClassName = ({ row }) => {
+			if (row.是否达标 === state.dabaio && row.是否达标 === state.budabaio) {
+				return 'hidden-row';
+			}
+			return '';
+		};
+
+		const limitBottomInp = () => {
+			let newValue = state.searchForm.limitBottom;
+
+			// 过滤掉非法字符,允许数字和小数点
+			newValue = newValue.replace(/[^0-9.]/g, '');
+
+			// 确保第一个字符不是小数点
+			if (newValue.startsWith('.')) {
+				newValue = '0' + newValue;
+			}
+
+			// 确保只有一个小数点
+			newValue = newValue.replace(/(\..*)\./g, '$1');
+
+			// 如果值为空或小于等于0,清空输入框
+			if (newValue !== '' && parseFloat(newValue) < 0) {
+				newValue = '';
+			}
+			state.searchForm.limitBottom = newValue;
+		};
+
+		const limitTopInp = () => {
+			let newValue = state.searchForm.limitTop;
+
+			// 过滤掉非法字符,允许数字和小数点
+			newValue = newValue.replace(/[^0-9.]/g, '');
+
+			// 确保第一个字符不是小数点
+			if (newValue.startsWith('.')) {
+				newValue = '0' + newValue;
+			}
+
+			// 确保只有一个小数点
+			newValue = newValue.replace(/(\..*)\./g, '$1');
+
+			// 如果值为空或小于等于0,清空输入框
+			if (newValue !== '' && parseFloat(newValue) < 0) {
+				newValue = '';
+			}
+			state.searchForm.limitTop = newValue;
+		};
+
+		const limitBottomInp2 = () => {
+			let newValue = state.searchForm.limitBplBottom;
+
+			// 过滤掉非法字符,允许数字和小数点
+			newValue = newValue.replace(/[^0-9.]/g, '');
+
+			// 确保第一个字符不是小数点
+			if (newValue.startsWith('.')) {
+				newValue = '0' + newValue;
+			}
+
+			// 确保只有一个小数点
+			newValue = newValue.replace(/(\..*)\./g, '$1');
+
+			// 如果值为空或小于等于0,清空输入框
+			if (newValue !== '' && parseFloat(newValue) < 0) {
+				newValue = '';
+			}
+			state.searchForm.limitBplBottom = newValue;
+		};
+
+		const limitTopInp2 = () => {
+			let newValue = state.searchForm.limitBplTop;
+
+			// 过滤掉非法字符,允许数字和小数点
+			newValue = newValue.replace(/[^0-9.]/g, '');
+
+			// 确保第一个字符不是小数点
+			if (newValue.startsWith('.')) {
+				newValue = '0' + newValue;
+			}
+
+			// 确保只有一个小数点
+			newValue = newValue.replace(/(\..*)\./g, '$1');
+
+			// 如果值为空或小于等于0,清空输入框
+			if (newValue !== '' && parseFloat(newValue) < 0) {
+				newValue = '';
+			}
+			state.searchForm.limitBplTop = newValue;
+		};
+
+		onMounted(() => {
+			setTableHeight();
+		});
+
+		return {
+			...toRefs(state),
+			myEchartsRef,
+			rowClassName,
+			onClickSearch,
+			initCompData,
+			onClickByStatusChange,
+			limitBottomInp,
+			limitTopInp,
+			limitBottomInp2,
+			limitTopInp2,
+		};
+	},
+});
+</script>
+
+<style lang="scss" scoped>
+.control_rate_container {
+	.card_box {
+		padding: 15px;
+		background: #fff;
+		padding-bottom: 0;
+		&.card_box_search {
+			padding-bottom: 0;
+			margin-bottom: 10px;
+		}
+	}
+	.sum_count {
+		padding-right: 20px;
+		line-height: 40px;
+		font-size: 14px;
+		font-weight: bold;
+		text-align: right;
+		color: #67c23a;
+	}
+
+	.container_l,
+	.container_r {
+		background: #fff;
+		overflow-y: auto;
+		.untested_patients_box {
+			background-color: #fff;
+			padding: 10px;
+			.untested_patients_header {
+				text-align: center;
+				font-weight: bold;
+				background: #409eff;
+				color: #fff;
+				line-height: 40px;
+			}
+			.untested_patients_conter {
+				padding: 20px;
+				max-height: 200px;
+				overflow: hidden;
+				overflow-y: auto;
+			}
+		}
+	}
+	.status_type_nav {
+		display: flex;
+		align-items: center;
+		padding-top: 20px;
+		span {
+			background: #fff;
+			color: #333;
+			padding: 8px 14px;
+			font-size: 14px;
+			border-radius: 5px 5px 0 0;
+			box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.12);
+			transition: 0.3s;
+			&:hover {
+				cursor: pointer;
+				background: #92c3f5;
+				color: #fff;
+			}
+			&.select_type {
+				background-color: #409eff;
+				color: #fff;
+			}
+		}
+	}
+}
+.xuedanbai {
+	.el-table .hidden-row {
+		display: none;
+	}
+	.chaxun {
+		padding-left: 10px;
+		padding-top: 10px;
+		padding-bottom: -10px;
+		background: #ffffff;
+		box-shadow: -1px 1px 6px 1px rgba(207, 212, 218, 0.5);
+		border-radius: 0px 4px 4px 4px;
+	}
+	.bodyclass {
+		background: #ffffff;
+		box-shadow: -1px 1px 6px 1px rgba(207, 212, 218, 0.5);
+		border-radius: 0px 4px 4px 4px;
+	}
+	/*在谷歌下移除input[number]的上下箭头*/
+	.inputNumber input[type='number']::-webkit-outer-spin-button,
+	.inputNumber input[type='number']::-webkit-inner-spin-button {
+		-webkit-appearance: none !important;
+		margin: 0;
+	}
+	/*在firefox下移除input[number]的上下箭头*/
+	.inputNumber input[type='number'] {
+		-moz-appearance: textfield;
+	}
+}
+</style>
\ No newline at end of file
diff --git a/src/views/zhikong/xueqingdanbai.vue b/src/views/zhikong/xueqingdanbai.vue
new file mode 100644
index 0000000..49865e9
--- /dev/null
+++ b/src/views/zhikong/xueqingdanbai.vue
@@ -0,0 +1,36 @@
+<template>
+	<ControlRate ref="controlRateRef" />
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted } from 'vue';
+import ControlRate from './components/components.vue';
+
+const controlRateRef = ref();
+
+onMounted(() => {
+	const end = new Date();
+	const start = new Date();
+	start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+	controlRateRef.value &&
+		controlRateRef.value.initCompData({
+			title: '血清白蛋白控制率',
+			itemName: '白蛋白',
+			defaultNameAndUnit: '血清白蛋白(g/L)',
+			subTit: '血清白蛋白',
+			whereFrom: {
+				itemName: '',
+				medMonth: 0,
+				isInState: 1,
+				statType: 0,
+				limitBottom: 35,
+				// filterStyle: 0,
+				limitTop: '',
+				dates: [start, end],
+			},
+		});
+});
+</script>
+
+<style>
+</style>
\ No newline at end of file

--
Gitblit v1.8.0