RegisterForm.vue
43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
<template>
<div class="register-form">
<el-form
:model="form"
:rules="rules"
ref="regFormRef"
label-width="0"
class="register-form-content"
>
<el-form-item prop="email" required>
<el-input
v-model="form.email"
:placeholder="$t('register.emailPlaceholder')"
clearable
size="large"
>
<template #prefix>
<span class="required-asterisk">*</span>
<img src="/youxiang.svg" alt="email" class="input-icon-svg" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="password" required>
<el-input
v-model="form.password"
:placeholder="$t('register.passwordPlaceholder')"
show-password
clearable
size="large"
>
<template #prefix>
<span class="required-asterisk">*</span>
<img src="/mima.svg" alt="password" class="input-icon-svg" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="inviteCode">
<el-input
v-model="form.inviteCode"
:placeholder="$t('register.invitePlaceholder')"
:disabled="isInviteCodeFromRoute"
clearable
size="large"
>
<template #prefix>
<img
src="/yaoqingma.svg"
alt="inviteCode"
class="input-icon-svg invite-icon-aligned"
/>
</template>
</el-input>
</el-form-item>
<div class="agreement-section">
<el-checkbox v-model="form.agreeTerms" />
<div class="agreement-text">
<span>{{ $t("register.agreeTerms") }} </span>
<el-link
type="primary"
class="agreement-link"
@click="showAgreementDialog = true"
>{{ $t("register.userAgreement") }}</el-link
> <span>{{ $t("register.and") }}</span
>
<el-link
type="primary"
class="agreement-link"
@click="showPrivacyDialog = true"
>{{ $t("register.privacyPolicy") }}</el-link
>
</div>
</div>
<el-form-item class="submit-section">
<el-button
type="primary"
@click="onSubmit"
:loading="authStore.loading"
size="medium"
class="submit-btn"
>
{{ $t("register.createBtn") }}
</el-button>
</el-form-item>
<el-divider>
{{ $t("register.haveAccount")
}}<el-link
type="primary"
@click="emit('switchToLogin')"
style="color: #1e6fff"
>
{{ $t("register.signIn") }}
</el-link>
</el-divider>
</el-form>
<!-- 成功弹窗:不再跳转 go-activate -->
<el-dialog
v-model="showSuccess"
:title="$t('register.successTitle')"
width="400px"
:show-close="false"
class="success-dialog"
append-to-body
>
<div class="success-content">
<el-icon class="success-icon" :size="64" color="#67c23a">
<SuccessFilled />
</el-icon>
<p class="success-message">
{{ $t("register.verificationEmailSent") }} <b>{{ form.email }}</b
><br />
{{ $t("register.pleaseCheckEmail") }}
</p>
</div>
</el-dialog>
<!-- 用户协议弹窗 -->
<el-dialog
v-model="showAgreementDialog"
:title="$t('register.userServiceAgreement')"
width="600px"
class="agreement-dialog"
append-to-body
:close-on-click-modal="false"
>
<div class="agreement-dialog-content">
<div
class="agreement-text-content"
v-html="formattedAgreementText"
></div>
</div>
<template #footer>
<div class="agreement-dialog-footer">
<el-checkbox v-model="agreementChecked">
{{ $t("register.readAndAgree")
}}<el-link type="primary" @click.prevent style="margin: 0 4px"
>【{{ $t("register.userAgreement") }}】</el-link
>
</el-checkbox>
<el-button type="primary" @click="handleAgreementConfirm">
{{ $t("register.confirm") }}
</el-button>
</div>
</template>
</el-dialog>
<!-- 隐私政策弹窗 -->
<el-dialog
v-model="showPrivacyDialog"
:title="$t('register.privacyPolicy')"
width="600px"
class="agreement-dialog"
append-to-body
:close-on-click-modal="false"
>
<div class="agreement-dialog-content">
<div class="agreement-text-content" v-html="formattedPrivacyText"></div>
</div>
<template #footer>
<div class="agreement-dialog-footer">
<el-checkbox v-model="privacyChecked">
{{ $t("register.readAndAgree")
}}<el-link type="primary" @click.prevent style="margin: 0 4px"
>【{{ $t("register.privacyPolicy") }}】</el-link
>
</el-checkbox>
<el-button type="primary" @click="handlePrivacyConfirm">
{{ $t("register.confirm") }}
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from "vue";
import { useI18n } from "vue-i18n";
import { ElMessage, type FormInstance, type FormRules } from "element-plus";
import { SuccessFilled } from "@element-plus/icons-vue";
import { useAuthStore } from "@/stores/auth";
import { useRouter, useRoute } from "vue-router";
const { t } = useI18n();
const authStore = useAuthStore();
const router = useRouter();
const route = useRoute();
const emit = defineEmits<{
(e: "switchToLogin"): void;
}>();
const regFormRef = ref<FormInstance>();
const showSuccess = ref(false);
const showAgreementDialog = ref(false);
const agreementChecked = ref(false);
const showPrivacyDialog = ref(false);
const privacyChecked = ref(false);
const isInviteCodeFromRoute = ref(false);
// 协议内容
const agreementContent = `
更新日期:2025年11月28日
更新日期:2025年11月28日
1. 导言
欢迎您使用北京连心医疗科技有限公司(以下简称"连心医疗"或"我们")提供的"领医智能体"服务(以下简称"本服务")。
本服务是一款基于人工智能技术的医疗健康信息辅助平台,旨在通过互动问答、报告解读等形式,为您提供信息参考和辅助工具。本《用户服务协议》(以下简称"本协议")是您与连心医疗之间就注册、登录、使用本服务所订立的具有法律效力的协议。
在您开始使用本服务之前,请您(以下简称"用户"或"您")务必审慎阅读、充分理解本协议各条款内容,特别是以加粗、下划线等形式显示的关于免除或限制责任、医疗风险、法律适用和争议解决的条款。如果您不同意本协议的任何内容,或者无法准确理解该等条款的含义,请不要进行后续操作,并应立即停止访问或使用本服务。您的注册、登录、使用等行为即视为您已阅读、理解并完全接受本协议的全部内容,并同意接受其约束。
2. 服务说明
2.1 本服务依托于生成式人工智能技术,主要功能包括但不限于:医学健康知识问答、检验报告智能解读、健康资讯推送以及相关的健康管理辅助功能。
2.2 本服务的核心功能需要您注册并登录账号后方可使用。
3. 重要提示:非医疗诊断性质与风险承担
3.1 【非医疗诊断声明】
您在此明确知悉并完全理解,"领医智能体"是一款人工智能健康信息辅助工具,其所有输出内容(包括但不限于文本、分析、解读、建议等,以下简称"AI生成内容")均是基于算法和已有数据模型生成的初步、参考性信息。本服务无法也绝不旨在替代执业医师或其他合格医疗专业人员的专业判断、面对面诊断、治疗或医学建议。我们明确声明不通过本服务提供任何形式的医疗诊断、治疗方案、处方开具、预后判断或紧急医疗服务。您与"领医智能体"之间的交互不构成任何形式的医患关系。
3.2 【用户责任与风险自知】
3.2.1 鉴于医学的极端复杂性、高度专业性和个体差异性,AI生成内容可能存在不准确、不完整、过时或不适用于您个人特定健康状况的风险。您必须清醒地认识到,完全依赖此类信息可能导致误判或延误必要的诊断和治疗。
3.2.2 对于任何与您或他人健康相关的问题,尤其是出现急性、严重、持续性或令人担忧的症状时,您必须立即咨询执业医师或前往正规医疗机构就诊。您明确承诺,不会将AI生成内容作为您决定是否采取或不采取任何医疗、健康相关行动(如用药、手术、改变生活方式、中止治疗等)的唯一或决定性依据。
3.2.3 您根据AI生成内容所作出的任何判断、决策或行动,所带来的全部直接或间接后果、风险和责任,均由您自行承担。连心医疗及其关联方在此范围内免除全部责任。
3.3 【检验报告解读特别约定】
3.3.1 您知悉并同意,您主动上传的检验报告、检查单等文件包含您的个人敏感信息(属于个人健康生理信息)。上传行为即表示您授权我们为向您提供解读服务之目的,在必要范围内处理该信息。
3.3.2 我们不对报告解读的准确性、完整性和临床符合率作出任何明示或默示的担保。报告解读结果仅为对检验指标的初步分析和健康科普,旨在帮助您理解报告内容,绝不代表最终的临床诊断。所有检验报告的最终解释权必须归属于出具该报告的医疗机构及您的执业医师。
4. 账号注册与安全
4.1 您需要按照本服务的指引注册账号,并设置符合安全要求的密码。您应提供真实、准确、完整的注册信息,并及时更新。
4.2 您注册的账号仅限于您本人使用。您应对您的账号和密码的安全承担全部责任,并对通过该账号进行的所有活动(包括但不限于信息输入、内容生成等)承担全部法律责任。
4.3 如发现任何未经授权的账号使用行为,您应立即通知我们。我们有权在怀疑账号被非注册人使用时,暂停或终止向该账号提供服务。
5. 用户行为规范
您承诺在使用本服务时,严格遵守中华人民共和国相关法律法规,并保证不得实施以下行为:
5.1 输入、上传、生成、传播任何违反国家法律法规、社会主义制度、中国共产党领导、国家利益、公民合法权益、社会公共秩序、道德风尚及信息真实性等"七条底线"的内容。
5.2 输入、上传、生成、传播任何侵犯他人知识产权、名誉权、隐私权、肖像权等合法权益的内容。
5.3 输入、上传、生成、传播任何涉及淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的内容。
5.4 输入、上传、生成、传播任何含有虚假、骚扰、侮辱、诽谤、恐吓或庸俗淫秽内容的信息。
5.5 从事任何危害计算机网络安全的行为,包括但不限于:使用恶意程序、非法侵入网络、干扰网络正常功能、窃取网络数据等。
5.6 利用本服务进行任何不利于连心医疗或可能对本服务正常运行造成不合理负荷的行为。
5.7 特别禁止: 将本服务用于任何医疗急症。如遇急症,您必须立即拨打急救电话(如120)或前往最近医院的急诊科。
6. 知识产权
6.1 连心医疗是本服务相关的软件、技术、程序、网站、文字、图片、图形、音频、视频、标识、版面设计等内容的合法知识产权权利人或被许可人。未经连心医疗明确书面授权,您不得对前述内容进行复制、修改、租赁、出售、传播或创建衍生作品。
6.2 您通过本服务输入的内容("您的输入"),其相关知识产权仍归您或原始权利人所有。您授予连心医疗一项全球范围内、免费的、不可撤销的许可,允许我们为提供服务之目的(包括但不限于模型训练、服务优化)使用、处理您的输入。
6.3 由本服务基于您的输入而生成的内容("AI输出"),在您遵守本协议全部条款的前提下,我们授予您一项非独占的、有限的使用许可,您可基于个人非商业目的使用。但您不得声称对AI输出拥有绝对所有权,并不得将其用于任何非法目的或进行恶意知识产权抢注、诉讼等行为。
7. 隐私与个人信息保护
我们高度重视保护您的个人信息,尤其是健康敏感信息。关于我们如何收集、使用、存储、共享和保护您的个人信息,请您详细阅读并理解我们另行制定的《隐私政策》。该政策是本协议不可分割的一部分。
8. 服务的变更、中断与终止
8.1 为提升用户体验和服务安全性,我们有权不定期对服务进行更新、升级、调整或中断,并在可行情况下予以公告。
8.2 我们保留根据自身商业决策、法律法规要求或不可抗力等因素,随时暂停、中止或终止向您提供部分或全部服务的权利,无需事先通知您,也无需承担任何责任。
8.3 您有权通过我们提供的流程申请注销您的账号。账号注销后,我们将依据《隐私政策》处理您的个人信息。
9. 免责声明
9.1 【服务"按现状"提供】 本服务是在现有技术和条件所能达到的现状下提供的。连心医疗尽最大努力提供服务,但无法保证服务永不中断、绝对安全或完全无错误。
9.2 【第三方内容】 本服务可能包含或链接至第三方提供的信息或服务。该等内容均由第三方负责,我们不对其真实性、准确性、合法性负责,也不代表我们赞同其观点。
9.3 【不可抗力】 对于因不可抗力(如战争、地震、洪水、火灾、政府行为、网络攻击、病毒入侵等)或非我们可控原因造成的服务中断、数据丢失或其他损失,我们在法律允许的范围内免除责任。
9.4 【责任限制】 在法律允许的最大范围内,连心医疗及其关联方、董事、员工对您因使用或无法使用本服务而产生的任何间接性、后果性、惩戒性、偶然性、特殊或惩罚性的损害赔偿(包括但不限于利润损失、数据丢失、商誉损害、人身伤害或其他无形损失),不承担任何责任。我们对您的全部直接赔偿责任总额,不应超过您就使用本服务而向我们直接支付的费用(如有)。
10. 违约处理
若您违反本协议的任何规定,我们有权独立判断并根据情节轻重,采取包括但不限于以下一种或多种措施:警告、限制或停止部分功能、暂停服务、终止服务、永久关闭账号,并保留追究您法律责任的权利。
11. 协议的变更与通知
我们有权在必要时修改本协议条款。更新后的协议将在我们的官方网站(https://linkmed.cc/)或本软件内显著位置公布,并注明更新日期。若您在本协议内容变更后继续使用本服务,即表示您已充分阅读、理解并接受修订后的协议,并同意受其约束。
12. 法律适用与争议解决
12.1 本协议的订立、效力、解释、履行及争议的解决,均适用中华人民共和国法律(不包括其冲突法规则)。
12.2 因本协议引起的或与本协议有关的任何争议,双方应首先通过友好协商解决。如果协商不成,任何一方均有权将争议提交至北京连心医疗科技有限公司所在地有管辖权的人民法院通过诉讼解决。
13. 其他
13.1 本协议构成您与我们之间就本服务所达成的完整协议,并取代您与我们之间先前就本服务所达成的任何口头或书面约定。
13.2 如果本协议中的任何条款因任何原因被有管辖权的法院认定为无效或不可执行,则该条款应在不影响其他条款效力的前提下从本协议中移除,其余条款继续完全有效。
13.3 我们未行使或执行本协议项下的任何权利或规定,不构成对该权利或规定的放弃。
联系我们
如果您对本协议或本服务有任何疑问、意见或建议,请通过以下方式与我们联系:
● 公司名称: 北京连心医疗科技有限公司
● 官方网址: https://linkmed.cc/
● 联系电话: 18515088735`;
// 格式化协议文本,将换行转换为HTML
const formattedAgreementText = computed(() => {
return agreementContent
.split("\n")
.map((line) => {
// 处理空行
if (!line.trim()) {
return "<br>";
}
// 处理标题(数字开头的行)
if (/^\d+\./.test(line.trim())) {
return `<p style="font-weight: bold; margin: 12px 0 8px 0;">${line}</p>`;
}
// 处理子标题(如 2.1, 3.2.1 等)
if (/^\d+\.\d+/.test(line.trim())) {
return `<p style="font-weight: 600; margin: 10px 0 6px 0;">${line}</p>`;
}
// 处理加粗标记的内容
if (line.includes("【") && line.includes("】")) {
return `<p style="font-weight: bold; margin: 8px 0 4px 0;">${line}</p>`;
}
// 普通段落
return `<p style="margin: 6px 0; line-height: 1.6;">${line}</p>`;
})
.join("");
});
// 隐私政策内容
const privacyContent = `
更新日期:2025年11月28日
更新日期:2025年11月28日
北京连心医疗科技有限公司(以下简称"我们""连心医疗"或"LinkMed")作为https://linkmed.cc/ 网站(以下简称"本网站")的运营者,深知医疗健康领域个人信息及敏感数据对您的重要性。本隐私政策专为本网站定制,将详细说明我们在您使用本网站账号注册、登录及关联医疗服务过程中,如何收集、使用、存储、保护您的个人信息,以及您享有的信息管理权利。请您在访问、使用本网站前仔细阅读并理解本政策,您使用本网站即视为同意我们按照本政策处理您的个人信息。
一、定义与适用范围
1.1 核心定义
● LinkMed:指北京连心医疗科技有限公司运营的医疗健康服务体系,本网站(https://linkmed.cc/)是其中用于账号认证的核心入口,主要提供账号注册、登录、身份核验等基础服务,为后续使用"领医智能体"等医疗相关功能提供身份支持。
● 个人信息:指以电子或其他方式记录的,能够单独或与其他信息结合识别特定自然人身份或反映其活动情况的信息,如姓名、手机号码、身份证号、医疗执业资质信息等。
● 敏感个人信息:指一旦泄露或非法使用,易导致人身、财产安全受损或人格尊严受侵害的信息,包括身份证件号码、医疗执业证编号、生物识别信息、精准位置信息等,本网站处理的敏感信息将以下划线标注。
1.2 适用范围
本政策仅适用于本网站(https://linkmed.cc/)的所有功能及服务,包括但不限于账号注册、手机号登录、第三方授权登录、身份核验等;若您通过本网站跳转至其他关联平台(如"领医智能体"服务页面),将适用该平台对应的隐私政策,我们会在跳转时通过页面提示告知您。
二、我们如何收集和使用您的个人信息
我们仅为实现本网站的账号认证及基础服务目的,收集必要的个人信息,不超出业务必需范围额外收集;非必要信息您可自主选择是否提供,且不影响基础登录功能使用。
2.1 账号注册与登录场景
本网站作为LinkMed体系的账号认证入口,需通过信息收集完成身份核验,保障医疗服务的合规性与安全性,具体收集信息如下:
| 功能模块 | 收集的个人信息 | 收集目的 | 是否必要 |
|------------------------|--------------------------------------------------------------------------------|--------------------------------------------------------------------------|------------------------------------------|
| 手机号注册 | 手机号码、短信验证码 | 完成网络实名制认证,创建 LinkMed 账号,用于后续登录及身份核验 | 否(可以邮箱) |
| 密码设置 | 账号密码(加密存储) | 保障账号安全,防止未授权访问 | 是 |
| 医疗身份核验(如适用) | 姓名、身份证号、医师资格证 / 执业证编号(仅针对医疗从业者用户) | 验证医疗从业者身份真实性,为后续使用专业医疗服务(如病例分析、诊疗辅助)提供资质支持 | 否(仅非医疗用户可跳过,医疗从业者使用专业功能需提供) |
| 第三方授权登录(如适用) | 授权平台(如医疗机构内部系统)返回的公开信息(姓名、所属机构、账号标识) | 简化登录流程,实现跨平台身份同步 | 否(可选择其他注册登录) |
2.2 账号安全与运营场景
为保障您的账号安全、维护本网站稳定运行,我们会自动收集以下非身份标识信息,无需您主动提供:
● 设备信息:包括设备型号、操作系统版本、设备标识符(如Android ID、OAID,仅用于设备唯一性识别,不关联身份信息)、浏览器类型及版本,用于识别异常登录设备,防范账号盗用风险。
● 日志信息:包括您访问本网站的IP地址、访问时间、页面浏览记录、操作行为(如"点击注册""获取验证码"),用于排查系统故障、优化页面加载速度,及追溯违规操作(如多次输错密码)。
● 安全验证信息:包括登录时的短信验证码、操作时的弹窗确认记录,用于验证操作行为的真实性,防止账号被非法操作。
2.3 征得授权同意的例外
根据《个人信息保护法》等法律法规,以下情形中,我们收集、使用您的个人信息无需事先征得您的同意:
1. 与履行法律法规规定的义务相关的(如向监管部门报送医疗从业者资质核验信息);
2. 与公共安全、公共卫生直接相关的(如配合疫情防控核查账号关联的医疗服务记录);
3. 出于维护您或他人的生命、财产安全,但难以获得本人授权的(如账号异常时冻结操作所需的设备日志);
4. 用于维护本网站安全稳定运行所必需的(如发现并处置恶意注册、黑客攻击行为);
5. 法律法规规定的其他情形。
三、我们如何使用Cookie及同类技术
为优化您的访问体验、保障账号登录安全,本网站会使用Cookie技术,具体用途如下:
1. 身份识别与登录状态保持:通过Cookie记录您的账号登录状态(如"保持登录"选项勾选后),避免您每次访问时重复输入账号密码,Cookie仅存储加密后的登录标识,不包含明文密码。
2. 安全防护:设置安全类Cookie,识别异常登录行为(如异地登录、频繁切换设备),当检测到风险时触发二次验证(如短信验证码),保护账号安全。
3. Cookie管理:您可通过浏览器设置关闭或清除Cookie(如Chrome浏览器:设置-隐私和安全-Cookie及其他网站数据-阻止所有Cookie);但关闭Cookie后,您可能无法使用"保持登录"功能,且每次访问需重新输入账号信息,部分页面加载可能异常。
四、个人信息的共享、转移与公开
我们严格控制个人信息的对外流转,仅在以下限定场景中共享、转移或公开您的信息,且均遵循"最小必要"原则:
4.1 共享场景
● 医疗资质核验合作方:若您需验证医疗从业者身份,我们会将您提供的姓名、执业证编号共享给合法持有医疗资质数据库的合作方(如地方卫健委授权的信息核验平台),仅用于资质真实性验证,合作方不得留存或用于其他目的,我们会与合作方签署数据保密协议。
● 云服务提供商:本网站的服务器存储、数据传输依赖合规的云服务提供商(如国内具备医疗数据存储资质的服务商),我们会将您的账号信息(加密后)、日志信息委托其存储,云服务提供商仅能按照我们的指令处理数据,不得擅自使用。
4.2 转移场景
若发生企业合并、收购、资产转让等情形,您的个人信息可能随业务资产一并转移,我们会要求继受方继续履行本隐私政策的义务;若继受方变更数据处理目的,需重新征得您的书面同意。
4.3 公开场景
我们不会主动公开您的个人信息,仅在以下情形中可公开:
1. 获得您的明确同意后(如您授权本网站公开您的医疗资质信息用于行业认证);
2. 法律法规要求公开的(如配合司法机关调查,提供涉案账号的注册信息);
3. 对违规账号进行处罚公告时,仅公开账号标识(如脱敏后的手机号"138******78"),不披露完整个人信息。
五、个人信息的存储与保护
5.1 存储规则
1. 存储地点:您的个人信息均存储在中华人民共和国境内的服务器上,我们不会将其传输至境外;如需跨境传输(如涉及国际医疗合作),会事先征得您的同意,并符合国家跨境数据传输的法律法规要求。
2. 存储期限:
- 账号基础信息(手机号、加密密码):保留至您主动注销账号之日;
- 医疗资质信息(如执业证编号):保留至您的资质有效期届满或您注销账号之日;
- 设备信息、日志信息:仅保留6个月(超出期限后自动匿名化处理,无法关联身份);
- 若法律法规要求延长存储期限(如医疗监管部门要求留存资质核验记录3年),我们会按照法定期限留存,到期后立即删除或匿名化。
六、您的个人信息权利
您对自己的个人信息享有查阅、更正、删除、撤回授权、注销账号等权利,具体操作方式如下:
1. 查阅与更正:登录本网站后,进入"账号中心"(或通过本网站跳转至LinkMed主平台"我的-个人信息"),可查阅您的手机号、医疗资质信息;如需更正(如手机号变更、资质更新),可提交修改申请并上传证明材料,我们会在1个工作日内审核处理。
2. 删除信息:您可申请删除非必要信息(如已过期的医疗资质扫描件),通过本网站"联系客服"通道提交申请,我们会在3个工作日内完成删除并告知您结果;账号基础信息(手机号)仅在注销账号后可删除。
3. 撤回授权:若您曾授权我们共享信息给第三方(如医疗资质核验平台),可通过"账号中心-授权管理"撤回授权,撤回后我们会立即通知第三方停止使用您的信息。
4. 账号注销:您可通过本网站"账号中心-安全设置-注销账号"提交申请,注销前需完成身份验证(如短信验证码+身份证号核验);账号注销后,我们会在15个工作日内删除您的所有个人信息(法律法规要求留存的除外),且注销不可逆。
5. 投诉与反馈:若您认为您的个人信息权利受到侵害,可通过以下方式联系我们:
- 电话:18515088735(工作日9:00-18:00);
- 邮寄公司:北京连心医疗科技有限公司(运营部收)。
我们会在15个工作日内核实并回复您的投诉或反馈。
七、未成年人保护
本网站及LinkMed相关服务主要面向成年人及医疗从业者,不主动向未满18周岁的未成年人提供服务。若未成年人需使用本网站(如实习医疗人员),需在监护人的指导下注册账号,并由监护人同意本隐私政策;监护人可联系我们查询、更正或删除未成年人的个人信息,我们会积极配合提供必要支持。
八、隐私政策的修订与通知
1. 若法律法规更新、本网站功能调整(如新增第三方登录方式),或我们的信息处理规则发生变更,我们会修订本隐私政策,并在本网站首页显著位置公示修订后的版本,同时通过短信或账号站内信告知您修订内容;修订后的政策自公示之日起7日后生效,若您在生效后继续使用本网站,视为同意修订后的政策。
2. 重大修订(如扩大个人信息收集范围、变更共享规则)会通过本网站弹窗强制提示您阅读,您需确认同意后才可继续使用服务。
九、联系我们
若您对本隐私政策有任何疑问,或需要协助管理个人信息,可通过以下方式联系我们:
● 官方网址:https://linkmed.cc/(可通过"帮助中心"提交问题);
● 联系电话:18515088735(工作日9:00-18:00);
我们会在15个工作日内对您的咨询、反馈给予正式回复。`;
// 将 markdown 表格转换为 HTML 表格
const parseMarkdownTable = (text: string): string => {
const lines = text.split("\n");
let result: string[] = [];
let inTable = false;
let tableRows: string[][] = [];
let tableHeaders: string[] = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i]?.trim() || "";
// 检测表格开始(包含 | 的行)
if (line.startsWith("|") && line.endsWith("|")) {
if (!inTable) {
inTable = true;
tableRows = [];
}
// 跳过分隔行(如 |---|---|)
if (/^\|[\s\-:]+\|/.test(line)) {
continue;
}
// 解析表格行
const cells = line
.split("|")
.map((cell) => cell.trim())
.filter((cell) => cell.length > 0);
if (cells.length > 0) {
if (tableHeaders.length === 0) {
tableHeaders = cells;
} else {
tableRows.push(cells);
}
}
} else {
// 表格结束,生成 HTML 表格
if (inTable && tableHeaders.length > 0) {
let tableHtml = '<table class="privacy-table">';
// 表头
tableHtml += "<thead><tr>";
tableHeaders.forEach((header) => {
tableHtml += `<th>${header}</th>`;
});
tableHtml += "</tr></thead>";
// 表体
tableHtml += "<tbody>";
tableRows.forEach((row: string[]) => {
tableHtml += "<tr>";
row.forEach((cell: string) => {
tableHtml += `<td>${cell || ""}</td>`;
});
tableHtml += "</tr>";
});
tableHtml += "</tbody></table>";
result.push(tableHtml);
inTable = false;
tableHeaders = [];
tableRows = [];
}
// 处理非表格行
if (line.trim()) {
result.push(line);
} else {
result.push("");
}
}
}
// 处理文件末尾的表格
if (inTable && tableHeaders.length > 0) {
let tableHtml = '<table class="privacy-table">';
tableHtml += "<thead><tr>";
tableHeaders.forEach((header) => {
tableHtml += `<th>${header}</th>`;
});
tableHtml += "</tr></thead>";
tableHtml += "<tbody>";
tableRows.forEach((row: string[]) => {
tableHtml += "<tr>";
row.forEach((cell: string) => {
tableHtml += `<td>${cell || ""}</td>`;
});
tableHtml += "</tr>";
});
tableHtml += "</tbody></table>";
result.push(tableHtml);
}
return result.join("\n");
};
// 格式化隐私政策文本,将换行转换为HTML,并处理表格
const formattedPrivacyText = computed(() => {
// 先解析表格
const textWithTables = parseMarkdownTable(privacyContent);
return textWithTables
.split("\n")
.map((line) => {
// 如果已经是表格 HTML,直接返回
if (line.includes("<table")) {
return line;
}
// 处理空行
if (!line.trim()) {
return "<br>";
}
// 处理标题(中文数字开头的行,如一、二、等)
if (/^[一二三四五六七八九十]+、/.test(line.trim())) {
return `<p style="font-weight: bold; margin: 16px 0 10px 0; font-size: 16px;">${line}</p>`;
}
// 处理子标题(如 1.1, 2.1 等)
if (/^\d+\.\d+/.test(line.trim())) {
return `<p style="font-weight: 600; margin: 12px 0 8px 0;">${line}</p>`;
}
// 处理列表项(以 ● 开头)
if (line.trim().startsWith("●")) {
return `<p style="margin: 6px 0; line-height: 1.6; padding-left: 20px;">${line}</p>`;
}
// 处理加粗标记的内容
if (line.includes("【") && line.includes("】")) {
return `<p style="font-weight: bold; margin: 8px 0 4px 0;">${line}</p>`;
}
// 普通段落
return `<p style="margin: 6px 0; line-height: 1.6;">${line}</p>`;
})
.join("");
});
// 处理协议确认
const handleAgreementConfirm = () => {
if (agreementChecked.value) {
form.agreeTerms = true;
showAgreementDialog.value = false;
// watch 会自动将 agreementChecked 和 privacyChecked 同步为 true
} else {
ElMessage.warning(t("register.pleaseReadAndAgreeAgreement"));
}
};
// 处理隐私政策确认
const handlePrivacyConfirm = () => {
if (privacyChecked.value) {
form.agreeTerms = true;
showPrivacyDialog.value = false;
// watch 会自动将 agreementChecked 和 privacyChecked 同步为 true
} else {
ElMessage.warning(t("register.pleaseReadAndAgreePrivacy"));
}
};
// 加载协议内容
onMounted(async () => {
// 从路由查询参数中获取邀请码
const refCode = route.query.ref as string;
if (refCode) {
form.inviteCode = refCode;
isInviteCodeFromRoute.value = true;
}
});
const form = reactive({
email: "",
password: "",
inviteCode: "",
agreeTerms: false,
});
// 监听 form.agreeTerms 的变化,同步更新 agreementChecked 和 privacyChecked
watch(
() => form.agreeTerms,
(newValue) => {
agreementChecked.value = newValue;
privacyChecked.value = newValue;
},
);
const rules = computed<FormRules>(() => ({
email: [
{ required: true, message: "请输入邮箱", trigger: "blur" },
{
validator: (_rule: any, value: string, callback: any) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (value && !emailRegex.test(value)) {
callback(new Error("邮箱格式不正确"));
} else {
callback();
}
},
trigger: ["blur", "change"],
},
],
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{ min: 6, max: 20, message: "密码长度至少6位,最多20位", trigger: "blur" },
],
}));
const onSubmit = async () => {
// 表单校验:校验不通过则不发送接口请求
if (!regFormRef.value) {
return;
}
try {
// 执行表单校验
await regFormRef.value.validate();
} catch (error: any) {
// 校验失败,提取第一个错误信息并提示用户
let errorMessage = "请检查表单输入";
if (error && typeof error === "object" && error.fields) {
// Element Plus 校验失败时,error.fields 包含所有字段的错误信息
const fields = error.fields;
const fieldKeys = Object.keys(fields);
// 获取第一个有错误的字段
for (const fieldKey of fieldKeys) {
const fieldErrors = fields[fieldKey];
if (Array.isArray(fieldErrors) && fieldErrors.length > 0) {
const firstError = fieldErrors[0];
if (firstError && firstError.message) {
errorMessage = firstError.message;
break;
}
}
}
}
ElMessage.error(errorMessage);
// 校验失败,直接返回,不发送请求
return;
}
// 检查是否同意协议
if (!form.agreeTerms) {
ElMessage.error("请阅读并同意用户协议和隐私政策");
return;
}
// 所有校验通过后,才发送注册请求
try {
const result = await authStore.register({
email: form.email,
password: form.password,
realName: form.email,
inviteCode: form.inviteCode || undefined,
});
if (result.success) {
ElMessage.success(
t("goActivateV2.sendSuccess_link") ||
"激活邮件已发送,请前往邮箱点击链接完成激活!",
);
emit("switchToLogin");
} else {
ElMessage.error(t("register.registerFailed"));
}
} catch (error) {
console.error("注册处理错误:", error);
ElMessage.error(t("register.registerFailed"));
}
};
</script>
<style scoped lang="scss">
.register-form {
width: 100%;
max-width: 400px;
margin: 0 auto;
padding: 0;
:deep(.el-input__prefix) {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
.required-asterisk {
color: #f56c6c;
font-size: 14px;
font-weight: bold;
margin-left: 10px;
margin-right: 2px;
line-height: 1;
}
.input-icon-svg {
margin-right: 6px;
display: block;
}
.invite-icon-aligned {
margin-left: 18px;
}
}
:deep(.el-input--large .el-input__wrapper) {
padding: 2px 15px 2px 2px !important;
}
:deep(.el-input.is-focus .el-input__prefix .input-icon-svg) {
opacity: 1;
}
}
.register-form-content {
background: transparent;
border-radius: 16px;
padding: 0;
}
.el-form-item {
margin-bottom: 20px;
}
.el-input {
border-radius: 12px;
:deep(.el-input__inner) {
height: 38px;
font-size: 16px;
border-radius: 12px;
background: #ffffff !important;
color: #606266 !important;
transition: all 0.3s ease;
&::placeholder {
color: #c0c4cc;
font-size: 16px;
}
}
:deep(.el-input__prefix) {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
.required-asterisk {
color: #f56c6c;
font-size: 14px;
font-weight: bold;
margin-left: 10px;
margin-right: 2px;
line-height: 1;
}
.input-icon-svg {
margin-right: 6px;
display: block;
}
}
:deep(.el-input.is-focus .el-input__prefix .input-icon-svg) {
opacity: 1;
}
:deep(.el-input__suffix) {
color: #909399;
}
}
.input-icon-svg {
width: 18px;
height: 18px;
object-fit: contain;
flex-shrink: 0;
opacity: 0.6;
transition: opacity 0.3s ease;
vertical-align: middle;
}
.agreement-section {
margin: 8px 0;
display: flex;
justify-content: flex-start;
align-items: flex-start;
gap: 8px;
margin-bottom: 16px;
}
.agreement-section :deep(.el-checkbox) {
display: flex;
align-items: flex-start;
line-height: 1.5;
margin-top: 0;
}
.agreement-section :deep(.el-checkbox__input) {
margin-top: 2px;
flex-shrink: 0;
}
.agreement-section :deep(.el-checkbox__label) {
display: none;
}
.agreement-text {
font-size: 14px;
color: #606266;
display: flex;
flex-wrap: wrap;
align-items: center;
line-height: 1.5;
flex: 1;
margin-top: 0;
}
.agreement-link {
font-size: 14px;
text-decoration: none;
}
.submit-section {
margin-top: 10px;
margin-bottom: 24px;
}
.submit-btn {
width: 100%;
height: 44px;
font-size: 16px;
font-weight: 500;
border-radius: 12px;
background: linear-gradient(135deg, #3399ff 0%, #00c9ff 100%);
border: none;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(51, 153, 255, 0.3);
&:hover {
box-shadow: 0 4px 12px rgba(51, 153, 255, 0.4);
}
&:active {
box-shadow: 0 1px 4px rgba(51, 153, 255, 0.3);
}
}
.success-dialog {
:deep(.el-dialog) {
border-radius: 16px;
background: var(--color-card-bg);
color: var(--color-text);
}
}
.success-content {
text-align: center;
padding: 20px 0;
}
.success-icon {
font-size: 64px;
color: #67c23a;
margin-bottom: 16px;
}
.success-message {
font-size: 16px;
color: var(--color-text);
line-height: 1.6;
margin: 0;
}
/* 用户协议弹窗样式 */
.agreement-dialog {
:deep(.el-dialog) {
border-radius: 16px;
background: #ffffff;
color: #606266;
display: flex;
flex-direction: column;
height: 600px;
max-height: 80vh;
margin: 0 auto;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
:deep(.el-dialog__wrapper) {
display: flex;
align-items: center;
justify-content: center;
}
:deep(.el-dialog__header) {
padding: 20px 24px;
border-bottom: 1px solid #ebeef5;
flex-shrink: 0;
}
:deep(.el-dialog__title) {
font-size: 18px;
font-weight: 600;
color: #303133;
}
:deep(.el-dialog__body) {
padding: 20px 24px;
overflow-y: auto;
flex: 1;
min-height: 0;
}
:deep(.el-dialog__footer) {
padding: 16px 24px;
border-top: 1px solid #ebeef5;
flex-shrink: 0;
}
}
.agreement-dialog-content {
max-height: 500px;
overflow-y: auto;
padding-right: 8px;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: #f5f5f5;
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: #c0c4cc;
border-radius: 3px;
&:hover {
background: #a0a4aa;
}
}
}
.agreement-text-content {
font-size: 14px;
line-height: 1.8;
color: #606266;
:deep(p) {
margin: 6px 0;
line-height: 1.8;
}
:deep(p:first-child) {
margin-top: 0;
}
}
.agreement-dialog-footer {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
:deep(.el-checkbox) {
flex: 1;
}
:deep(.el-button) {
margin-left: 16px;
}
}
/* 隐私政策表格样式 */
:deep(.privacy-table) {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 14px;
border: 1px solid #ebeef5;
thead {
background-color: #f5f7fa;
th {
padding: 12px 16px;
text-align: left;
font-weight: 600;
color: #303133;
border-bottom: 2px solid #ebeef5;
border-right: 1px solid #ebeef5;
&:last-child {
border-right: none;
}
}
}
tbody {
tr {
border-bottom: 1px solid #ebeef5;
&:hover {
background-color: #fafafa;
}
&:last-child {
border-bottom: none;
}
}
td {
padding: 12px 16px;
color: #606266;
line-height: 1.6;
border-right: 1px solid #ebeef5;
vertical-align: top;
&:last-child {
border-right: none;
}
}
}
}
/* 响应式设计 */
@media (max-width: 540px) {
.register-form {
:deep(.el-input__inner) {
height: 52px !important;
font-size: 17px !important;
&::placeholder {
font-size: 16px;
}
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
}
.submit-btn {
height: 54px !important;
font-size: 19px !important;
margin-top: 12px;
}
.agreement-text {
font-size: 14px;
line-height: 1.6;
}
.agreement-link {
font-size: 14px;
}
:deep(.el-divider__text) {
font-size: 16px;
}
}
@media (max-width: 320px) {
.register-form {
padding: 16px;
}
.register-form-content {
padding: 24px;
}
.el-input {
:deep(.el-input__inner) {
height: 44px;
font-size: 15px;
padding-left: 50px;
}
}
.submit-btn {
height: 44px;
font-size: 15px;
}
}
</style>