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