web-core-tcm 0.0.24 → 0.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +7 -0
- package/.github/workflows/test.yml +29 -0
- package/.prettierrc.json +5 -0
- package/.vscode/extensions.json +15 -0
- package/.vscode/settings.json +9 -0
- package/eslint.config.js +83 -0
- package/index.html +24 -0
- package/package.json +1 -4
- package/postcss.config.js +29 -0
- package/public/favicon.ico +0 -0
- package/public/icons/favicon-128x128.png +0 -0
- package/public/icons/favicon-16x16.png +0 -0
- package/public/icons/favicon-32x32.png +0 -0
- package/public/icons/favicon-96x96.png +0 -0
- package/quasar.config.ts +233 -0
- package/src/App.vue +7 -0
- package/src/api/algorithm/comprehensiveAlgorithm.ts +20 -0
- package/src/api/algorithm/index.ts +50 -0
- package/src/api/algorithm/inquiriesAlgorithm.ts +16 -0
- package/src/api/algorithm/inspectionsAlgorithm.ts +11 -0
- package/src/api/algorithm/lisemsAlgorithm.ts +16 -0
- package/src/api/algorithm/pulsationsAlgorithm.ts +8 -0
- package/src/api/authorization/alova/apiDefinitions.ts +23 -0
- package/src/api/authorization/alova/createApis.ts +114 -0
- package/src/api/authorization/alova/globals.d.ts +394 -0
- package/src/api/authorization/alova/implement/authorization.ts +14 -0
- package/src/api/authorization/alova/implement/index.ts +1 -0
- package/src/api/authorization/alova/index.ts +22 -0
- package/src/api/authorization/authorization.ts +16 -0
- package/src/api/authorization/index.ts +2 -0
- package/src/api/check/alova/apiDefinitions.ts +30 -0
- package/src/api/check/alova/createApis.ts +114 -0
- package/src/api/check/alova/globals.d.ts +1257 -0
- package/src/api/check/alova/implement/check.ts +165 -0
- package/src/api/check/alova/implement/index.ts +1 -0
- package/src/api/check/alova/index.ts +22 -0
- package/src/api/check/check.ts +217 -0
- package/src/api/check/index.ts +2 -0
- package/src/api/config/alova/index.ts +71 -0
- package/src/api/config/index.ts +132 -0
- package/src/api/device/device.js +58 -0
- package/src/api/doctor/alova/apiDefinitions.ts +28 -0
- package/src/api/doctor/alova/createApis.ts +114 -0
- package/src/api/doctor/alova/globals.d.ts +559 -0
- package/src/api/doctor/alova/implement/doctor.ts +51 -0
- package/src/api/doctor/alova/implement/index.ts +1 -0
- package/src/api/doctor/alova/index.ts +23 -0
- package/src/api/doctor/doctor.ts +53 -0
- package/src/api/doctor/index.ts +2 -0
- package/src/api/index.ts +12 -0
- package/src/api/metric/implement/index.ts +1 -0
- package/src/api/metric/implement/metric.ts +108 -0
- package/src/api/metric/index.ts +2 -0
- package/src/api/metric/metric.ts +114 -0
- package/src/api/oauth/alova/apiDefinitions.ts +26 -0
- package/src/api/oauth/alova/createApis.ts +114 -0
- package/src/api/oauth/alova/globals.d.ts +460 -0
- package/src/api/oauth/alova/implement/index.ts +1 -0
- package/src/api/oauth/alova/implement/oauth.ts +24 -0
- package/src/api/oauth/alova/index.ts +21 -0
- package/src/api/oauth/index.ts +2 -0
- package/src/api/oauth/oauth.ts +19 -0
- package/src/api/outpatient/alova/apiDefinitions.ts +27 -0
- package/src/api/outpatient/alova/createApis.ts +114 -0
- package/src/api/outpatient/alova/globals.d.ts +685 -0
- package/src/api/outpatient/alova/implement/index.ts +1 -0
- package/src/api/outpatient/alova/implement/outpatient.ts +91 -0
- package/src/api/outpatient/alova/index.ts +22 -0
- package/src/api/outpatient/index.ts +2 -0
- package/src/api/outpatient/outpatient.ts +67 -0
- package/src/api/patient/alova/apiDefinitions.ts +41 -0
- package/src/api/patient/alova/createApis.ts +114 -0
- package/src/api/patient/alova/globals.d.ts +1690 -0
- package/src/api/patient/alova/implement/index.ts +2 -0
- package/src/api/patient/alova/implement/meta.ts +517 -0
- package/src/api/patient/alova/implement/patient.ts +99 -0
- package/src/api/patient/alova/index.ts +22 -0
- package/src/api/patient/core.ts +133 -0
- package/src/api/patient/index.ts +4 -0
- package/src/api/patient/meta.ts +570 -0
- package/src/api/patient/patient.ts +98 -0
- package/src/api/prescription/alova/apiDefinitions.ts +29 -0
- package/src/api/prescription/alova/createApis.ts +114 -0
- package/src/api/prescription/alova/globals.d.ts +968 -0
- package/src/api/prescription/alova/implement/herbal.ts +68 -0
- package/src/api/prescription/alova/implement/index.ts +2 -0
- package/src/api/prescription/alova/implement/prescription.ts +62 -0
- package/src/api/prescription/alova/index.ts +22 -0
- package/src/api/prescription/herbal.ts +51 -0
- package/src/api/prescription/index.ts +3 -0
- package/src/api/prescription/prescription.ts +76 -0
- package/src/api/scientist/alova/apiDefinitions.ts +27 -0
- package/src/api/scientist/alova/createApis.ts +114 -0
- package/src/api/scientist/alova/globals.d.ts +447 -0
- package/src/api/scientist/alova/implement/index.ts +1 -0
- package/src/api/scientist/alova/implement/scientist.ts +40 -0
- package/src/api/scientist/alova/index.ts +24 -0
- package/src/api/scientist/index.ts +2 -0
- package/src/api/scientist/scientist.ts +49 -0
- package/src/assets/quasar-logo-vertical.svg +15 -0
- package/src/boot/.gitkeep +0 -0
- package/src/components/ExampleComponent.vue +37 -0
- package/src/components/models.ts +8 -0
- package/src/css/app.scss +1 -0
- package/src/css/quasar.variables.scss +25 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +3 -0
- package/src/layouts/UserLayout.vue +108 -0
- package/src/pages/LoginPage.vue +29 -0
- package/src/proto/Images.proto +7 -0
- package/src/proto/WaveMap.proto +10 -0
- package/src/proto/index.ts +2 -0
- package/src/proto/types/Images_pb.ts +48 -0
- package/src/proto/types/WaveMap_pb.ts +59 -0
- package/src/router/index.ts +37 -0
- package/src/router/routes.ts +14 -0
- package/src/util/RichTextUtil.ts +5 -0
- package/src/util/datetime.ts +43 -0
- package/src/util/export.ts +46 -0
- package/src/util/helper.ts +159 -0
- package/src/util/image.ts +28 -0
- package/src/util/number.ts +146 -0
- package/src/util/s256.js +27 -0
- package/src/util/secret.ts +60 -0
- package/src/util/string.ts +121 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
function buf2hex(buffer: ArrayBuffer) {
|
|
2
|
+
// buffer is an ArrayBuffer
|
|
3
|
+
const hexArr = Array.prototype.map.call(new Uint8Array(buffer), function (bit) {
|
|
4
|
+
return ('00' + bit.toString(16)).slice(-2);
|
|
5
|
+
});
|
|
6
|
+
return hexArr.join('');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function hex2int(hex: string) {
|
|
10
|
+
const len = hex.length,
|
|
11
|
+
a = new Array(len);
|
|
12
|
+
let code;
|
|
13
|
+
for (let i = 0; i < len; i++) {
|
|
14
|
+
code = hex.charCodeAt(i);
|
|
15
|
+
if (48 <= code && code < 58) {
|
|
16
|
+
code -= 48;
|
|
17
|
+
} else {
|
|
18
|
+
code = (code & 0xdf) - 65 + 10;
|
|
19
|
+
}
|
|
20
|
+
a[i] = code;
|
|
21
|
+
}
|
|
22
|
+
return a.reduce(function (acc, c) {
|
|
23
|
+
acc = 16 * acc + c;
|
|
24
|
+
return acc;
|
|
25
|
+
}, 0);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 将数组分割成指定长度的数组
|
|
29
|
+
* @param arr 数组
|
|
30
|
+
* @param size 分割长度
|
|
31
|
+
* @returns 分割后的数组
|
|
32
|
+
*/
|
|
33
|
+
function chunk(arr: number[], size: number) {
|
|
34
|
+
const objArr = [];
|
|
35
|
+
let index = 0;
|
|
36
|
+
const objArrLen = arr.length / size;
|
|
37
|
+
for (let i = 0; i < objArrLen; i++) {
|
|
38
|
+
const arrTemp = [];
|
|
39
|
+
for (let j = 0; j < size; j++) {
|
|
40
|
+
arrTemp[j] = arr[index++];
|
|
41
|
+
if (index == arr.length) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
objArr[i] = arrTemp;
|
|
46
|
+
}
|
|
47
|
+
return objArr;
|
|
48
|
+
}
|
|
49
|
+
// 发送指令字符串转二进制
|
|
50
|
+
function stringToBytes(str: string) {
|
|
51
|
+
const array = new Uint8Array(str.length);
|
|
52
|
+
for (let i = 0, l = str.length; i < l; i++) {
|
|
53
|
+
array[i] = str.charCodeAt(i);
|
|
54
|
+
}
|
|
55
|
+
return array.buffer;
|
|
56
|
+
}
|
|
57
|
+
function numberToChinese(num: number | null): string {
|
|
58
|
+
if (num === null || isNaN(num)) return '无效数字';
|
|
59
|
+
|
|
60
|
+
// 处理负数
|
|
61
|
+
const isNegative = num < 0;
|
|
62
|
+
num = Math.abs(num);
|
|
63
|
+
|
|
64
|
+
// 处理小数部分
|
|
65
|
+
const integerPart = Math.floor(num);
|
|
66
|
+
const decimalPart = num - integerPart;
|
|
67
|
+
|
|
68
|
+
// 中文数字单位
|
|
69
|
+
const chineseNums: string[] = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
|
|
70
|
+
const chineseUnits: string[] = ['', '十', '百', '千'];
|
|
71
|
+
const chineseBigUnits: string[] = ['', '万', '亿', '兆'];
|
|
72
|
+
|
|
73
|
+
// 转换整数部分
|
|
74
|
+
let chineseInteger = '';
|
|
75
|
+
if (integerPart === 0) {
|
|
76
|
+
chineseInteger = '零';
|
|
77
|
+
} else {
|
|
78
|
+
const numStr = integerPart.toString();
|
|
79
|
+
const groupCount = Math.ceil(numStr.length / 4);
|
|
80
|
+
const groups: string[] = [];
|
|
81
|
+
|
|
82
|
+
// 将数字分成四位一组
|
|
83
|
+
for (let i = 0; i < groupCount; i++) {
|
|
84
|
+
const start = Math.max(0, numStr.length - (i + 1) * 4);
|
|
85
|
+
const end = numStr.length - i * 4;
|
|
86
|
+
groups.unshift(numStr.slice(start, end));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 处理每一组
|
|
90
|
+
for (let i = 0; i < groups.length; i++) {
|
|
91
|
+
let groupStr = '';
|
|
92
|
+
const groupNum = groups[i];
|
|
93
|
+
|
|
94
|
+
for (let j = 0; j < groupNum.length; j++) {
|
|
95
|
+
const digit = parseInt(groupNum[j]);
|
|
96
|
+
const pos = groupNum.length - 1 - j;
|
|
97
|
+
|
|
98
|
+
if (digit !== 0) {
|
|
99
|
+
groupStr += chineseNums[digit] + chineseUnits[pos];
|
|
100
|
+
} else {
|
|
101
|
+
// 处理连续的零
|
|
102
|
+
if (j < groupNum.length - 1 && groupNum[j + 1] !== '0') {
|
|
103
|
+
groupStr += chineseNums[0];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 添加大单位(万、亿等)
|
|
109
|
+
if (groupStr !== '') {
|
|
110
|
+
groupStr += chineseBigUnits[groups.length - 1 - i];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
chineseInteger += groupStr;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 处理特殊情况(如"一十"简化为"十")
|
|
117
|
+
if (chineseInteger.startsWith('一十')) {
|
|
118
|
+
chineseInteger = chineseInteger.substring(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 去除多余的零
|
|
122
|
+
chineseInteger = chineseInteger.replace(/零+/g, '零').replace(/零+$/, '');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 转换小数部分
|
|
126
|
+
let chineseDecimal = '';
|
|
127
|
+
if (decimalPart > 0) {
|
|
128
|
+
chineseDecimal = '点';
|
|
129
|
+
let decimalStr = decimalPart.toString().split('.')[1] || '';
|
|
130
|
+
|
|
131
|
+
// 限制小数位数,避免无限循环小数
|
|
132
|
+
decimalStr = decimalStr.substring(0, 10);
|
|
133
|
+
|
|
134
|
+
for (let i = 0; i < decimalStr.length; i++) {
|
|
135
|
+
const digit = parseInt(decimalStr[i]);
|
|
136
|
+
chineseDecimal += chineseNums[digit];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 组合结果
|
|
141
|
+
const result = (isNegative ? '负' : '') + chineseInteger + chineseDecimal;
|
|
142
|
+
|
|
143
|
+
return result === '零' ? '零' : result.replace(/^零+/, '');
|
|
144
|
+
}
|
|
145
|
+
//导出方法
|
|
146
|
+
export { buf2hex, hex2int, chunk, stringToBytes, numberToChinese };
|
package/src/util/s256.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// 生成随机字符串 (用于 code_verifier)
|
|
2
|
+
import CryptoJS from 'crypto-js';
|
|
3
|
+
export function generateRandomString(length) {
|
|
4
|
+
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
|
|
5
|
+
let result = '';
|
|
6
|
+
|
|
7
|
+
const randomValues = new Uint8Array(length);
|
|
8
|
+
crypto.getRandomValues(randomValues);
|
|
9
|
+
|
|
10
|
+
for (let i = 0; i < length; i++) {
|
|
11
|
+
result += charset[randomValues[i] % charset.length];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 生成 code_challenge (S256 方法)
|
|
18
|
+
export function generateCodeChallenge(codeVerifier) {
|
|
19
|
+
// 1. 计算 SHA256 哈希
|
|
20
|
+
const hash = CryptoJS.SHA256(codeVerifier);
|
|
21
|
+
|
|
22
|
+
// 2. 转换为 Base64 URL 安全编码
|
|
23
|
+
const base64 = hash.toString(CryptoJS.enc.Base64);
|
|
24
|
+
const base64Url = base64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
25
|
+
|
|
26
|
+
return base64Url;
|
|
27
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 生成OAuth PKCE所需的code_verifier和code_challenge
|
|
3
|
+
* @param method code_challenge_method,目前只支持S256
|
|
4
|
+
* @returns 包含code_verifier和code_challenge的对象
|
|
5
|
+
*/
|
|
6
|
+
export async function generatePKCECodes(method: string = 'S256') {
|
|
7
|
+
// 生成code_verifier (随机字符串)
|
|
8
|
+
const codeVerifier = generateCodeVerifier();
|
|
9
|
+
|
|
10
|
+
// 根据method生成code_challenge
|
|
11
|
+
let codeChallenge: string;
|
|
12
|
+
switch (method) {
|
|
13
|
+
case 'S256':
|
|
14
|
+
codeChallenge = await generateCodeChallengeS256(codeVerifier);
|
|
15
|
+
break;
|
|
16
|
+
default:
|
|
17
|
+
throw new Error(`Unsupported code challenge method: ${method}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
code_verifier: codeVerifier,
|
|
22
|
+
code_challenge: codeChallenge,
|
|
23
|
+
code_challenge_method: method,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 生成code_verifier
|
|
29
|
+
* 根据RFC 7636,code_verifier应该是43-128个字符的随机字符串
|
|
30
|
+
* @returns code_verifier字符串
|
|
31
|
+
*/
|
|
32
|
+
function generateCodeVerifier(): string {
|
|
33
|
+
const array = new Uint8Array(32);
|
|
34
|
+
crypto.getRandomValues(array);
|
|
35
|
+
return base64UrlEncode(array);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 使用S256方法生成code_challenge
|
|
40
|
+
* @param codeVerifier code_verifier字符串
|
|
41
|
+
* @returns code_challenge字符串
|
|
42
|
+
*/
|
|
43
|
+
async function generateCodeChallengeS256(codeVerifier: string) {
|
|
44
|
+
const encoder = new TextEncoder();
|
|
45
|
+
const data = encoder.encode(codeVerifier);
|
|
46
|
+
const hash = crypto.subtle.digest('SHA-256', data);
|
|
47
|
+
return await hash.then((buffer) => base64UrlEncode(new Uint8Array(buffer)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Base64 URL安全编码
|
|
52
|
+
* @param array 需要编码的字节数组
|
|
53
|
+
* @returns URL安全的Base64编码字符串
|
|
54
|
+
*/
|
|
55
|
+
function base64UrlEncode(array: Uint8Array): string {
|
|
56
|
+
return btoa(String.fromCharCode(...array))
|
|
57
|
+
.replace(/\+/g, '-')
|
|
58
|
+
.replace(/\//g, '_')
|
|
59
|
+
.replace(/=/g, '');
|
|
60
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import pinyin from 'pinyin';
|
|
2
|
+
export function getName(name: string): string {
|
|
3
|
+
if (!name) {
|
|
4
|
+
return ''; // 如果名字不存在或为空,返回空字符串
|
|
5
|
+
}
|
|
6
|
+
const parts = name.split(' ');
|
|
7
|
+
if (parts.length === 2) {
|
|
8
|
+
return `${parts[1]}${parts[0]}`; // 交换名字的顺序
|
|
9
|
+
} else {
|
|
10
|
+
return name; // 标红处理
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 通用拼音筛选函数
|
|
16
|
+
* @param val 搜索关键词
|
|
17
|
+
* @param options 选项数组
|
|
18
|
+
* @returns 筛选后的选项数组
|
|
19
|
+
*/
|
|
20
|
+
export function pinyinFilter(val: string, options: string[]): string[] {
|
|
21
|
+
if (!val) return [];
|
|
22
|
+
|
|
23
|
+
const lowerVal = val.toLowerCase();
|
|
24
|
+
|
|
25
|
+
return options.filter((option) => {
|
|
26
|
+
const targetStr = option;
|
|
27
|
+
if (!targetStr) return false;
|
|
28
|
+
|
|
29
|
+
// 1. 直接包含搜索词
|
|
30
|
+
if (targetStr.includes(lowerVal)) return true;
|
|
31
|
+
|
|
32
|
+
// 2. 拼音全拼包含搜索词
|
|
33
|
+
const fullPinyin = pinyin(targetStr, { style: pinyin.STYLE_NORMAL })
|
|
34
|
+
.map((word) => word[0])
|
|
35
|
+
.join('');
|
|
36
|
+
if (fullPinyin.includes(lowerVal)) return true;
|
|
37
|
+
|
|
38
|
+
// 3. 拼音首字母缩写包含搜索词
|
|
39
|
+
const abbr = getPinyinAbbr(targetStr);
|
|
40
|
+
return abbr.includes(lowerVal);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 通用拼音筛选函数(返回匹配项的索引)
|
|
46
|
+
* @param val 搜索关键词
|
|
47
|
+
* @param options 选项数组
|
|
48
|
+
* @returns 匹配项的索引数组
|
|
49
|
+
*/
|
|
50
|
+
export function pinyinFilterIndex(val: string, options: string[]): number[] {
|
|
51
|
+
if (!val) return [];
|
|
52
|
+
|
|
53
|
+
const lowerVal = val.toLowerCase();
|
|
54
|
+
|
|
55
|
+
return options
|
|
56
|
+
.map((option, index) => ({ option, index })) // 保留原始索引
|
|
57
|
+
.filter(({ option }) => {
|
|
58
|
+
const targetStr = option;
|
|
59
|
+
if (!targetStr) return false;
|
|
60
|
+
|
|
61
|
+
// 1. 直接包含搜索词
|
|
62
|
+
if (targetStr.includes(lowerVal)) return true;
|
|
63
|
+
|
|
64
|
+
// 2. 拼音全拼包含搜索词
|
|
65
|
+
const fullPinyin = pinyin(targetStr, { style: pinyin.STYLE_NORMAL })
|
|
66
|
+
.map((word) => word[0])
|
|
67
|
+
.join('');
|
|
68
|
+
if (fullPinyin.includes(lowerVal)) return true;
|
|
69
|
+
|
|
70
|
+
// 3. 拼音首字母缩写包含搜索词
|
|
71
|
+
const abbr = getPinyinAbbr(targetStr);
|
|
72
|
+
return abbr.includes(lowerVal);
|
|
73
|
+
})
|
|
74
|
+
.map(({ index }) => index); // 提取索引
|
|
75
|
+
}
|
|
76
|
+
// 获取拼音首字母缩写(单独封装以便复用)
|
|
77
|
+
function getPinyinAbbr(chinese: string): string {
|
|
78
|
+
if (!chinese) return '';
|
|
79
|
+
const pinyinArray = pinyin(chinese, {
|
|
80
|
+
style: pinyin.STYLE_NORMAL,
|
|
81
|
+
heteronym: false,
|
|
82
|
+
});
|
|
83
|
+
return pinyinArray.map((word) => word[0].charAt(0)).join('');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function parseIdNumber(idNumber: string) {
|
|
87
|
+
if (!/^\d{17}[\dXx]$/.test(idNumber)) {
|
|
88
|
+
throw new Error('无效的身份证号码');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 提取出生日期
|
|
92
|
+
const birthDateStr = idNumber.substring(6, 14);
|
|
93
|
+
const birthDate = `${birthDateStr.substring(0, 4)}/${birthDateStr.substring(4, 6)}/${birthDateStr.substring(6, 8)}`;
|
|
94
|
+
|
|
95
|
+
// 计算年龄
|
|
96
|
+
const birthYear = parseInt(birthDateStr.substring(0, 4), 10);
|
|
97
|
+
const birthMonth = parseInt(birthDateStr.substring(4, 6), 10) - 1; // 月份从0开始
|
|
98
|
+
const birthDay = parseInt(birthDateStr.substring(6, 8), 10);
|
|
99
|
+
|
|
100
|
+
const today = new Date();
|
|
101
|
+
const age =
|
|
102
|
+
today.getFullYear() -
|
|
103
|
+
birthYear -
|
|
104
|
+
(today.getMonth() < birthMonth ||
|
|
105
|
+
(today.getMonth() === birthMonth && today.getDate() < birthDay)
|
|
106
|
+
? 1
|
|
107
|
+
: 0);
|
|
108
|
+
|
|
109
|
+
// 提取性别(第17位)
|
|
110
|
+
const genderDigit = parseInt(idNumber.substring(16, 17), 10);
|
|
111
|
+
const gender = genderDigit % 2 === 1 ? '男' : '女';
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
birthDate, // 格式:YYYY-MM-DD
|
|
115
|
+
gender, // '男' 或 '女'
|
|
116
|
+
age, // 周岁年龄
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export function isNumber(value: string) {
|
|
120
|
+
return /^\d+$/.test(value);
|
|
121
|
+
}
|