各页面内容已丰富,等待后续继续增加、精修。

This commit is contained in:
william.wan 2026-02-20 20:48:55 +08:00
parent 59867e4113
commit ea21c579c8
96 changed files with 12011 additions and 1088 deletions

View File

@ -24,7 +24,7 @@
### 1.3 目标用户 ### 1.3 目标用户
| 角色 | 名称 | 对应岗位 | | 角色 | 名称 | 对应岗位 |
|-----------|-----------------|-----------------------------------| |-----------|-----------------|-----------------------------------|
| 投保人 | Policyholder | 申办者采购、项目经理、上市后质量团队 | | 投保人 | PolicyHolder | 申办者采购、项目经理、上市后质量团队 |
| 保险人 | Insurer | 保司核保人员 | | 保险人 | Insurer | 保司核保人员 |
| 服务方 | TPA | 临研安/华泰经纪 | | 服务方 | TPA | 临研安/华泰经纪 |
@ -107,9 +107,9 @@
│ │ ├── 专家经纪 │ │ ├── 专家经纪
│ │ └── 第三方机构 │ │ └── 第三方机构
│ └── 风险职责 │ └── 风险职责
│ ├── 风险职责总览(/concern │ ├── 风险职责总览(/RMO-About-Responsibility
│ ├── 申办者职责(/sponsor │ ├── 申办者职责(/sponsor
│ ├── 持有人职责(/holder │ ├── 持有人职责(/Holder
│ ├── 研究中心(/institution │ ├── 研究中心(/institution
│ ├── 参与者(/participant │ ├── 参与者(/participant
│ └── CXO职责/service-provider │ └── CXO职责/service-provider
@ -312,9 +312,9 @@ flowchart TB
#### 2.3.3 风险职责 #### 2.3.3 风险职责
**路由结构:** **路由结构:**
- `/concern`风险职责总览RiskDutiesOverview - `/RMO-About-Responsibility`风险职责总览RiskDutiesOverview
- `/sponsor`:申办者职责 - `/sponsor`:申办者职责
- `/holder`:持有人职责 - `/Holder`:持有人职责
- `/participant`:受试者专区 - `/participant`:受试者专区
- `/institution`:研究中心 - `/institution`:研究中心
- `/service-provider`CXO职责 - `/service-provider`CXO职责
@ -363,7 +363,7 @@ flowchart TB
#### 2.3.7 登录页面 #### 2.3.7 登录页面
- **路由**`/login` - **路由**`/login`
- **功能**:用户名/邮箱 + 密码登录表单测试账号admin、policyholder、insurer、tpa密码123456 - **功能**:用户名/邮箱 + 密码登录表单测试账号admin、policyHolder、insurer、tpa密码123456
- **登录逻辑**:登录成功后跳转到工作台(`/dashboard`);支持 `from` 查询参数回跳 - **登录逻辑**:登录成功后跳转到工作台(`/dashboard`);支持 `from` 查询参数回跳
- **说明**:用户由管理员创建,无自助注册;记住我、忘记密码为 UI 占位 - **说明**:用户由管理员创建,无自助注册;记住我、忘记密码为 UI 占位

View File

@ -735,11 +735,11 @@ const pagerConfig = {
label-width="120px" label-width="120px"
> >
<el-form-item label="名称" prop="name"> <el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" /> <el-input v-model="formData.name" placeHolder="请输入名称" />
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-select v-model="formData.status" placeholder="请选择状态"> <el-select v-model="formData.status" placeHolder="请选择状态">
<el-option label="启用" value="enabled" /> <el-option label="启用" value="enabled" />
<el-option label="禁用" value="disabled" /> <el-option label="禁用" value="disabled" />
</el-select> </el-select>
@ -758,7 +758,7 @@ const pagerConfig = {
<el-form-item label="严重程度" prop="severity"> <el-form-item label="严重程度" prop="severity">
<el-select <el-select
v-model="formData.severity" v-model="formData.severity"
placeholder="请选择或输入严重程度" placeHolder="请选择或输入严重程度"
style="width: 100%" style="width: 100%"
filterable filterable
> >
@ -770,7 +770,7 @@ const pagerConfig = {
<!-- ❌ 错误:不支持搜索的下拉框 --> <!-- ❌ 错误:不支持搜索的下拉框 -->
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-select v-model="formData.status" placeholder="请选择状态"> <el-select v-model="formData.status" placeHolder="请选择状态">
<el-option label="启用" value="enabled" /> <el-option label="启用" value="enabled" />
<el-option label="禁用" value="disabled" /> <el-option label="禁用" value="disabled" />
</el-select> </el-select>
@ -786,7 +786,7 @@ const pagerConfig = {
<el-form-item label="严重程度" prop="severity"> <el-form-item label="严重程度" prop="severity">
<el-select <el-select
v-model="formData.severity" v-model="formData.severity"
placeholder="请选择或输入严重程度" placeHolder="请选择或输入严重程度"
style="width: 100%" style="width: 100%"
filterable filterable
allow-create allow-create
@ -844,10 +844,10 @@ const pagerConfig = {
</el-select> </el-select>
``` ```
**Placeholder 规范** **PlaceHolder 规范**
- **仅搜索**`placeholder="请选择或搜索XXX"` - **仅搜索**`placeHolder="请选择或搜索XXX"`
- **搜索+输入**`placeholder="请选择或输入XXX"` - **搜索+输入**`placeHolder="请选择或输入XXX"`
**注意事项** **注意事项**
@ -2648,7 +2648,7 @@ export function getTaskPage(params) {
<el-date-picker <el-date-picker
v-model="formData.dueDate" v-model="formData.dueDate"
type="datetime" type="datetime"
placeholder="选择截止时间" placeHolder="选择截止时间"
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
/> />
@ -2657,7 +2657,7 @@ export function getTaskPage(params) {
<el-date-picker <el-date-picker
v-model="formData.startDate" v-model="formData.startDate"
type="date" type="date"
placeholder="选择开始日期" placeHolder="选择开始日期"
format="YYYY-MM-DD" format="YYYY-MM-DD"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
/> />

View File

@ -8,7 +8,7 @@
# 角色 # 角色
| 角色 | 名称 | 对应岗位 | | 角色 | 名称 | 对应岗位 |
|----------|-----------------|----------------------------------| |----------|-----------------|----------------------------------|
| 投保人 | Policyholder | - 申办者采购、项目经理<br>- 上市后质量团队 | | 投保人 | PolicyHolder | - 申办者采购、项目经理<br>- 上市后质量团队 |
| 保险人 | Insurer | - 保司核保人员 | | 保险人 | Insurer | - 保司核保人员 |
| 服务方 | TPA | - 临研安/华泰经纪 | | 服务方 | TPA | - 临研安/华泰经纪 |

View File

@ -119,9 +119,9 @@ App.tsx
| 路径 | 组件 | 说明 | | 路径 | 组件 | 说明 |
|------|------|------| |------|------|------|
| `/` | Home | 首页 | | `/` | Home | 首页 |
| `/concern` | RiskDutiesOverview | 风险职责总览 | | `/RMO-About-Responsibility` | RiskDutiesOverview | 风险职责总览 |
| `/sponsor` | Sponsor | 申办者职责 | | `/sponsor` | Sponsor | 申办者职责 |
| `/holder` | Holder | 持有人职责 | | `/Holder` | Holder | 持有人职责 |
| `/institution` | Institution | 研究中心 | | `/institution` | Institution | 研究中心 |
| `/service-provider` | ServiceProvider | CXO 职责 | | `/service-provider` | ServiceProvider | CXO 职责 |
| `/participant` | Participant | 受试者专区 | | `/participant` | Participant | 受试者专区 |
@ -194,7 +194,7 @@ App.tsx
- **海外风险**(一级) - **海外风险**(一级)
- **资源中心**(一级,二级下拉):实践指南、培训材料、常见问题 - **资源中心**(一级,二级下拉):实践指南、培训材料、常见问题
- 登录/用户信息(根据登录状态切换) - 登录/用户信息(根据登录状态切换)
- **交互**:下拉菜单由本地 state`concernOpen`、`riskDataOpen`、`riskActivitiesOpen`、`resourceOpen`控制hover 展开/收起;风险活动下“临床试验”为分组标题,其下为三级子项;当前路由高亮通过 `useLocation` 与 path 匹配实现。 - **交互**:下拉菜单由本地 state`RMO-About-ResponsibilityOpen`、`riskDataOpen`、`riskActivitiesOpen`、`resourceOpen`控制hover 展开/收起;风险活动下“临床试验”为分组标题,其下为三级子项;当前路由高亮通过 `useLocation` 与 path 匹配实现。
- **登录后**:已登录用户点击 Logo 或"工作台"可进入 `/dashboard`Header 显示用户角色、姓名,提供退出登录入口。 - **登录后**:已登录用户点击 Logo 或"工作台"可进入 `/dashboard`Header 显示用户角色、姓名,提供退出登录入口。
### 4.3 Footer ### 4.3 Footer

View File

@ -192,8 +192,8 @@ yarn preview
## 联系方式 ## 联系方式
如有问题或建议,请联系: 如有问题或建议,请联系:
- 邮箱:info@rmo.com - 邮箱:rmo@vdano.com
- 电话400-XXX-XXXX - 电话4009-606-520
--- ---

View File

@ -5,7 +5,7 @@ Risks relating to quality, timeliness, safety, or budgets can affect the overall
Managing risk involves identification, assessment, planning, monitoring, and reacting to threats to the safe and efficacious desired outcomes of clinical trials and is a practice that is entirely necessary to maintain a standard of care and safety to the medical and patient communities. Managing risk involves identification, assessment, planning, monitoring, and reacting to threats to the safe and efficacious desired outcomes of clinical trials and is a practice that is entirely necessary to maintain a standard of care and safety to the medical and patient communities.
As clinical trials become more complex, new risks emerge, and the essential nature of risk management is emphasized and put to the test. Every stakeholder in trials benefits from good risk management, from those interested in an ROI to those looking to further the science, and of course, those in need of urgent new treatments. As clinical trials become more complex, new risks emerge, and the essential nature of risk management is emphasized and put to the test. Every stakeHolder in trials benefits from good risk management, from those interested in an ROI to those looking to further the science, and of course, those in need of urgent new treatments.
Risk assessment, therefore, means two, intimately-related things: it is both the program and its goal. A robust risk assessment program underlies effective management of risk, and it does this by comprising a series of activities or processes that follow the entire lifecycle of the product or treatment and striving to identify and remove or avoid any factor or process that threatens its quality. Risk assessment, therefore, means two, intimately-related things: it is both the program and its goal. A robust risk assessment program underlies effective management of risk, and it does this by comprising a series of activities or processes that follow the entire lifecycle of the product or treatment and striving to identify and remove or avoid any factor or process that threatens its quality.
@ -34,7 +34,7 @@ Compliance E.g., minimizing missing or incomplete ICF files, inadequate moni
Budget Increases in clinical trial duration or sites budgets could be an objective here. Budget Increases in clinical trial duration or sites budgets could be an objective here.
This forms a foundation for the identification of risks under each category, and how these risks might affect the success of each objective. This stage could represent a section of its own, as it involves a thorough assessment and identification procedure, in which internal and external processes, people, systems, and technologies are all examined for their impact on the risk of the study. This forms a foundation for the identification of risks under each category, and how these risks might affect the success of each objective. This stage could represent a section of its own, as it involves a thorough assessment and identification procedure, in which internal and external processes, people, systems, and technologies are all examined for their impact on the risk of the study.
However, these details will be specific to each case, so a summary will suffice here. Clear identification of these risks takes time and should involve multiple different stakeholders depending on the context but the objective should be the same: find and categorize each risk that may jeopardize the ideal outcome of the objectives you have set. However, these details will be specific to each case, so a summary will suffice here. Clear identification of these risks takes time and should involve multiple different stakeHolders depending on the context but the objective should be the same: find and categorize each risk that may jeopardize the ideal outcome of the objectives you have set.
2 Conduct a Root Cause Analysis (RCA) 2 Conduct a Root Cause Analysis (RCA)
The next step is a deeper assessment of the events and risks that youve identified, and a discussion about the tolerance limits. RCA is a critical component of risk assessment as it is a process of making everything more efficient. Starting by finding the cause of each risk, youll then be able to work out how to address it and whether or how critically it really needs to be addressed. The next step is a deeper assessment of the events and risks that youve identified, and a discussion about the tolerance limits. RCA is a critical component of risk assessment as it is a process of making everything more efficient. Starting by finding the cause of each risk, youll then be able to work out how to address it and whether or how critically it really needs to be addressed.
@ -79,9 +79,9 @@ As risk management is a diverse practice that needs to be tailored specifically
In 2016, ICH published a document that represents a new formal standard of risk-based approaches to clinical trials. In the Quality management section of this revision, the document covers the concept of risk-based thinking and divides this into two fundamental aspects: In 2016, ICH published a document that represents a new formal standard of risk-based approaches to clinical trials. In the Quality management section of this revision, the document covers the concept of risk-based thinking and divides this into two fundamental aspects:
Define what is critical to success Focus on what matters. In the development stages of the trial protocol, and at the “Identify and Define” stage of the management plan, its important to clearly identify specific data and processes that are critical to human protection and the reliability of the results of the trial. This is a matter of how to think in a way that prioritizes the desired outcome of risk management, rather than the process. Your risk management plan is about the return on the significant investment into clinical trials from every stakeholder, whether its money, time, or therapeutic treatment. A studys risk management helps bring these returns to life, resulting in a safe, effective, and powerful set of data that covers the needs of everyone involved. Define what is critical to success Focus on what matters. In the development stages of the trial protocol, and at the “Identify and Define” stage of the management plan, its important to clearly identify specific data and processes that are critical to human protection and the reliability of the results of the trial. This is a matter of how to think in a way that prioritizes the desired outcome of risk management, rather than the process. Your risk management plan is about the return on the significant investment into clinical trials from every stakeHolder, whether its money, time, or therapeutic treatment. A studys risk management helps bring these returns to life, resulting in a safe, effective, and powerful set of data that covers the needs of everyone involved.
Manage the critical elements of a clinical trial This relates to the ongoing and dynamic nature of the risks involved in clinical trials. During the whole lifecycle of the study, its important to maintain an effort of identifying, evaluating, controlling, communicating, reviewing, and reporting on risks. Manage the critical elements of a clinical trial This relates to the ongoing and dynamic nature of the risks involved in clinical trials. During the whole lifecycle of the study, its important to maintain an effort of identifying, evaluating, controlling, communicating, reviewing, and reporting on risks.
Having everyone involved on the same page is a key component to successful risk management. Breaking down the thinking around risk into these two categories opens up the way for all stakeholders to take part in the following suggested best practices: Having everyone involved on the same page is a key component to successful risk management. Breaking down the thinking around risk into these two categories opens up the way for all stakeHolders to take part in the following suggested best practices:
Provide adequate support to clinical trial teams Consider assigning a Risk Manager as a champion of the risk management process. They can be responsible for guiding thought processes and controlling appropriate documentation. Provide adequate support to clinical trial teams Consider assigning a Risk Manager as a champion of the risk management process. They can be responsible for guiding thought processes and controlling appropriate documentation.
Start early Since risk presents itself at every stage of the trial, including protocol design, risk management should precede it. Start early Since risk presents itself at every stage of the trial, including protocol design, risk management should precede it.
@ -92,6 +92,6 @@ Integrate multiple data sources when rereviewing risk registers to drive the rig
Communicate regularly internally and externally Ensure real-time access to the risk register is available to reduce latency and improve communications access to the most current information. Communicate regularly internally and externally Ensure real-time access to the risk register is available to reduce latency and improve communications access to the most current information.
Apply lessons learned Adapt in response to root cause analyses in the form of lessons-learned activities. Apply lessons learned Adapt in response to root cause analyses in the form of lessons-learned activities.
Conclusion Conclusion
Risk assessment is a systematic process of identifying, analyzing, and responding to events or processes that jeopardize a trials objectives. Risks come in many shapes and forms, and from all directions, and it is in the effective management of these risks that trial design and execution is able to run with the best possible outcomes for all stakeholders. Risk assessment is a systematic process of identifying, analyzing, and responding to events or processes that jeopardize a trials objectives. Risks come in many shapes and forms, and from all directions, and it is in the effective management of these risks that trial design and execution is able to run with the best possible outcomes for all stakeHolders.
Start early, and break the process into stages of identification, root cause analysis, likelihood and impact evaluation, and tolerance thresholds. From there, follow some best practices and maintain critical thinking while applying these principles throughout the study. Start early, and break the process into stages of identification, root cause analysis, likelihood and impact evaluation, and tolerance thresholds. From there, follow some best practices and maintain critical thinking while applying these principles throughout the study.

View File

@ -0,0 +1,59 @@
# 申办者职责总结
基于资源中心/关于RMO介绍/风险职责/申办者职责相关内容的整理。
---
## 一、安全性数据管理
申办者应当在药物临床试验期间进行**持续的安全性评估**,并按照要求和时限进行报告:
- **即时分析评估**:收到任何来源的安全性相关信息后,均应当立即分析评估,包括严重性、与试验药物的相关性以及是否为预期事件
- **快速报告**将SUSAR可疑且非预期严重不良反应、其他潜在的严重安全性风险信息快速报告国家药品监督管理局药品审评中心向主要研究者和伦理委员会递交的方式应与需采取措施的紧迫性相称
- **研究者手册**:制定研究者手册并及时更新,向主要研究者和伦理委员会提供试验方案和最新的研究者手册
- **DSUR报告**:药物研发期间安全性更新报告应当包括临床试验风险与获益评估,并通报主要研究者、伦理委员会和药监部门
- **数据治理**履行数据治理责任确保数据的可靠性、可追溯性和安全性建立、实施和及时更新质量保证与质量控制相关的书面SOP
**风险类型**AE不良事件、ADR药物不良反应、SADR、SUSAR、与试验相关非医疗一切风险
---
## 二、知情同意书撰写
申办者应确保知情同意书等材料符合法规要求,保障受试者充分知情:
- **伦理审查**:向伦理委员会提交知情同意书等文件供审查
- **补偿说明**:知情同意书应说明补偿方式、数额和计划;伦理委员会应确保知情同意书不含免除申办者、研究者、机构责任的内容
- **标本与数据**:剩余标本的继续保存、数据保密性、共享条件等,应在知情同意书中明确说明
- **风险提示**采用通俗易懂的语言确保受试者易于理解可借助RMO的**知情同意书审阅、修改、建议**服务,确保风险提示充分、内容合规
- **新信息告知**:获知可能影响受试者继续参加试验的新信息时,及时告知受试者或法定代理人,必要时再次签署知情同意书
---
## 三、风险管理体系建立与运维
申办者应建立并持续运维基于风险的**质量管理与风险管理体系**
- **风险识别与评估**:在试验开始前和整个试验过程中识别可能对关键质量因素产生有意义影响的风险,并对风险损害发生的可能性、可被检测到的程度、对试验参与者保护和试验结果可靠性的影响进行评估
- **风险控制**:风险控制应与风险对试验参与者权益和安全、试验结果可靠性影响的重要性相称;预先设定风险控制可接受范围,超出时评估是否需要采取措施
- **风险沟通**:记录已识别的风险和相应的缓解措施,并与参与采取措施或受此类活动影响的人员沟通
- **风险审查**:结合临床试验期间的新知识和经验,定期审查风险控制措施,确保现行质量管理活动的有效性和适用性
- **质量保证与质量控制**建立、实施和及时更新书面SOP委派合格监查员实施监查采用基于风险的方法进行质量控制
- **监督**:对临床试验全过程进行监督,确保试验过程符合试验方案、相关法律法规和伦理标准
---
## 四、风险管理策略
申办者应结合风险性质与程度,采取**分层风险管理策略**
- **法律依据**GCP第三十九条规定申办者应当向研究者和临床试验机构提供与临床试验相关的法律上、经济上的保险或者保证并与临床试验的风险性质和风险程度相适应
- **责任主体**:申办者是临床试验相关损害的责任主体,承担损害费用和补偿、先行及时兑付
- **首要风险**SUSAR、SAE等通过保险进行风险转移确保身故/伤残赔偿金等大额责任有保障
- **次要风险**AE、试验相关医疗费用等通过"自保(专项风险管理基金)"+风险减量服务+外溢风险管理服务解决
- **及时兑付**:承担试验参与者参加临床试验相关损害的诊疗费用及相应补偿;与研究者及时兑付给予受试者的补偿或赔偿
- **RMO模式支持**华泰经纪协助申办者厘清首要风险与次要风险完成首要风险投保申办者设立专项风险管理基金如3-5万/项目);临研安等提供风险减量(知情同意书审阅、人员培训、方案完善建议)与理赔支持
---
*基于《GCP 2025 征求意见》《风险管理模式》《申办者和持有人责任风险管理》等文档整理*

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 4.0 MiB

After

Width:  |  Height:  |  Size: 4.0 MiB

View File

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

Before

Width:  |  Height:  |  Size: 577 KiB

After

Width:  |  Height:  |  Size: 577 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

Before

Width:  |  Height:  |  Size: 736 KiB

After

Width:  |  Height:  |  Size: 736 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -27,11 +27,11 @@ const pathLabels: Record<string, string> = {
'/about/third-party': '第三方机构', '/about/third-party': '第三方机构',
'/about': '关于RMO', '/about': '关于RMO',
// //
'/concern': '风险职责', '/RMO-About-Responsibility': '风险职责',
'/sponsor': '申办者', '/sponsor': '申办者',
'/holder': '持有人', '/Holder': '持有人',
'/institution': '研究中心', '/institution': '研究中心',
'/service-provider': 'CXO', '/service-provider': 'RMO',
'/participant': '参与者', '/participant': '参与者',
// //
'/solutions/pharmacovigilance': '药物警戒', '/solutions/pharmacovigilance': '药物警戒',
@ -118,6 +118,9 @@ function getLabel(path: string): string {
if (claimMatch) return '理赔详情' if (claimMatch) return '理赔详情'
const quoteTaskMatch = path.match(/^(\/dashboard\/smart-ops\/quote-tasks\/)[^/]+$/) const quoteTaskMatch = path.match(/^(\/dashboard\/smart-ops\/quote-tasks\/)[^/]+$/)
if (quoteTaskMatch) return '报价任务详情' if (quoteTaskMatch) return '报价任务详情'
const videoDetailMatch = path.match(/^\/knowledge\/learning-center\/videos\/[^/]+$/)
if (videoDetailMatch) return '视频详情'
if (path === '/faq/ask') return '发起提问'
// //
const segments = path.split('/').filter(Boolean) const segments = path.split('/').filter(Boolean)
const lastSegment = segments[segments.length - 1] const lastSegment = segments[segments.length - 1]

View File

@ -13,7 +13,7 @@ function DashboardLayout({ children }: DashboardLayoutProps) {
const navigate = useNavigate() const navigate = useNavigate()
const [sidebarOpen, setSidebarOpen] = useState(true) const [sidebarOpen, setSidebarOpen] = useState(true)
const [rmoMenuOpen, setRmoMenuOpen] = useState(false) const [rmoMenuOpen, setRmoMenuOpen] = useState(false)
const [concernMenuOpen, setConcernMenuOpen] = useState(false) const [responsibilityMenuOpen, setResponsibilityMenuOpen] = useState(false)
const [systemMenuOpen, setSystemMenuOpen] = useState(false) const [systemMenuOpen, setSystemMenuOpen] = useState(false)
const isActive = (path: string) => location.pathname === path const isActive = (path: string) => location.pathname === path
@ -21,7 +21,7 @@ function DashboardLayout({ children }: DashboardLayoutProps) {
const isActiveParentMulti = (paths: string[]) => paths.some(path => location.pathname.startsWith(path)) const isActiveParentMulti = (paths: string[]) => paths.some(path => location.pathname.startsWith(path))
// 根据角色判断权限 // 根据角色判断权限
const isPolicyholder = user?.role === '投保人' const isPolicyHolder = user?.role === '投保人'
const isInsurer = user?.role === '保险人' const isInsurer = user?.role === '保险人'
// 投保人可见:项目列表、理赔进度、智能工具 // 投保人可见:项目列表、理赔进度、智能工具
@ -60,7 +60,7 @@ function DashboardLayout({ children }: DashboardLayoutProps) {
</Link> </Link>
{/* 投保人可见:项目报价、项目列表 */} {/* 投保人可见:项目报价、项目列表 */}
{isPolicyholder && ( {isPolicyHolder && (
<> <>
<Link <Link
to="/dashboard/project-quotes" to="/dashboard/project-quotes"
@ -91,7 +91,7 @@ function DashboardLayout({ children }: DashboardLayoutProps) {
)} )}
{/* 投保人、保险人可见:理赔进度 */} {/* 投保人、保险人可见:理赔进度 */}
{(isPolicyholder || isInsurer) && ( {(isPolicyHolder || isInsurer) && (
<Link <Link
to="/dashboard/claims" to="/dashboard/claims"
className={`nav-item ${isActiveParent('/dashboard/claims') ? 'active' : ''}`} className={`nav-item ${isActiveParent('/dashboard/claims') ? 'active' : ''}`}
@ -117,7 +117,7 @@ function DashboardLayout({ children }: DashboardLayoutProps) {
</Link> </Link>
{/* ICF智能修改仅投保人可见 */} {/* ICF智能修改仅投保人可见 */}
{isPolicyholder && ( {isPolicyHolder && (
<Link <Link
to="/dashboard/tools/icf-editor" to="/dashboard/tools/icf-editor"
className={`nav-subitem ${isActive('/dashboard/tools/icf-editor') ? 'active' : ''}`} className={`nav-subitem ${isActive('/dashboard/tools/icf-editor') ? 'active' : ''}`}
@ -126,7 +126,7 @@ function DashboardLayout({ children }: DashboardLayoutProps) {
</Link> </Link>
)} )}
{/* 方案风险评分:仅投保人可见 */} {/* 方案风险评分:仅投保人可见 */}
{isPolicyholder && ( {isPolicyHolder && (
<Link <Link
to="/dashboard/tools/risk-scoring" to="/dashboard/tools/risk-scoring"
className={`nav-subitem ${isActive('/dashboard/tools/risk-scoring') ? 'active' : ''}`} className={`nav-subitem ${isActive('/dashboard/tools/risk-scoring') ? 'active' : ''}`}
@ -171,19 +171,19 @@ function DashboardLayout({ children }: DashboardLayoutProps) {
{/* 风险职责 */} {/* 风险职责 */}
<div <div
className="nav-dropdown" className="nav-dropdown"
onMouseEnter={() => setConcernMenuOpen(true)} onMouseEnter={() => setResponsibilityMenuOpen(true)}
onMouseLeave={() => setConcernMenuOpen(false)} onMouseLeave={() => setResponsibilityMenuOpen(false)}
> >
<Link <Link
to="/concern" to="/RMO-About-Responsibility"
className={isActiveParentMulti(['/concern', '/sponsor', '/holder', '/institution', '/service-provider', '/participant']) ? 'active' : ''} className={isActiveParentMulti(['/RMO-About-Responsibility', '/sponsor', '/Holder', '/institution', '/service-provider', '/participant']) ? 'active' : ''}
> >
</Link> </Link>
{concernMenuOpen && ( {responsibilityMenuOpen && (
<div className="dropdown-menu"> <div className="dropdown-menu">
<Link to="/sponsor"></Link> <Link to="/sponsor"></Link>
<Link to="/holder"></Link> <Link to="/Holder"></Link>
<Link to="/participant"></Link> <Link to="/participant"></Link>
<Link to="/institution"></Link> <Link to="/institution"></Link>
<Link to="/service-provider">CXO职责</Link> <Link to="/service-provider">CXO职责</Link>

View File

@ -18,7 +18,7 @@
<span class="nav-icon">📊</span> <span class="nav-icon">📊</span>
<span v-show="sidebarOpen" class="nav-text">工作台</span> <span v-show="sidebarOpen" class="nav-text">工作台</span>
</RouterLink> </RouterLink>
<template v-if="isPolicyholder"> <template v-if="isPolicyHolder">
<RouterLink to="/dashboard/project-quotes" :class="['nav-item', { active: route.path === '/dashboard/project-quotes' }]"> <RouterLink to="/dashboard/project-quotes" :class="['nav-item', { active: route.path === '/dashboard/project-quotes' }]">
<span class="nav-icon">💰</span> <span class="nav-icon">💰</span>
<span v-show="sidebarOpen" class="nav-text">项目报价</span> <span v-show="sidebarOpen" class="nav-text">项目报价</span>
@ -50,7 +50,7 @@
</div> </div>
</div> </div>
</template> </template>
<template v-if="isPolicyholder || isInsurer"> <template v-if="isPolicyHolder || isInsurer">
<RouterLink to="/dashboard/claims" :class="['nav-item', { active: isActiveParent('/dashboard/claims') }]"> <RouterLink to="/dashboard/claims" :class="['nav-item', { active: isActiveParent('/dashboard/claims') }]">
<span class="nav-icon">📝</span> <span class="nav-icon">📝</span>
<span v-show="sidebarOpen" class="nav-text">理赔进度</span> <span v-show="sidebarOpen" class="nav-text">理赔进度</span>
@ -63,8 +63,8 @@
</div> </div>
<div v-show="isActiveParent('/dashboard/tools')" class="nav-submenu"> <div v-show="isActiveParent('/dashboard/tools')" class="nav-submenu">
<RouterLink to="/dashboard/tools/premium-calculator" :class="['nav-subitem', { active: route.path === '/dashboard/tools/premium-calculator' }]">保费测算工具</RouterLink> <RouterLink to="/dashboard/tools/premium-calculator" :class="['nav-subitem', { active: route.path === '/dashboard/tools/premium-calculator' }]">保费测算工具</RouterLink>
<RouterLink v-if="isPolicyholder" to="/dashboard/tools/icf-editor" :class="['nav-subitem', { active: route.path === '/dashboard/tools/icf-editor' }]">ICF智能修改</RouterLink> <RouterLink v-if="isPolicyHolder" to="/dashboard/tools/icf-editor" :class="['nav-subitem', { active: route.path === '/dashboard/tools/icf-editor' }]">ICF智能修改</RouterLink>
<RouterLink v-if="isPolicyholder" to="/dashboard/tools/risk-scoring" :class="['nav-subitem', { active: route.path === '/dashboard/tools/risk-scoring' }]">方案风险评分</RouterLink> <RouterLink v-if="isPolicyHolder" to="/dashboard/tools/risk-scoring" :class="['nav-subitem', { active: route.path === '/dashboard/tools/risk-scoring' }]">方案风险评分</RouterLink>
<RouterLink to="/dashboard/tools/protocol-risk" :class="['nav-subitem', { active: route.path === '/dashboard/tools/protocol-risk' }]">方案风险评估</RouterLink> <RouterLink to="/dashboard/tools/protocol-risk" :class="['nav-subitem', { active: route.path === '/dashboard/tools/protocol-risk' }]">方案风险评估</RouterLink>
<RouterLink to="/dashboard/tools/drug-safety" :class="['nav-subitem', { active: route.path === '/dashboard/tools/drug-safety' }]">药安查</RouterLink> <RouterLink to="/dashboard/tools/drug-safety" :class="['nav-subitem', { active: route.path === '/dashboard/tools/drug-safety' }]">药安查</RouterLink>
</div> </div>
@ -80,14 +80,14 @@
</RouterLink> </RouterLink>
<nav class="header-nav"> <nav class="header-nav">
<RouterLink to="/" :class="{ active: route.path === '/' }">首页</RouterLink> <RouterLink to="/" :class="{ active: route.path === '/' }">首页</RouterLink>
<div class="nav-dropdown" @mouseenter="concernMenuOpen = true" @mouseleave="concernMenuOpen = false"> <div class="nav-dropdown" @mouseenter="responsibilityMenuOpen = true" @mouseleave="responsibilityMenuOpen = false">
<RouterLink to="/concern" :class="{ active: isActiveParentMulti(concernPaths) }">风险职责</RouterLink> <RouterLink to="/RMO-About-Responsibility" :class="{ active: isActiveParentMulti(responsibilityPaths) }">风险职责</RouterLink>
<div v-show="concernMenuOpen" class="dropdown-menu"> <div v-show="responsibilityMenuOpen" class="dropdown-menu">
<RouterLink to="/sponsor">申办者职责</RouterLink> <RouterLink to="/sponsor">申办者职责</RouterLink>
<RouterLink to="/holder">持有人职责</RouterLink> <RouterLink to="/Holder">持有人职责</RouterLink>
<RouterLink to="/participant">受试者专区</RouterLink> <RouterLink to="/participant">受试者</RouterLink>
<RouterLink to="/institution">研究中心</RouterLink> <RouterLink to="/institution">研究中心</RouterLink>
<RouterLink to="/service-provider">CXO职责</RouterLink> <RouterLink to="/about/overview">RMO</RouterLink>
</div> </div>
</div> </div>
<div class="nav-dropdown" @mouseenter="rmoMenuOpen = true" @mouseleave="rmoMenuOpen = false"> <div class="nav-dropdown" @mouseenter="rmoMenuOpen = true" @mouseleave="rmoMenuOpen = false">
@ -139,14 +139,14 @@ const auth = useAuthStore()
const sidebarOpen = ref(true) const sidebarOpen = ref(true)
const rmoMenuOpen = ref(false) const rmoMenuOpen = ref(false)
const concernMenuOpen = ref(false) const responsibilityMenuOpen = ref(false)
const systemMenuOpen = ref(false) const systemMenuOpen = ref(false)
const concernPaths = ['/concern', '/sponsor', '/holder', '/institution', '/service-provider', '/participant'] const responsibilityPaths = ['/RMO-About-Responsibility', '/sponsor', '/Holder', '/institution', '/service-provider', '/participant']
const rmoPaths = ['/rmo-mode', '/insurance', '/guarantee', '/insurance-guarantee'] const rmoPaths = ['/rmo-mode', '/insurance', '/guarantee', '/insurance-guarantee']
const systemPaths = ['/system-management', '/faq'] const systemPaths = ['/system-management', '/faq']
const isPolicyholder = computed(() => auth.user?.role === '投保人') const isPolicyHolder = computed(() => auth.user?.role === '投保人')
const isInsurer = computed(() => auth.user?.role === '保险人') const isInsurer = computed(() => auth.user?.role === '保险人')
const isTPA = computed(() => auth.user?.role === '服务方') const isTPA = computed(() => auth.user?.role === '服务方')

View File

@ -0,0 +1,173 @@
<template>
<div class="faq-ask-form">
<p class="form-intro">请填写以下信息我们会尽快通过邮件或电话回复您</p>
<form class="ask-form" @submit.prevent="handleSubmit">
<div class="form-row">
<label for="faq-name">姓名 <span class="required">*</span></label>
<input id="faq-name" v-model="form.name" type="text" required placeholder="请输入您的姓名" class="form-input" />
</div>
<div class="form-row">
<label for="faq-email">邮箱 <span class="required">*</span></label>
<input id="faq-email" v-model="form.email" type="email" required placeholder="请输入您的邮箱" class="form-input" />
</div>
<div class="form-row">
<label for="faq-phone">电话</label>
<input id="faq-phone" v-model="form.phone" type="tel" placeholder="请输入您的电话(选填)" class="form-input" />
</div>
<div class="form-row">
<label for="faq-category">问题分类</label>
<select id="faq-category" v-model="form.category" class="form-input">
<option value="">请选择选填</option>
<option value="duty">职责逻辑</option>
<option value="coverage">保障范围</option>
<option value="pv-insurance">PV与保险</option>
<option value="claims">理赔相关</option>
<option value="other">其他</option>
</select>
</div>
<div class="form-row">
<label for="faq-content">问题内容 <span class="required">*</span></label>
<textarea id="faq-content" v-model="form.content" required placeholder="请详细描述您的问题" rows="6" class="form-input form-textarea"></textarea>
</div>
<div class="form-actions">
<button type="submit" class="btn-submit">提交提问</button>
<RouterLink to="/faq" class="btn-cancel">返回常见问题</RouterLink>
</div>
</form>
<p class="form-note">
您也可以直接通过邮件 <a href="mailto:rmo@vdanno.com">rmo@vdanno.com</a> 或电话 <a href="tel:4009606520">4009 606 520</a> 联系我们
</p>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
const form = reactive({
name: '',
email: '',
phone: '',
category: '',
content: ''
})
function handleSubmit() {
const subject = encodeURIComponent(`[FAQ提问] ${form.category ? `[${getCategoryLabel(form.category)}] ` : ''}${form.content.slice(0, 30)}...`)
const body = encodeURIComponent(
`姓名:${form.name}\n邮箱${form.email}\n电话${form.phone}\n问题分类${getCategoryLabel(form.category) || '未选择'}\n\n问题内容\n${form.content}`
)
window.location.href = `mailto:rmo@vdanno.com?subject=${subject}&body=${body}`
}
function getCategoryLabel(val: string): string {
const map: Record<string, string> = {
duty: '职责逻辑',
coverage: '保障范围',
'pv-insurance': 'PV与保险',
claims: '理赔相关',
other: '其他'
}
return map[val] || ''
}
</script>
<style scoped>
.faq-ask-form {
max-width: 560px;
}
.form-intro {
font-size: 0.9375rem;
color: var(--text-secondary);
margin: 0 0 1.5rem 0;
line-height: 1.6;
}
.ask-form {
margin-bottom: 1.5rem;
}
.form-row {
margin-bottom: 1.25rem;
}
.form-row label {
display: block;
font-size: 0.9375rem;
font-weight: 500;
color: var(--text-color);
margin-bottom: 0.4rem;
}
.required {
color: #dc2626;
}
.form-input {
width: 100%;
padding: 0.6rem 0.75rem;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 0.9375rem;
font-family: inherit;
transition: border-color 0.2s;
}
.form-input:focus {
outline: none;
border-color: var(--brand-primary);
}
.form-textarea {
resize: vertical;
min-height: 120px;
}
.form-actions {
display: flex;
gap: 1rem;
margin-top: 1.5rem;
}
.btn-submit {
padding: 0.6rem 1.5rem;
background: var(--brand-primary, #2563eb);
color: white;
border: none;
border-radius: 6px;
font-size: 0.9375rem;
font-weight: 500;
cursor: pointer;
}
.btn-submit:hover {
opacity: 0.9;
}
.btn-cancel {
padding: 0.6rem 1rem;
color: var(--text-secondary);
font-size: 0.9375rem;
text-decoration: none;
}
.btn-cancel:hover {
color: var(--brand-primary);
}
.form-note {
font-size: 0.8125rem;
color: var(--text-light);
margin: 0;
line-height: 1.6;
}
.form-note a {
color: var(--brand-primary);
text-decoration: none;
}
.form-note a:hover {
text-decoration: underline;
}
</style>

View File

@ -0,0 +1,37 @@
<template>
<div class="faq-ask-link-wrap">
<RouterLink to="/faq/ask" class="faq-ask-link">
<span class="faq-ask-icon"></span>
发起提问
</RouterLink>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.faq-ask-link-wrap {
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 1px solid var(--border-color);
}
.faq-ask-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 1rem;
font-weight: 500;
color: var(--brand-primary, #2563eb);
text-decoration: none;
}
.faq-ask-link:hover {
text-decoration: underline;
}
.faq-ask-icon {
font-size: 1.2rem;
}
</style>

View File

@ -7,12 +7,12 @@ function Footer() {
<div className="footer-content"> <div className="footer-content">
<div className="footer-section"> <div className="footer-section">
<h3></h3> <h3></h3>
<p></p> <p></p>
</div> </div>
<div className="footer-section"> <div className="footer-section">
<h3></h3> <h3></h3>
<p>400-XXX-XXXX</p> <p>4009-606-520</p>
<p>info@rmo.com</p> <p>rmo@vdano.com</p>
</div> </div>
<div className="footer-section"> <div className="footer-section">
<h3></h3> <h3></h3>

View File

@ -4,17 +4,17 @@
<div class="footer-content"> <div class="footer-content">
<div class="footer-section"> <div class="footer-section">
<h3>关于我们</h3> <h3>关于我们</h3>
<p>临研安致力于为临床试验提供一站式的风险管理解决方案保障受试者安全</p> <p>关爱患者安全让药物警戒更有价值从生命科学的发展中获得更多获益更少风险</p>
</div> </div>
<div class="footer-section"> <div class="footer-section">
<h3>联系方式</h3> <h3>联系方式</h3>
<p>电话400-XXX-XXXX</p> <p>电话4009-606-520</p>
<p>邮箱info@rmo.com</p> <p>邮箱rmo@vdano.com</p>
</div> </div>
<div class="footer-section"> <div class="footer-section">
<h3>合作伙伴</h3> <h3>合作伙伴</h3>
<p>华泰保险经纪</p> <p>华泰保险经纪</p>
<p>临研安</p> <p>北京药盾公益基金</p>
</div> </div>
</div> </div>
<div class="footer-bottom"> <div class="footer-bottom">

View File

@ -40,7 +40,7 @@ function Header() {
> >
<Link <Link
to="/about/overview" to="/about/overview"
className={isActiveParent(['/about', '/concern', '/sponsor', '/holder', '/institution', '/service-provider', '/participant']) ? 'active' : ''} className={isActiveParent(['/about', '/RMO-About-Responsibility', '/sponsor', '/Holder', '/institution', '/service-provider', '/participant']) ? 'active' : ''}
> >
RMO RMO
<span className="dropdown-arrow"></span> <span className="dropdown-arrow"></span>
@ -53,9 +53,9 @@ function Header() {
</div> </div>
{/* 风险职责 */} {/* 风险职责 */}
<div className="dropdown-submenu"> <div className="dropdown-submenu">
<Link to="/concern" className="dropdown-submenu-title-link"></Link> <Link to="/RMO-About-Responsibility" className="dropdown-submenu-title-link"></Link>
<Link to="/sponsor"></Link> <Link to="/sponsor"></Link>
<Link to="/holder"></Link> <Link to="/Holder"></Link>
<Link to="/institution"></Link> <Link to="/institution"></Link>
<Link to="/participant"></Link> <Link to="/participant"></Link>
<Link to="/service-provider">CXO</Link> <Link to="/service-provider">CXO</Link>

View File

@ -29,12 +29,12 @@
<RouterLink to="/about/third-party">第三方机构</RouterLink> <RouterLink to="/about/third-party">第三方机构</RouterLink>
</div> </div>
<div class="dropdown-submenu"> <div class="dropdown-submenu">
<RouterLink to="/concern" class="dropdown-submenu-title-link">风险职责</RouterLink> <RouterLink to="/RMO-About-Responsibility" class="dropdown-submenu-title-link">风险职责</RouterLink>
<RouterLink to="/sponsor">申办者</RouterLink> <RouterLink to="/sponsor">申办者</RouterLink>
<RouterLink to="/holder">持有人</RouterLink> <RouterLink to="/Holder">持有人</RouterLink>
<RouterLink to="/institution">研究中心</RouterLink> <RouterLink to="/institution">研究中心</RouterLink>
<RouterLink to="/participant">参与者</RouterLink> <RouterLink to="/participant">参与者</RouterLink>
<RouterLink to="/service-provider">CXO</RouterLink> <RouterLink to="/about/overview">RMO</RouterLink>
</div> </div>
</div> </div>
</div> </div>
@ -140,7 +140,7 @@ const aboutRmoOpen = ref(false)
const solutionsOpen = ref(false) const solutionsOpen = ref(false)
const knowledgeOpen = ref(false) const knowledgeOpen = ref(false)
const aboutPaths = ['/about', '/concern', '/sponsor', '/holder', '/institution', '/service-provider', '/participant'] const aboutPaths = ['/about', '/RMO-About-Responsibility', '/sponsor', '/Holder', '/institution', '/service-provider', '/participant']
const solutionsPaths = ['/solutions', '/risk-data', '/rmo-mode', '/post-market', '/product-insurance'] const solutionsPaths = ['/solutions', '/risk-data', '/rmo-mode', '/post-market', '/product-insurance']
const knowledgePaths = ['/knowledge', '/system-management', '/faq'] const knowledgePaths = ['/knowledge', '/system-management', '/faq']

View File

@ -141,7 +141,7 @@ function QuoteRequestModal() {
type="text" type="text"
value={formData.projectCode} value={formData.projectCode}
onChange={(e) => setFormData((d) => ({ ...d, projectCode: e.target.value }))} onChange={(e) => setFormData((d) => ({ ...d, projectCode: e.target.value }))}
placeholder="如CT-2025-001" placeHolder="如CT-2025-001"
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
@ -150,7 +150,7 @@ function QuoteRequestModal() {
type="text" type="text"
value={formData.projectTitle} value={formData.projectTitle}
onChange={(e) => setFormData((d) => ({ ...d, projectTitle: e.target.value }))} onChange={(e) => setFormData((d) => ({ ...d, projectTitle: e.target.value }))}
placeholder="试验方案标题" placeHolder="试验方案标题"
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
@ -159,7 +159,7 @@ function QuoteRequestModal() {
type="text" type="text"
value={formData.sponsor} value={formData.sponsor}
onChange={(e) => setFormData((d) => ({ ...d, sponsor: e.target.value }))} onChange={(e) => setFormData((d) => ({ ...d, sponsor: e.target.value }))}
placeholder="申办者名称" placeHolder="申办者名称"
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
@ -168,7 +168,7 @@ function QuoteRequestModal() {
type="text" type="text"
value={formData.projectPhase} value={formData.projectPhase}
onChange={(e) => setFormData((d) => ({ ...d, projectPhase: e.target.value }))} onChange={(e) => setFormData((d) => ({ ...d, projectPhase: e.target.value }))}
placeholder="如I期、II期、III期" placeHolder="如I期、II期、III期"
/> />
</div> </div>
</div> </div>

View File

@ -27,19 +27,19 @@
<div class="quote-form-grid"> <div class="quote-form-grid">
<div class="form-group"> <div class="form-group">
<label>项目方案编号</label> <label>项目方案编号</label>
<input v-model="formData.projectCode" type="text" placeholder="如CT-2025-001" /> <input v-model="formData.projectCode" type="text" placeHolder="如CT-2025-001" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label>项目标题</label> <label>项目标题</label>
<input v-model="formData.projectTitle" type="text" placeholder="试验方案标题" /> <input v-model="formData.projectTitle" type="text" placeHolder="试验方案标题" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label>申办者</label> <label>申办者</label>
<input v-model="formData.sponsor" type="text" placeholder="申办者名称" /> <input v-model="formData.sponsor" type="text" placeHolder="申办者名称" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label>项目分期</label> <label>项目分期</label>
<input v-model="formData.projectPhase" type="text" placeholder="如I期、II期、III期" /> <input v-model="formData.projectPhase" type="text" placeHolder="如I期、II期、III期" />
</div> </div>
</div> </div>
</section> </section>

View File

@ -0,0 +1,339 @@
<template>
<div v-if="open" class="training-modal-overlay" @click="handleClose">
<div class="training-modal" @click.stop>
<div class="training-modal-header">
<h2 class="training-modal-title">预约培训申请</h2>
<button type="button" class="training-modal-close" @click="handleClose" aria-label="关闭">×</button>
</div>
<div class="training-modal-body">
<p class="training-modal-desc">
请填写以下信息我们将根据您的需求安排定制化培训或线下讲座并在 13 个工作日内与您联系
</p>
<form class="training-form" @submit.prevent="handleSubmit">
<div class="form-row">
<div class="form-group">
<label for="name">姓名 <span class="required">*</span></label>
<input
id="name"
v-model="formData.name"
type="text"
placeholder="请输入您的姓名"
required
/>
</div>
<div class="form-group">
<label for="company">单位/机构 <span class="required">*</span></label>
<input
id="company"
v-model="formData.company"
type="text"
placeholder="请输入单位或机构名称"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="phone">联系电话 <span class="required">*</span></label>
<input
id="phone"
v-model="formData.phone"
type="tel"
placeholder="请输入联系电话"
required
/>
</div>
<div class="form-group">
<label for="email">邮箱 <span class="required">*</span></label>
<input
id="email"
v-model="formData.email"
type="email"
placeholder="请输入邮箱地址"
required
/>
</div>
</div>
<div class="form-group">
<label for="topic">培训主题/意向</label>
<select id="topic" v-model="formData.topic">
<option value="">请选择培训主题</option>
<option value="clinical-trial-liability">临床试验申办者责任与赔偿</option>
<option value="insurance-basics">临床试验保险基础知识</option>
<option value="claims-process">理赔流程与实务</option>
<option value="risk-management">风险管理与保险配置</option>
<option value="icf-compliance">知情同意书与合规</option>
<option value="custom">其他请在备注中说明</option>
</select>
</div>
<div class="form-row">
<div class="form-group">
<label for="preferredTime">培训时间偏好</label>
<input
id="preferredTime"
v-model="formData.preferredTime"
type="text"
placeholder="如2025年3月、工作日等"
/>
</div>
<div class="form-group">
<label for="participantCount">预计参与人数</label>
<input
id="participantCount"
v-model="formData.participantCount"
type="text"
placeholder="如20人"
/>
</div>
</div>
<div class="form-group">
<label for="remark">备注</label>
<textarea
id="remark"
v-model="formData.remark"
rows="4"
placeholder="请补充其他需求或说明"
/>
</div>
<div v-if="submitSuccess" class="submit-success">
<p>感谢您的申请我们已收到您的预约信息将在 13 个工作日内与您联系</p>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" @click="handleClose">取消</button>
<button
type="submit"
class="btn btn-primary"
:disabled="submitting"
>
{{ submitting ? '提交中…' : '提交申请' }}
</button>
</div>
</form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
const props = defineProps<{
open: boolean
}>()
const emit = defineEmits<{
(e: 'update:open', value: boolean): void
}>()
const formData = ref({
name: '',
company: '',
phone: '',
email: '',
topic: '',
preferredTime: '',
participantCount: '',
remark: ''
})
const submitting = ref(false)
const submitSuccess = ref(false)
function handleClose() {
emit('update:open', false)
}
watch(() => props.open, (val) => {
if (!val) {
formData.value = {
name: '',
company: '',
phone: '',
email: '',
topic: '',
preferredTime: '',
participantCount: '',
remark: ''
}
submitSuccess.value = false
}
})
async function handleSubmit() {
if (submitting.value) return
submitting.value = true
await new Promise(r => setTimeout(r, 800))
submitSuccess.value = true
submitting.value = false
}
</script>
<style scoped>
.training-modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 16px;
box-sizing: border-box;
}
.training-modal {
background: var(--white, #fff);
border-radius: 12px;
max-width: 560px;
width: 100%;
max-height: 90vh;
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}
.training-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid var(--border-color, #e5e7eb);
flex-shrink: 0;
}
.training-modal-title {
margin: 0;
font-size: 18px;
font-weight: 600;
color: var(--brand-text-default, #111);
}
.training-modal-close {
width: 32px;
height: 32px;
border: none;
background: transparent;
font-size: 24px;
line-height: 1;
color: var(--text-color, #555);
cursor: pointer;
border-radius: 6px;
padding: 0;
}
.training-modal-close:hover {
background: var(--bg-color, #f3f4f6);
color: var(--brand-text-default, #111);
}
.training-modal-body {
padding: 20px;
overflow-y: auto;
}
.training-modal-desc {
font-size: 14px;
color: var(--text-color, #555);
margin: 0 0 20px 0;
line-height: 1.5;
}
.training-form .form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.training-form .form-group {
margin-bottom: 16px;
}
.training-form .form-group label {
display: block;
font-size: 13px;
font-weight: 500;
color: var(--text-color, #555);
margin-bottom: 6px;
}
.required {
color: #c00;
}
.training-form .form-group input,
.training-form .form-group select,
.training-form .form-group textarea {
width: 100%;
padding: 8px 10px;
font-size: 14px;
border: 1px solid var(--border-color, #e5e7eb);
border-radius: 6px;
box-sizing: border-box;
}
.training-form .form-group textarea {
resize: vertical;
min-height: 80px;
}
.submit-success {
padding: 12px;
background: rgba(34, 197, 94, 0.1);
border: 1px solid rgba(34, 197, 94, 0.3);
border-radius: 8px;
margin-bottom: 16px;
}
.submit-success p {
margin: 0;
font-size: 14px;
color: var(--text-color);
line-height: 1.5;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 20px;
}
.form-actions .btn {
padding: 8px 20px;
font-size: 14px;
border-radius: 8px;
border: none;
cursor: pointer;
}
.form-actions .btn-primary {
background: var(--brand-primary, #0ea5e9);
color: var(--white, #fff);
}
.form-actions .btn-primary:hover:not(:disabled) {
background: var(--brand-primary-dark, #0284c7);
}
.form-actions .btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.form-actions .btn-secondary {
background: var(--white, #fff);
color: var(--text-color, #555);
border: 1px solid var(--border-color, #e5e7eb);
}
.form-actions .btn-secondary:hover {
background: var(--bg-color, #f3f4f6);
}
@media (max-width: 520px) {
.training-form .form-row {
grid-template-columns: 1fr;
}
}
</style>

View File

@ -66,9 +66,9 @@ export function AuthProvider({ children }: { children: ReactNode }) {
// 模拟用户数据(根据用户名返回不同角色) // 模拟用户数据(根据用户名返回不同角色)
const mockUsers: Record<string, { user: User; token: string }> = { const mockUsers: Record<string, { user: User; token: string }> = {
'policyholder': { 'policyHolder': {
user: { id: '1', name: '投保人', email: 'policyholder@rmo.com', role: '投保人' }, user: { id: '1', name: '投保人', email: 'policyHolder@rmo.com', role: '投保人' },
token: 'mock_token_policyholder' token: 'mock_token_policyHolder'
}, },
'insurer': { 'insurer': {
user: { id: '2', name: '保险人', email: 'insurer@rmo.com', role: '保险人' }, user: { id: '2', name: '保险人', email: 'insurer@rmo.com', role: '保险人' },

View File

@ -0,0 +1,95 @@
/**
* - /// PPTX
* public/knowledge/videos/ videoFile
*/
export interface LearningVideo {
id: string
title: string
summary: string
/** 缩略图路径,可选;不填则使用默认占位 */
thumbnail?: string
/** 视频文件名,相对于 /knowledge/videos/ */
videoFile: string
/** 对应 PPTX 文件名,便于追溯 */
pptxFile: string
}
export const learningVideos: LearningVideo[] = [
{
id: 'sponsor-liability',
title: '临床试验申办者赔不赔,从责任本质说起',
summary: '从责任本质角度分析临床试验申办者在何种情况下需要赔偿。涵盖《民法典》相关条款、知情同意书约束关系、申办者与受试者的法律责任界定,以及赔偿的法律依据与适用情形。',
videoFile: '1.临床试验申办者赔不赔,从责任本质说起.mp4',
pptxFile: '1.临床试验申办者赔不赔,从责任本质说起.pptx'
},
{
id: 'compensation-amount',
title: '赔多少,准备多少费用',
summary: '讲解临床试验赔偿金额的测算方法、费用准备与预算规划。包括损失构成、赔偿范围、费用预估模型,以及申办方如何合理配置保险与预算以覆盖潜在赔偿需求。',
videoFile: '2.赔多少准备多少费用V2.0(终稿).mp4',
pptxFile: '2.赔多少准备多少费用V2.0(终稿).pptx'
},
{
id: 'risk-management-lesson',
title: '第三课:风险管理',
summary: '药品全生命周期风险与保险保障。涵盖新药研发阶段的风险识别、风险评价与缓解策略REMS以及药品上市前后各阶段的风险管理与保险保障衔接。',
videoFile: '3.第三课风险管理.mp4',
pptxFile: '药品全生命周期风险与保险保障20250804_终稿.pptx'
},
{
id: 'claims-process',
title: '临床试验受试者责任保险理赔流程',
summary: '介绍受试者责任保险的完整理赔流程,包括报案、材料提交、审核、赔付等环节。说明所需材料清单、时效要求、常见问题及操作注意事项。',
videoFile: '4.临床试验受试者责任保险理赔流程.mp4',
pptxFile: '4.临床试验受试者责任保险理赔流程.pptx'
},
{
id: 'claims-cases',
title: '临床试验受试者责任保险理赔案例',
summary: '通过真实理赔案例展示保险理赔的实操流程与争议处理。分析典型场景下的责任认定、因果关系判断、赔偿比例酌定,以及保险与申办方责任的衔接。',
videoFile: '5.临床试验受试者责任保险理赔案例.mp4',
pptxFile: '5.临床试验受试者责任保险理赔案例.pptx'
},
{
id: 'icf-content',
title: '作为受试者应该知道哪些 ICF 的内容',
summary: '知情同意书ICF关键条款解读与受试者权益保障。涵盖试验目的、风险告知、保险与补偿承诺、退出权利、隐私保护等核心内容帮助受试者理解自身权益。',
videoFile: '6.作为受试者应该知道哪些ICF的内容.mp4',
pptxFile: '6.作为受试者应该知道哪些ICF的内容.pptx'
},
{
id: 'international-relief',
title: '药害救济赔偿的国际经验',
summary: '各国药害救济与赔偿制度的比较与借鉴。介绍日本、德国、美国等国家的药害救济基金、无过错补偿机制及对我国临床试验责任与保险制度的启示。',
videoFile: '7.药害救济赔偿的国际经验.mp4',
pptxFile: '7.药害救济赔偿的国际经验.pptx'
},
{
id: 'insurance-elements',
title: '临床试验保险需要考虑的要素',
summary: '投保临床试验受试者责任保险时需考虑的关键要素:保障范围、赔偿限额、除外责任、追溯期、发现期、免赔额等。帮助申办方科学选配保险方案。',
videoFile: '临床试验保险需要考虑的要素.mp4',
pptxFile: '临床试验保险需要考虑的要素.pptx'
},
{
id: 'dispute-resolution',
title: '临床试验受试者责任保险处理纠纷',
summary: '保险纠纷的类型、处理原则与程序。涵盖理赔争议、责任认定分歧、格式条款解释、举证责任分配,以及协商、调解、诉讼等解决途径。',
videoFile: '临床试验受试者责任保险处理纠纷.mp4',
pptxFile: '临床试验受试者责任保险处理纠纷.pptx'
},
{
id: 'pricing',
title: '临床试验受试者责任保险投保定价',
summary: '保费定价因素与测算方法。包括试验风险等级、受试者数量、赔偿限额、试验周期等因素对保费的影响,以及常见定价模型与报价逻辑。',
videoFile: '临床试验受试者责任保险投保定价.mp4',
pptxFile: '临床试验受试者责任保险投保定价.pptx'
},
{
id: 'claims-cases-general',
title: '理赔案例',
summary: '典型理赔案例深度分析。通过多个真实案例展示不同情境下的责任划分、因果关系认定、赔偿计算,以及保险条款在实际争议中的应用。',
videoFile: '理赔案例.mp4',
pptxFile: '理赔案例.pptx'
}
]

View File

@ -1,4 +1,3 @@
import { Link } from 'react-router-dom'
import PageContainer from '../components/PageContainer' import PageContainer from '../components/PageContainer'
import PageHeader from '../components/PageHeader' import PageHeader from '../components/PageHeader'
import './Contact.css' import './Contact.css'
@ -17,52 +16,12 @@ function Contact() {
<div className="contact-grid"> <div className="contact-grid">
<div className="contact-card"> <div className="contact-card">
<h3></h3> <h3></h3>
<p>clientservice@rmo.com</p> <p>rmo@vdano.com</p>
<p>400-XXX-XXXX</p> <p>4009-606-520</p>
</div>
<div className="contact-card">
<h3>/RFP</h3>
<p>marketing@rmo.com</p>
<p>400-XXX-XXXX</p>
</div>
<div className="contact-card">
<h3></h3>
<p>PR@rmo.com</p>
<p>400-XXX-XXXX</p>
</div>
<div className="contact-card">
<h3></h3>
<p>compliance@rmo.com</p>
<p>400-XXX-XXXX</p>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<section className="section">
<div className="container">
<h2 className="section-title"></h2>
<p className="section-description">
RMO通讯简报
</p>
<form className="newsletter-form">
<div className="form-group">
<input
type="email"
placeholder="请输入您的邮箱地址"
className="form-input"
required
/>
<button type="submit" className="btn btn-primary">
</button>
</div>
<p className="form-note">
RMO承诺对您在本网页下提供的任何信息RMO<Link to="/privacy-policy" className="privacy-link"></Link>
</p>
</form>
</div>
</section>
</div> </div>
</div> </div>
</PageContainer> </PageContainer>

View File

@ -36,3 +36,89 @@
line-height: 1.6; line-height: 1.6;
margin: 0; margin: 0;
} }
/* FAQ 索引页 */
.faq-index-intro {
font-size: 1rem;
color: var(--text-secondary);
margin: 0 0 1.5rem 0;
line-height: 1.6;
}
.faq-nav-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.25rem;
margin-bottom: 2rem;
}
.faq-nav-card {
display: block;
padding: 1.5rem;
border: 1px solid var(--border-color);
border-radius: 12px;
text-decoration: none;
color: inherit;
transition: border-color 0.2s, box-shadow 0.2s;
}
.faq-nav-card:hover {
border-color: var(--brand-primary);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.faq-nav-icon {
font-size: 1.75rem;
display: block;
margin-bottom: 0.5rem;
}
.faq-nav-card h3 {
font-size: 1.1rem;
font-weight: 600;
color: var(--text-color);
margin: 0 0 0.35rem 0;
}
.faq-nav-card p {
font-size: 0.875rem;
color: var(--text-secondary);
line-height: 1.5;
margin: 0;
}
.faq-ask-section {
padding: 1.5rem;
background: var(--bg-color, #f8f9fa);
border-radius: 12px;
text-align: center;
}
.faq-ask-section h2 {
font-size: 1.1rem;
font-weight: 600;
margin: 0 0 0.5rem 0;
color: var(--text-color);
}
.faq-ask-section p {
font-size: 0.9375rem;
color: var(--text-secondary);
margin: 0 0 1rem 0;
}
.faq-ask-btn {
display: inline-block;
padding: 0.6rem 1.5rem;
background: var(--brand-primary, #2563eb);
color: white;
border-radius: 6px;
font-size: 0.9375rem;
font-weight: 500;
text-decoration: none;
transition: opacity 0.2s;
}
.faq-ask-btn:hover {
opacity: 0.9;
}

View File

@ -1,49 +1,271 @@
.responsibility-overview { /* ========== 持有人页面布局 ========== */
list-style: none; .holder .section {
padding-left: 0; margin-bottom: 32px;
margin: 15px 0 0;
} }
.responsibility-overview li { .holder .section:last-child {
padding: 8px 0; margin-bottom: 0;
padding-left: 1.5em; }
.holder .section-title {
font-size: 20px;
font-weight: 600;
margin: 0 0 20px 0;
color: var(--text-color);
}
.holder .card {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 28px 32px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.holder .card h2 {
font-size: 20px;
font-weight: 600;
margin: 0 0 16px 0;
color: var(--text-color);
}
/* ========== 持有人职责概述 ========== */
.holder .responsibility-overview {
list-style: none;
padding: 0;
margin: 0;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px 24px;
}
.holder .responsibility-overview li {
padding: 10px 0 10px 28px;
position: relative; position: relative;
font-size: 15px;
color: var(--text-color);
line-height: 1.5;
}
.holder .responsibility-overview li::before {
content: "✓";
position: absolute;
left: 0;
color: var(--brand-primary);
font-weight: bold;
}
/* ========== 职责描述左右布局:左侧法规文件,右侧职责内容 ========== */
.duty-layout {
display: flex;
gap: 28px;
align-items: stretch;
margin-top: 20px;
}
.regulation-panel {
flex-shrink: 0;
width: 260px;
}
.regulation-doc {
position: sticky;
top: 24px;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.25s ease;
}
.regulation-doc:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 16px rgba(14, 165, 233, 0.15);
}
.regulation-doc img {
display: block;
width: 100%;
height: auto;
object-fit: cover;
aspect-ratio: 3/4;
}
.regulation-doc-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 32px 24px;
min-height: 200px;
background: linear-gradient(135deg, rgba(14, 165, 233, 0.06) 0%, rgba(14, 165, 233, 0.02) 100%);
border-bottom: 3px solid var(--brand-primary);
}
.regulation-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.8;
}
.regulation-name {
font-size: 15px;
font-weight: 600;
color: var(--text-color);
text-align: center;
line-height: 1.5;
}
.duty-content {
flex: 1;
min-width: 0;
}
.duty-summary-card {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 28px 32px;
border-left: 4px solid var(--brand-primary);
transition: all 0.25s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.duty-summary-card:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.12);
}
.duty-summary-card p {
font-size: 14px;
color: var(--text-color);
line-height: 1.65;
margin: 0 0 14px 0;
}
.duty-summary-card ul {
list-style: none;
padding: 0;
margin: 0;
}
.duty-summary-card li {
padding: 6px 0 6px 20px;
position: relative;
font-size: 14px;
color: var(--text-light);
line-height: 1.6; line-height: 1.6;
} }
.responsibility-overview li::before { .duty-summary-card li::before {
content: "•"; content: "•";
position: absolute; position: absolute;
left: 0; left: 0;
color: var(--primary-color); color: var(--brand-primary);
font-weight: bold;
} }
.content-section { /* ========== 风险管理策略卡片 ========== */
margin: 20px 0; .holder .main-card {
padding: 32px 36px;
} }
.content-section h3 { .strategy-grid {
font-size: 22px; display: grid;
margin: 30px 0 15px; grid-template-columns: repeat(3, 1fr);
color: var(--primary-color); gap: 20px;
margin-top: 16px;
} }
.content-section p { .strategy-card {
font-size: 16px; background: var(--white);
line-height: 1.8; border: 1px solid var(--border-color);
color: var(--text-color); padding: 24px;
margin-bottom: 20px; border-radius: 10px;
text-align: center;
transition: all 0.25s ease;
} }
.content-section ul { .strategy-card:hover {
list-style: none; border-color: var(--brand-primary);
padding-left: 20px; box-shadow: 0 4px 12px rgba(14, 165, 233, 0.1);
margin: 15px 0;
} }
.content-section li { .strategy-card h4 {
padding: 10px 0; font-size: 17px;
font-size: 16px; font-weight: 600;
color: var(--text-color); margin: 0 0 10px 0;
line-height: 1.6; color: var(--brand-primary);
}
.strategy-card p {
font-size: 14px;
color: var(--text-light);
line-height: 1.5;
margin: 0;
}
/* ========== 响应式 ========== */
@media (max-width: 1024px) {
.duty-layout {
flex-direction: column;
gap: 20px;
}
.regulation-panel {
width: 100%;
}
.regulation-doc {
position: static;
display: flex;
}
.regulation-doc-placeholder {
flex-direction: row;
gap: 20px;
min-height: 100px;
padding: 24px 28px;
}
.regulation-icon {
margin-bottom: 0;
font-size: 40px;
}
.regulation-name {
text-align: left;
flex: 1;
}
}
@media (max-width: 768px) {
.regulation-doc-placeholder {
flex-direction: column;
min-height: 120px;
padding: 20px 24px;
}
.regulation-icon {
font-size: 36px;
}
.regulation-name {
font-size: 14px;
text-align: center;
}
.holder .card,
.holder .main-card {
padding: 20px 24px;
}
.holder .responsibility-overview {
grid-template-columns: 1fr;
}
.strategy-grid {
grid-template-columns: 1fr;
}
} }

View File

@ -5,7 +5,7 @@ import './Holder.css'
function Holder() { function Holder() {
return ( return (
<PageContainer> <PageContainer>
<div className="holder"> <div className="Holder">
<PageHeader <PageHeader
title="持有人职责" title="持有人职责"
description="负责上市后药物安全" description="负责上市后药物安全"

View File

@ -1,125 +1,279 @@
.hero-section { /* ========== 研究中心页面布局 ========== */
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); .institution .section {
color: var(--white); margin-bottom: 32px;
padding: 100px 0 60px;
text-align: center;
} }
.hero-section .section-title { .institution .section:last-child {
color: var(--white); margin-bottom: 0;
} }
.hero-section .section-subtitle { .institution .section-title {
color: rgba(255, 255, 255, 0.9); font-size: 20px;
font-weight: 600;
margin: 0 0 20px 0;
color: var(--text-color);
} }
.responsibility-overview { .institution .card {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 28px 32px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.institution .card h2 {
font-size: 20px;
font-weight: 600;
margin: 0 0 16px 0;
color: var(--text-color);
}
/* ========== 研究中心职责概述 ========== */
.institution .responsibility-overview {
list-style: none; list-style: none;
padding-left: 0; padding: 0;
margin: 15px 0 0; margin: 0;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px 24px;
} }
.responsibility-overview li { .institution .responsibility-overview li {
padding: 8px 0; padding: 10px 0 10px 28px;
padding-left: 1.5em;
position: relative; position: relative;
font-size: 15px;
color: var(--text-color);
line-height: 1.5;
}
.institution .responsibility-overview li::before {
content: "✓";
position: absolute;
left: 0;
color: var(--brand-primary);
font-weight: bold;
}
/* ========== 职责描述左右布局:左侧法规文件,右侧职责内容 ========== */
.duty-layout {
display: flex;
gap: 28px;
align-items: stretch;
margin-top: 20px;
}
.regulation-panel {
flex-shrink: 0;
width: 260px;
}
.regulation-doc {
position: sticky;
top: 24px;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.25s ease;
}
.regulation-doc:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 16px rgba(14, 165, 233, 0.15);
}
.regulation-doc img {
display: block;
width: 100%;
height: auto;
object-fit: cover;
aspect-ratio: 3/4;
}
.regulation-doc-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 32px 24px;
min-height: 200px;
background: linear-gradient(135deg, rgba(14, 165, 233, 0.06) 0%, rgba(14, 165, 233, 0.02) 100%);
border-bottom: 3px solid var(--brand-primary);
}
.regulation-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.8;
}
.regulation-name {
font-size: 15px;
font-weight: 600;
color: var(--text-color);
text-align: center;
line-height: 1.5;
}
.duty-content {
flex: 1;
min-width: 0;
}
.duty-summary-card {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 28px 32px;
border-left: 4px solid var(--brand-primary);
transition: all 0.25s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.duty-summary-card:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.12);
}
.duty-summary-card p {
font-size: 14px;
color: var(--text-color);
line-height: 1.65;
margin: 0 0 14px 0;
}
.duty-summary-card ul {
list-style: none;
padding: 0;
margin: 0;
}
.duty-summary-card li {
padding: 6px 0 6px 20px;
position: relative;
font-size: 14px;
color: var(--text-light);
line-height: 1.6; line-height: 1.6;
} }
.responsibility-overview li::before { .duty-summary-card li::before {
content: "•"; content: "•";
position: absolute; position: absolute;
left: 0; left: 0;
color: var(--primary-color); color: var(--brand-primary);
font-weight: bold;
} }
.content-section { /* ========== 风险管理策略卡片 ========== */
margin: 20px 0; .institution .main-card {
padding: 32px 36px;
} }
.content-section h3 { .strategy-grid {
font-size: 22px;
margin: 30px 0 15px;
color: var(--primary-color);
}
.content-section p {
font-size: 16px;
line-height: 1.8;
color: var(--text-color);
margin-bottom: 20px;
}
.content-section ul {
list-style: none;
padding-left: 20px;
margin: 15px 0;
}
.content-section li {
padding: 10px 0;
font-size: 16px;
color: var(--text-color);
line-height: 1.6;
}
.support-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); grid-template-columns: repeat(3, 1fr);
gap: 20px; gap: 20px;
margin: 30px 0; margin-top: 16px;
} }
.support-item { .strategy-grid--2 {
background: var(--bg-color); grid-template-columns: repeat(2, 1fr);
padding: 25px;
border-radius: 8px;
border-left: 4px solid var(--primary-color);
} }
.support-item h4 { .strategy-card {
font-size: 18px; background: var(--white);
margin-bottom: 10px; border: 1px solid var(--border-color);
color: var(--primary-color); padding: 24px;
border-radius: 10px;
text-align: center;
transition: all 0.25s ease;
} }
.support-item p { .strategy-card:hover {
font-size: 15px; border-color: var(--brand-primary);
color: var(--text-color); box-shadow: 0 4px 12px rgba(14, 165, 233, 0.1);
}
.strategy-card h4 {
font-size: 17px;
font-weight: 600;
margin: 0 0 10px 0;
color: var(--brand-primary);
}
.strategy-card p {
font-size: 14px;
color: var(--text-light);
line-height: 1.5;
margin: 0; margin: 0;
line-height: 1.6;
} }
.guarantee-list { /* ========== 响应式 ========== */
display: grid; @media (max-width: 1024px) {
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); .duty-layout {
gap: 25px; flex-direction: column;
margin: 30px 0; gap: 20px;
} }
.guarantee-item { .regulation-panel {
background: var(--bg-color); width: 100%;
padding: 25px;
border-radius: 8px;
} }
.guarantee-item h4 { .regulation-doc {
font-size: 20px; position: static;
margin-bottom: 15px; display: flex;
color: var(--primary-color);
} }
.guarantee-item p { .regulation-doc-placeholder {
font-size: 15px; flex-direction: row;
color: var(--text-color); gap: 20px;
line-height: 1.8; min-height: 100px;
margin: 0; padding: 24px 28px;
}
.regulation-icon {
margin-bottom: 0;
font-size: 40px;
}
.regulation-name {
text-align: left;
flex: 1;
}
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.support-grid, .strategy-grid--2 {
.guarantee-list { grid-template-columns: 1fr;
}
.regulation-doc-placeholder {
flex-direction: column;
min-height: 120px;
padding: 20px 24px;
}
.regulation-icon {
font-size: 36px;
}
.regulation-name {
font-size: 14px;
text-align: center;
}
.institution .card,
.institution .main-card {
padding: 20px 24px;
}
.institution .responsibility-overview {
grid-template-columns: 1fr;
}
.strategy-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }

View File

@ -57,7 +57,7 @@ function Login() {
type="text" type="text"
id="username" id="username"
name="username" name="username"
placeholder="请输入用户名或邮箱" placeHolder="请输入用户名或邮箱"
value={username} value={username}
onChange={(e) => setUsername(e.target.value)} onChange={(e) => setUsername(e.target.value)}
required required
@ -73,7 +73,7 @@ function Login() {
type="password" type="password"
id="password" id="password"
name="password" name="password"
placeholder="请输入密码" placeHolder="请输入密码"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
required required

View File

@ -1,238 +1,205 @@
.hero-section { /* ========== 受试者专区页面布局 ========== */
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); .participant .section {
color: var(--white); margin-bottom: 32px;
padding: 100px 0 60px;
text-align: center;
} }
.hero-section .section-title { .participant .section:last-child {
color: var(--white); margin-bottom: 0;
} }
.hero-section .section-subtitle { .participant .section-title {
color: rgba(255, 255, 255, 0.9);
}
.responsibility-overview {
list-style: none;
padding-left: 0;
margin: 15px 0 0;
}
.responsibility-overview li {
padding: 8px 0;
padding-left: 1.5em;
position: relative;
line-height: 1.6;
}
.responsibility-overview li::before {
content: "•";
position: absolute;
left: 0;
color: var(--primary-color);
}
.content-section {
margin: 20px 0;
}
.content-section h3 {
font-size: 22px;
margin: 30px 0 15px;
color: var(--primary-color);
}
.content-section p {
font-size: 16px;
line-height: 1.8;
color: var(--text-color);
margin-bottom: 20px;
}
.content-section ul {
list-style: none;
padding-left: 20px;
margin: 15px 0;
}
.content-section li {
padding: 10px 0;
font-size: 16px;
color: var(--text-color);
}
.process-flow {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 20px;
margin: 30px 0;
}
.flow-step {
text-align: center;
min-width: 120px;
}
.flow-number {
width: 50px;
height: 50px;
background: var(--primary-color);
color: var(--white);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
margin: 0 auto 10px;
}
.flow-step p {
font-size: 14px;
color: var(--text-color);
margin: 0;
}
.flow-arrow {
font-size: 24px;
color: var(--primary-color);
font-weight: bold;
}
.rights-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
margin: 30px 0;
}
.right-item {
text-align: center;
padding: 30px 20px;
background: var(--bg-color);
border-radius: 12px;
transition: all 0.3s ease;
}
.right-item:hover {
box-shadow: var(--shadow-hover);
filter: brightness(1.02);
}
.right-icon {
font-size: 48px;
margin-bottom: 15px;
}
.right-item h3 {
font-size: 20px; font-size: 20px;
margin-bottom: 15px; font-weight: 600;
color: var(--primary-color); margin: 0 0 20px 0;
}
.right-item p {
font-size: 15px;
color: var(--text-color); color: var(--text-color);
line-height: 1.6;
margin: 0;
} }
.compensation-principles { .participant .card {
display: grid; background: var(--white);
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); border: 1px solid var(--border-color);
gap: 25px; border-radius: 12px;
margin: 30px 0; padding: 28px 32px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
} }
.principle-item { .participant .card h2 {
background: var(--bg-color); font-size: 20px;
padding: 25px; font-weight: 600;
border-radius: 8px; margin: 0 0 16px 0;
border-left: 4px solid var(--primary-color);
}
.principle-item h4 {
font-size: 18px;
margin-bottom: 15px;
color: var(--primary-color);
}
.principle-item p {
font-size: 15px;
color: var(--text-color); color: var(--text-color);
line-height: 1.8;
margin: 0;
} }
.relief-process { /* ========== 受试者职责概述 ========== */
.participant .responsibility-overview {
list-style: none;
padding: 0;
margin: 0;
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 25px; gap: 12px 24px;
margin: 30px 0;
} }
.process-item { .participant .responsibility-overview li {
text-align: center; padding: 10px 0 10px 28px;
padding: 25px; position: relative;
background: var(--bg-color); font-size: 15px;
border-radius: 8px;
}
.process-icon {
width: 50px;
height: 50px;
background: var(--primary-color);
color: var(--white);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
margin: 0 auto 15px;
}
.process-item h4 {
font-size: 18px;
margin-bottom: 10px;
color: var(--text-color); color: var(--text-color);
line-height: 1.5;
} }
.process-item p { .participant .responsibility-overview li::before {
font-size: 14px; content: "✓";
color: var(--text-light); position: absolute;
margin: 0; left: 0;
color: var(--brand-primary);
font-weight: bold;
} }
.contact-info { /* ========== 了解临床试验 - 卡片形式 ========== */
background: var(--bg-color); .info-cards {
padding: 25px; display: grid;
border-radius: 8px; grid-template-columns: repeat(3, 1fr);
gap: 24px;
margin-top: 20px; margin-top: 20px;
} }
.contact-info p { .info-card {
font-size: 16px; background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 28px 24px;
border-left: 4px solid var(--brand-primary);
transition: all 0.25s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.info-card:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.12);
}
.info-card h3 {
font-size: 17px;
font-weight: 600;
margin: 0 0 14px 0;
color: var(--brand-primary);
}
.info-card p {
font-size: 14px;
color: var(--text-color); color: var(--text-color);
margin: 10px 0; line-height: 1.65;
margin: 0;
} }
@media (max-width: 768px) { /* ========== 职责内容卡片 ========== */
.process-flow { .duty-summary-card {
flex-direction: column; background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 28px 32px;
border-left: 4px solid var(--brand-primary);
transition: all 0.25s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
margin-top: 20px;
} }
.flow-arrow { .duty-summary-card:hover {
transform: rotate(90deg); border-color: var(--brand-primary);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.12);
} }
.rights-grid, .duty-summary-card p {
.compensation-principles, font-size: 14px;
.relief-process { color: var(--text-color);
line-height: 1.65;
margin: 0 0 14px 0;
}
.duty-summary-card ul {
list-style: none;
padding: 0;
margin: 0;
}
.duty-summary-card li {
padding: 6px 0 6px 20px;
position: relative;
font-size: 14px;
color: var(--text-light);
line-height: 1.6;
}
.duty-summary-card li::before {
content: "•";
position: absolute;
left: 0;
color: var(--brand-primary);
font-weight: bold;
}
/* ========== 风险管理策略卡片 ========== */
.participant .main-card {
padding: 32px 36px;
}
.strategy-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-top: 16px;
}
.strategy-grid--2 {
grid-template-columns: repeat(2, 1fr);
}
.strategy-card {
background: var(--white);
border: 1px solid var(--border-color);
padding: 24px;
border-radius: 10px;
text-align: center;
transition: all 0.25s ease;
}
.strategy-card:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.1);
}
.strategy-card h4 {
font-size: 17px;
font-weight: 600;
margin: 0 0 10px 0;
color: var(--brand-primary);
}
.strategy-card p {
font-size: 14px;
color: var(--text-light);
line-height: 1.5;
margin: 0;
}
/* ========== 响应式 ========== */
@media (max-width: 1024px) {
.info-cards {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
@media (max-width: 768px) {
.participant .card,
.participant .main-card {
padding: 20px 24px;
}
.participant .responsibility-overview {
grid-template-columns: 1fr;
}
.strategy-grid--2 {
grid-template-columns: 1fr;
}
}

View File

@ -170,7 +170,7 @@ function Participant() {
</div> </div>
<h3></h3> <h3></h3>
<div className="contact-info"> <div className="contact-info">
<p><strong>线</strong>400-XXX-XXXX24</p> <p><strong>线</strong>4009-606-52024</p>
<p><strong></strong>service@rmo.com</p> <p><strong></strong>service@rmo.com</p>
<p><strong></strong>24</p> <p><strong></strong>24</p>
</div> </div>

View File

@ -1,3 +1,57 @@
.post-market .section-title {
font-size: 20px;
font-weight: 600;
margin: 0 0 12px 0;
color: var(--text-color);
}
.post-market .section-subtitle {
font-size: 15px;
color: var(--text-light);
line-height: 1.6;
margin: 0 0 24px 0;
}
.post-market-measures {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 24px;
margin-top: 28px;
}
.post-market-measures .closed-loop-measure-card {
flex: none;
}
.mah-intro-card h3 {
font-size: 17px;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 16px 0;
}
.mah-intro-card ul {
list-style: none;
padding: 0;
margin: 0;
}
.mah-intro-card li {
padding: 8px 0 8px 24px;
position: relative;
font-size: 15px;
line-height: 1.6;
color: var(--text-color);
}
.mah-intro-card li::before {
content: "✓";
position: absolute;
left: 0;
color: var(--brand-primary);
font-weight: bold;
}
.post-market .building-notice { .post-market .building-notice {
text-align: center; text-align: center;
font-size: 1.25rem; font-size: 1.25rem;

View File

@ -13,6 +13,24 @@
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
} }
.diagram-section {
margin-bottom: 0;
}
.responsibility-diagram-wrap {
margin-top: 24px;
border-radius: 12px;
overflow: hidden;
border: 1px solid var(--border-color);
background: var(--white);
}
.responsibility-diagram {
width: 100%;
height: auto;
display: block;
}
.nav-section { .nav-section {
background: var(--bg-color); background: var(--bg-color);
} }

View File

@ -39,7 +39,7 @@ function RiskDutiesOverview() {
<h3></h3> <h3></h3>
<p></p> <p></p>
</Link> </Link>
<Link to="/holder" className="nav-card"> <Link to="/Holder" className="nav-card">
<div className="nav-icon">📋</div> <div className="nav-icon">📋</div>
<h3></h3> <h3></h3>
<p></p> <p></p>

View File

@ -0,0 +1,137 @@
/* ========== 保证方案页面 ========== */
.rmo-guarantee .section-title {
font-size: 20px;
font-weight: 600;
margin: 0 0 12px 0;
color: var(--text-color);
}
.rmo-guarantee .section-subtitle {
font-size: 15px;
color: var(--text-light);
line-height: 1.6;
margin: 0 0 24px 0;
}
/* 风险分层卡片 */
.risk-split-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
margin-top: 24px;
}
.risk-split-card {
border-radius: 12px;
padding: 28px 32px;
border-left: 4px solid;
transition: all 0.25s ease;
background: var(--white);
border: 1px solid var(--border-color);
border-left-width: 4px;
}
.risk-split-card h3 {
font-size: 18px;
font-weight: 600;
margin: 0 0 14px 0;
}
.risk-split-card p {
font-size: 15px;
line-height: 1.7;
margin: 0;
color: var(--text-color);
}
.risk-primary {
border-left-color: var(--brand-primary);
}
.risk-primary h3 {
color: var(--brand-primary);
}
.risk-secondary {
border-left-color: #10b981;
}
.risk-secondary h3 {
color: #059669;
}
/* 保证实现流程 */
.guarantee-flow {
display: flex;
flex-direction: column;
gap: 20px;
margin-top: 24px;
}
.guarantee-flow-item {
background: linear-gradient(135deg, rgba(14, 165, 233, 0.04) 0%, var(--white) 100%);
border: 1px solid var(--border-color);
border-left: 4px solid var(--brand-primary);
border-radius: 10px;
padding: 24px 28px;
}
.guarantee-flow-item h4 {
font-size: 17px;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 12px 0;
}
.guarantee-flow-item p {
font-size: 15px;
line-height: 1.7;
color: var(--text-color);
margin: 0;
}
/* 相关服务列 */
.related-service-columns {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 32px;
}
.related-service-column h3 {
font-size: 17px;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 16px 0;
}
.service-list {
list-style: none;
padding: 0;
margin: 0;
}
.service-list li {
padding: 8px 0 8px 24px;
position: relative;
font-size: 14px;
line-height: 1.6;
color: var(--text-color);
}
.service-list li::before {
content: "✓";
position: absolute;
left: 0;
color: var(--brand-primary);
font-weight: bold;
}
@media (max-width: 768px) {
.risk-split-cards {
grid-template-columns: 1fr;
}
.related-service-columns {
grid-template-columns: 1fr;
}
}

View File

@ -0,0 +1,183 @@
/* ========== 保险方案页面 ========== */
/* 获取报价、申请理赔 链接 */
.action-links {
display: flex;
gap: 24px;
flex-wrap: wrap;
justify-content: center;
}
.action-link {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 32px;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 10px;
text-decoration: none;
color: var(--text-color);
font-size: 16px;
font-weight: 500;
transition: all 0.25s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.action-link:hover {
border-color: var(--brand-primary);
color: var(--brand-primary);
box-shadow: 0 4px 16px rgba(14, 165, 233, 0.15);
}
.action-link-icon {
font-size: 24px;
}
.action-link-text {
flex: 1;
}
.rmo-insurance .section-title {
font-size: 20px;
font-weight: 600;
margin: 0 0 12px 0;
color: var(--text-color);
}
.rmo-insurance .section-subtitle {
font-size: 15px;
color: var(--text-light);
line-height: 1.6;
margin: 0 0 24px 0;
max-width: 800px;
}
/* 风险管理图示 */
.rmo-insurance .why-rm-images {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
}
.rmo-insurance .why-rm-image-wrap {
max-width: 100%;
width: 100%;
border-radius: 12px;
overflow: hidden;
border: 1px solid var(--border-color);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
.rmo-insurance .why-rm-image {
display: block;
width: 100%;
height: auto;
}
.insurance-flow-captions {
display: flex;
justify-content: space-between;
gap: 16px;
margin-top: 16px;
padding: 0 20px;
font-size: 14px;
color: var(--text-light);
}
@media (max-width: 768px) {
.insurance-flow-captions {
flex-direction: column;
align-items: center;
}
}
/* 保险图示通用 */
.insurance-image-wrap {
max-width: 100%;
width: 100%;
margin-top: 20px;
border-radius: 12px;
overflow: hidden;
border: 1px solid var(--border-color);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
.insurance-diagram {
display: block;
width: 100%;
height: auto;
}
/* RMO 角色卡片 */
.rmo-roles-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
margin-top: 28px;
}
.rmo-role-card {
background: var(--white);
border: 1px solid var(--border-color);
border-left: 4px solid var(--brand-primary);
border-radius: 10px;
padding: 20px 24px;
transition: all 0.25s ease;
}
.rmo-role-card:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.12);
}
.rmo-role-card h4 {
font-size: 16px;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 10px 0;
}
.rmo-role-card p {
font-size: 14px;
color: var(--text-light);
line-height: 1.6;
margin: 0;
}
/* 理赔流程摘要 */
.claims-flow-summary {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 24px;
margin-top: 28px;
}
.flow-step-item {
background: linear-gradient(135deg, rgba(14, 165, 233, 0.04) 0%, var(--white) 100%);
border: 1px solid var(--border-color);
border-radius: 10px;
padding: 24px;
}
.flow-step-item h4 {
font-size: 16px;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 12px 0;
}
.flow-step-item p {
font-size: 14px;
color: var(--text-color);
line-height: 1.65;
margin: 0;
}
@media (max-width: 768px) {
.rmo-roles-grid,
.claims-flow-summary {
grid-template-columns: 1fr;
}
}

View File

@ -7,14 +7,14 @@ function ServiceProvider() {
<PageContainer> <PageContainer>
<div className="service-provider"> <div className="service-provider">
<PageHeader <PageHeader
title="CXO职责" title="RMO职责"
description="为CRO、CDMO、SMO提供专业支持" description="为CRO、CDMO、SMO提供专业支持"
/> />
<div className="page-body"> <div className="page-body">
<section className="section"> <section className="section">
<div className="container"> <div className="container">
<div className="card"> <div className="card">
<h2>CXO职责概述</h2> <h2>RMO职责概述</h2>
<ul className="responsibility-overview"> <ul className="responsibility-overview">
<li>CRO</li> <li>CRO</li>
<li>CDMO</li> <li>CDMO</li>

View File

@ -0,0 +1,173 @@
/* ========== GCP 中的内容 ========== */
.solutions-clinical-insurance .section-title {
font-size: 20px;
font-weight: 600;
margin: 0 0 20px 0;
color: var(--text-color);
}
.gcp-section .gcp-card {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 28px 32px;
border-left: 4px solid var(--brand-primary);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.gcp-source {
font-size: 16px;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 16px 0;
}
.gcp-content {
font-size: 15px;
color: var(--text-color);
line-height: 1.8;
margin: 0;
}
/* ========== 公司项目总结轮播 ========== */
.company-carousel-container {
position: relative;
width: 100%;
margin-top: 20px;
}
.company-carousel-wrapper {
position: relative;
width: 100%;
overflow: hidden;
border-radius: 12px;
}
.company-carousel-track {
display: flex;
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform;
}
.company-carousel-slide {
min-width: 100%;
width: 100%;
flex-shrink: 0;
padding: 0 10px;
box-sizing: border-box;
}
.company-summary-card {
background: linear-gradient(135deg, rgba(14, 165, 233, 0.04) 0%, var(--white) 100%);
border: 1px solid var(--border-color);
border-left: 4px solid var(--brand-primary);
border-radius: 12px;
padding: 32px 36px;
min-height: 160px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.company-name {
font-size: 18px;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 16px 0;
word-wrap: break-word;
}
.company-summary {
font-size: 15px;
color: var(--text-color);
line-height: 1.75;
margin: 0;
overflow-wrap: break-word;
word-wrap: break-word;
}
/* 轮播控制 */
.carousel-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
margin-top: 24px;
}
.carousel-btn {
position: relative;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 8px;
width: 48px;
height: 48px;
font-size: 24px;
font-weight: bold;
color: var(--brand-primary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.carousel-btn:hover {
border-color: var(--brand-primary);
background: var(--brand-primary);
color: var(--white);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.3);
}
.carousel-btn-prev::before {
content: '←';
font-size: 20px;
}
.carousel-btn-next::before {
content: '→';
font-size: 20px;
}
.carousel-indicators {
display: flex;
justify-content: center;
gap: 10px;
}
.carousel-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
border: 2px solid var(--brand-primary);
background: transparent;
cursor: pointer;
transition: all 0.3s ease;
padding: 0;
}
.carousel-indicator:hover {
background: rgba(14, 165, 233, 0.3);
}
.carousel-indicator.active {
background: var(--brand-primary);
width: 32px;
border-radius: 6px;
}
@media (max-width: 768px) {
.company-summary-card {
padding: 24px 28px;
min-height: 140px;
}
.carousel-controls {
gap: 16px;
margin-top: 20px;
}
.carousel-btn {
width: 40px;
height: 40px;
}
}

View File

@ -1,262 +1,428 @@
.hero-section { /* ========== 申办者页面布局 ========== */
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); .sponsor .section {
color: var(--white); margin-bottom: 32px;
padding: 100px 0 60px;
text-align: center;
} }
.hero-section .section-title { .sponsor .section:last-child {
color: var(--white); margin-bottom: 0;
} }
.hero-section .section-subtitle { .sponsor .section-title {
color: rgba(255, 255, 255, 0.9); font-size: 20px;
font-weight: 600;
margin: 0 0 20px 0;
color: var(--text-color);
} }
.responsibility-overview { .sponsor .card {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 28px 32px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.sponsor .card h2 {
font-size: 20px;
font-weight: 600;
margin: 0 0 16px 0;
color: var(--text-color);
}
/* ========== 申办者职责概述 ========== */
.sponsor .responsibility-overview {
list-style: none; list-style: none;
padding-left: 0; padding: 0;
margin: 15px 0 0; margin: 0;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px 24px;
} }
.responsibility-overview li { .sponsor .responsibility-overview li {
padding: 8px 0; padding: 10px 0 10px 28px;
padding-left: 1.5em;
position: relative; position: relative;
font-size: 15px;
color: var(--text-color);
line-height: 1.5;
}
.sponsor .responsibility-overview li::before {
content: "✓";
position: absolute;
left: 0;
color: var(--brand-primary);
font-weight: bold;
}
/* ========== 申办者职责(左右布局) ========== */
/* 职责描述左右布局:左侧法规文件,右侧职责内容 */
.duty-layout {
display: flex;
gap: 28px;
align-items: stretch;
margin-top: 20px;
}
.regulation-panel {
flex-shrink: 0;
width: 260px;
}
.regulation-doc {
position: sticky;
top: 24px;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.25s ease;
}
.regulation-doc:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 16px rgba(14, 165, 233, 0.15);
}
.regulation-doc img {
display: block;
width: 100%;
height: auto;
object-fit: cover;
aspect-ratio: 3/4;
}
.regulation-doc-placeHolder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 32px 24px;
min-height: 200px;
background: linear-gradient(135deg, rgba(14, 165, 233, 0.06) 0%, rgba(14, 165, 233, 0.02) 100%);
border-bottom: 3px solid var(--brand-primary);
}
.regulation-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.8;
}
.regulation-name {
font-size: 15px;
font-weight: 600;
color: var(--text-color);
text-align: center;
line-height: 1.5;
}
.duty-content {
flex: 1;
min-width: 0;
}
.duty-summary-card {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 28px 32px;
border-left: 4px solid var(--brand-primary);
transition: all 0.25s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.duty-summary-card:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.12);
}
.duty-summary-card p {
font-size: 14px;
color: var(--text-color);
line-height: 1.65;
margin: 0 0 14px 0;
}
.duty-summary-card ul {
list-style: none;
padding: 0;
margin: 0;
}
.duty-summary-card li {
padding: 6px 0 6px 20px;
position: relative;
font-size: 14px;
color: var(--text-light);
line-height: 1.6; line-height: 1.6;
} }
.responsibility-overview li::before { .duty-summary-card li::before {
content: "•"; content: "•";
position: absolute; position: absolute;
left: 0; left: 0;
color: var(--primary-color); color: var(--brand-primary);
font-weight: bold;
}
/* ========== 风险管理体系 ========== */
.sponsor .main-card {
padding: 32px 36px;
} }
.risk-section { .risk-section {
margin: 30px 0; margin: 28px 0;
padding: 20px 0; padding: 24px 0;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
} }
.risk-section:first-child {
margin-top: 0;
padding-top: 0;
}
.risk-section:last-child { .risk-section:last-child {
border-bottom: none; border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
} }
.risk-section h3 { .risk-section h3 {
font-size: 24px; font-size: 18px;
margin-bottom: 20px; font-weight: 600;
color: var(--primary-color); margin: 0 0 16px 0;
color: var(--brand-primary);
}
.risk-section p {
font-size: 15px;
color: var(--text-light);
line-height: 1.7;
margin: 0;
} }
.risk-categories { .risk-categories {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 30px; gap: 20px;
margin-top: 20px; margin-top: 16px;
} }
.risk-category { .risk-category {
background: var(--bg-color); background: rgba(14, 165, 233, 0.05);
padding: 20px; border: 1px solid rgba(14, 165, 233, 0.15);
border-radius: 8px; padding: 20px 24px;
border-radius: 10px;
transition: all 0.25s ease;
}
.risk-category:hover {
background: rgba(14, 165, 233, 0.08);
border-color: rgba(14, 165, 233, 0.25);
} }
.risk-category h4 { .risk-category h4 {
font-size: 18px; font-size: 16px;
margin-bottom: 15px; font-weight: 600;
margin: 0 0 12px 0;
color: var(--text-color); color: var(--text-color);
} }
.risk-category ul { .risk-category ul {
list-style: none; list-style: none;
padding-left: 20px; padding: 0;
} }
.risk-category li { .risk-category li {
padding: 8px 0; padding: 6px 0;
font-size: 15px; font-size: 14px;
color: var(--text-color); color: var(--text-light);
line-height: 1.6; line-height: 1.5;
} }
.risk-category ul ul { .risk-category ul ul {
margin-top: 5px; margin-top: 4px;
padding-left: 20px; padding-left: 16px;
} }
.risk-category ul ul li {
font-size: 13px;
}
/* ========== 风险管理策略卡片 ========== */
.strategy-grid { .strategy-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); grid-template-columns: repeat(3, 1fr);
gap: 20px; gap: 20px;
margin-top: 20px; margin-top: 16px;
} }
.strategy-card { .strategy-card {
background: var(--bg-color); background: var(--white);
padding: 25px; border: 1px solid var(--border-color);
border-radius: 8px; padding: 24px;
border-left: 4px solid var(--primary-color); border-radius: 10px;
text-align: center;
transition: all 0.25s ease;
}
.strategy-card:hover {
border-color: var(--brand-primary);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.1);
} }
.strategy-card h4 { .strategy-card h4 {
font-size: 20px; font-size: 17px;
margin-bottom: 10px; font-weight: 600;
color: var(--primary-color); margin: 0 0 10px 0;
color: var(--brand-primary);
} }
.strategy-card p { .strategy-card p {
font-size: 15px; font-size: 14px;
color: var(--text-color); color: var(--text-light);
line-height: 1.5;
margin: 0; margin: 0;
} }
.solution-section { /* ========== 操作流程 ========== */
background: var(--bg-color);
}
.solution-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 30px;
}
.solution-card h3 {
font-size: 24px;
margin-bottom: 20px;
color: var(--primary-color);
}
.solution-card ul {
list-style: none;
padding-left: 20px;
margin: 15px 0;
}
.solution-card li {
padding: 8px 0;
font-size: 15px;
color: var(--text-color);
}
.process-section { .process-section {
background: var(--white); background: var(--white);
border-radius: 12px;
padding: 32px;
border: 1px solid var(--border-color);
} }
.process-steps { .process-steps {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); grid-template-columns: repeat(4, 1fr);
gap: 30px; gap: 24px;
margin-top: 40px; margin-top: 28px;
position: relative;
} }
.process-step { .process-step {
text-align: center; text-align: center;
position: relative; position: relative;
padding: 24px 16px;
background: rgba(14, 165, 233, 0.04);
border-radius: 12px;
border: 1px solid rgba(14, 165, 233, 0.12);
transition: all 0.25s ease;
}
.process-step:hover {
background: rgba(14, 165, 233, 0.08);
border-color: rgba(14, 165, 233, 0.2);
} }
.step-number { .step-number {
width: 60px; width: 48px;
height: 60px; height: 48px;
background: var(--primary-color); background: var(--brand-primary);
color: var(--white); color: var(--white);
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 28px; font-size: 20px;
font-weight: bold; font-weight: 700;
margin: 0 auto 20px; margin: 0 auto 16px;
} }
.process-step h3 { .process-step h3 {
font-size: 20px; font-size: 16px;
margin-bottom: 15px; font-weight: 600;
margin: 0 0 10px 0;
color: var(--text-color); color: var(--text-color);
} }
.process-step p { .process-step p {
font-size: 15px; font-size: 14px;
color: var(--text-light); color: var(--text-light);
line-height: 1.6; line-height: 1.5;
}
.team-section {
background: var(--bg-color);
}
.team-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
.team-card {
background: var(--white);
padding: 25px;
border-radius: 8px;
border-left: 4px solid var(--primary-color);
}
.team-card h4 {
font-size: 20px;
margin-bottom: 15px;
color: var(--primary-color);
}
.team-card p {
font-size: 15px;
color: var(--text-color);
line-height: 1.6;
margin: 0; margin: 0;
} }
.team-section .card h3 { /* ========== 响应式 ========== */
font-size: 24px; @media (max-width: 1024px) {
margin-bottom: 20px; .duty-layout {
color: var(--primary-color); flex-direction: column;
gap: 20px;
} }
.team-section .card h4 { .regulation-panel {
font-size: 20px; width: 100%;
margin: 25px 0 15px;
color: var(--text-color);
} }
.team-section .service-list { .regulation-doc {
list-style: none; position: static;
padding: 0; display: flex;
margin: 15px 0;
} }
.team-section .service-list li { .regulation-doc-placeHolder {
padding: 10px 0; flex-direction: row;
font-size: 16px; gap: 20px;
color: var(--text-color); min-height: 100px;
padding: 24px 28px;
} }
.team-section .service-list a { .regulation-icon {
color: var(--primary-color); margin-bottom: 0;
text-decoration: none; font-size: 40px;
} }
.team-section .service-list a:hover { .regulation-name {
text-decoration: underline; text-align: left;
flex: 1;
}
.process-steps {
grid-template-columns: repeat(2, 1fr);
}
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.risk-categories, .regulation-doc-placeHolder {
.solution-grid { flex-direction: column;
min-height: 120px;
padding: 20px 24px;
}
.regulation-icon {
font-size: 36px;
}
.regulation-name {
font-size: 14px;
text-align: center;
}
.sponsor .card,
.sponsor .main-card {
padding: 20px 24px;
}
.sponsor .responsibility-overview {
grid-template-columns: 1fr;
}
.risk-categories {
grid-template-columns: 1fr;
}
.strategy-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.process-steps { .process-steps {
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: 16px;
} }
.team-grid { .process-step {
grid-template-columns: 1fr; padding: 20px 16px;
} }
} }

View File

@ -7,7 +7,7 @@ import './ClaimDetail.css'
function ClaimDetail() { function ClaimDetail() {
const { id } = useParams() const { id } = useParams()
const { user } = useAuth() const { user } = useAuth()
const isPolicyholder = user?.role === '投保人' const isPolicyHolder = user?.role === '投保人'
const _isInsurer = user?.role === '保险人' const _isInsurer = user?.role === '保险人'
void _isInsurer // 预留:保险人视图 void _isInsurer // 预留:保险人视图
@ -123,7 +123,7 @@ function ClaimDetail() {
<Link to="/dashboard/claims" className="btn btn-secondary"> <Link to="/dashboard/claims" className="btn btn-secondary">
</Link> </Link>
{isPolicyholder ? ( {isPolicyHolder ? (
<button className="btn btn-primary"> <button className="btn btn-primary">
</button> </button>

View File

@ -6,7 +6,7 @@ import './ClaimProgress.css'
function ClaimProgress() { function ClaimProgress() {
const { user } = useAuth() const { user } = useAuth()
const isPolicyholder = user?.role === '投保人' const isPolicyHolder = user?.role === '投保人'
const _isInsurer = user?.role === '保险人' const _isInsurer = user?.role === '保险人'
void _isInsurer // 预留:保险人视图 void _isInsurer // 预留:保险人视图
@ -56,7 +56,7 @@ function ClaimProgress() {
<div className="claim-progress-page"> <div className="claim-progress-page">
<PageHeader <PageHeader
title="理赔进度" title="理赔进度"
description={isPolicyholder ? "查看您的理赔申请进度" : "查看需要处理的理赔申请"} description={isPolicyHolder ? "查看您的理赔申请进度" : "查看需要处理的理赔申请"}
/> />
<div className="page-body"> <div className="page-body">
<div className="card main-card"> <div className="card main-card">
@ -93,7 +93,7 @@ function ClaimProgress() {
> >
</Link> </Link>
{isPolicyholder ? ( {isPolicyHolder ? (
<button className="btn btn-sm btn-secondary"> <button className="btn btn-sm btn-secondary">
</button> </button>

View File

@ -8,11 +8,11 @@ import './Dashboard.css'
function Dashboard() { function Dashboard() {
const { user } = useAuth() const { user } = useAuth()
const { openQuoteModal } = useQuoteModal() const { openQuoteModal } = useQuoteModal()
const isPolicyholder = user?.role === '投保人' const isPolicyHolder = user?.role === '投保人'
const _isInsurer = user?.role === '保险人' const _isInsurer = user?.role === '保险人'
// 投保人数据 // 投保人数据
const policyholderData = { const policyHolderData = {
inquiryProjects: 5, inquiryProjects: 5,
activeCoverage: 8, activeCoverage: 8,
allProjects: 15, allProjects: 15,
@ -33,7 +33,7 @@ function Dashboard() {
] ]
} }
const data = isPolicyholder ? policyholderData : _isInsurer ? insurerData : policyholderData const data = isPolicyHolder ? policyHolderData : _isInsurer ? insurerData : policyHolderData
return ( return (
<PageContainer> <PageContainer>
@ -54,7 +54,7 @@ function Dashboard() {
<span></span> <span></span>
</div> </div>
</div> </div>
{isPolicyholder ? ( {isPolicyHolder ? (
<Link to="/dashboard/projects" className="stat-link"> </Link> <Link to="/dashboard/projects" className="stat-link"> </Link>
) : ( ) : (
<Link to="/dashboard/inquiries" className="stat-link"> </Link> <Link to="/dashboard/inquiries" className="stat-link"> </Link>
@ -70,7 +70,7 @@ function Dashboard() {
<span></span> <span></span>
</div> </div>
</div> </div>
{isPolicyholder ? ( {isPolicyHolder ? (
<Link to="/dashboard/projects" className="stat-link"> </Link> <Link to="/dashboard/projects" className="stat-link"> </Link>
) : ( ) : (
<Link to="/dashboard/inquiries" className="stat-link"> </Link> <Link to="/dashboard/inquiries" className="stat-link"> </Link>
@ -86,7 +86,7 @@ function Dashboard() {
<span></span> <span></span>
</div> </div>
</div> </div>
{isPolicyholder ? ( {isPolicyHolder ? (
<Link to="/dashboard/projects" className="stat-link"> </Link> <Link to="/dashboard/projects" className="stat-link"> </Link>
) : ( ) : (
<Link to="/dashboard/inquiries" className="stat-link"> </Link> <Link to="/dashboard/inquiries" className="stat-link"> </Link>
@ -95,7 +95,7 @@ function Dashboard() {
</div> </div>
{/* 投保人快捷方式 */} {/* 投保人快捷方式 */}
{isPolicyholder && ( {isPolicyHolder && (
<section className="section"> <section className="section">
<div className="container"> <div className="container">
<div className="section-header"> <div className="section-header">

View File

@ -19,7 +19,7 @@
margin-bottom: 0; margin-bottom: 0;
} }
.placeholder-block { .placeHolder-block {
margin-top: var(--padding-lg, 24px); margin-top: var(--padding-lg, 24px);
padding: var(--padding-xl, 32px); padding: var(--padding-xl, 32px);
background: var(--bg-color, #f5f5f5); background: var(--bg-color, #f5f5f5);

View File

@ -20,7 +20,7 @@ function DrugSafetyQuery() {
</p> </p>
<div className="placeholder-block"> <div className="placeHolder-block">
<p></p> <p></p>
</div> </div>
</div> </div>

View File

@ -47,7 +47,7 @@ function ICFEditor() {
className="editor-textarea" className="editor-textarea"
value={content} value={content}
onChange={(e) => setContent(e.target.value)} onChange={(e) => setContent(e.target.value)}
placeholder="请粘贴或输入ICF内容..." placeHolder="请粘贴或输入ICF内容..."
rows={15} rows={15}
/> />
</div> </div>

View File

@ -75,7 +75,7 @@ function PremiumCalculator() {
id="premium-coverageAmount" id="premium-coverageAmount"
value={formData.coverageAmount} value={formData.coverageAmount}
onChange={(e) => handleChange('coverageAmount', e.target.value)} onChange={(e) => handleChange('coverageAmount', e.target.value)}
placeholder="请输入保障金额" placeHolder="请输入保障金额"
required required
/> />
</div> </div>
@ -87,7 +87,7 @@ function PremiumCalculator() {
id="premium-participantCount" id="premium-participantCount"
value={formData.participantCount} value={formData.participantCount}
onChange={(e) => handleChange('participantCount', e.target.value)} onChange={(e) => handleChange('participantCount', e.target.value)}
placeholder="请输入受试者人数" placeHolder="请输入受试者人数"
required required
/> />
</div> </div>
@ -99,7 +99,7 @@ function PremiumCalculator() {
id="premium-duration" id="premium-duration"
value={formData.duration} value={formData.duration}
onChange={(e) => handleChange('duration', e.target.value)} onChange={(e) => handleChange('duration', e.target.value)}
placeholder="请输入试验周期" placeHolder="请输入试验周期"
required required
/> />
</div> </div>

View File

@ -153,7 +153,7 @@ function ProjectQuotes() {
type="text" type="text"
value={formData.projectCode} value={formData.projectCode}
onChange={(e) => setFormData((d) => ({ ...d, projectCode: e.target.value }))} onChange={(e) => setFormData((d) => ({ ...d, projectCode: e.target.value }))}
placeholder="如CT-2025-001" placeHolder="如CT-2025-001"
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
@ -162,7 +162,7 @@ function ProjectQuotes() {
type="text" type="text"
value={formData.projectTitle} value={formData.projectTitle}
onChange={(e) => setFormData((d) => ({ ...d, projectTitle: e.target.value }))} onChange={(e) => setFormData((d) => ({ ...d, projectTitle: e.target.value }))}
placeholder="试验方案标题" placeHolder="试验方案标题"
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
@ -171,7 +171,7 @@ function ProjectQuotes() {
type="text" type="text"
value={formData.sponsor} value={formData.sponsor}
onChange={(e) => setFormData((d) => ({ ...d, sponsor: e.target.value }))} onChange={(e) => setFormData((d) => ({ ...d, sponsor: e.target.value }))}
placeholder="申办者名称" placeHolder="申办者名称"
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
@ -180,7 +180,7 @@ function ProjectQuotes() {
type="text" type="text"
value={formData.projectPhase} value={formData.projectPhase}
onChange={(e) => setFormData((d) => ({ ...d, projectPhase: e.target.value }))} onChange={(e) => setFormData((d) => ({ ...d, projectPhase: e.target.value }))}
placeholder="如I期、II期、III期" placeHolder="如I期、II期、III期"
/> />
</div> </div>
</div> </div>

View File

@ -91,7 +91,7 @@ function RiskScoring() {
id="riskScoring-participantCount" id="riskScoring-participantCount"
value={formData.participantCount} value={formData.participantCount}
onChange={(e) => handleChange('participantCount', e.target.value)} onChange={(e) => handleChange('participantCount', e.target.value)}
placeholder="请输入受试者人数" placeHolder="请输入受试者人数"
required required
/> />
</div> </div>

View File

@ -7,7 +7,7 @@ import './Tools.css'
function Tools() { function Tools() {
const { user } = useAuth() const { user } = useAuth()
const isPolicyholder = user?.role === '投保人' const isPolicyHolder = user?.role === '投保人'
const tools = [ const tools = [
{ {
@ -24,7 +24,7 @@ function Tools() {
description: '智能辅助修改知情同意书ICF内容', description: '智能辅助修改知情同意书ICF内容',
icon: '📝', icon: '📝',
path: '/dashboard/tools/icf-editor', path: '/dashboard/tools/icf-editor',
available: isPolicyholder // 仅投保人可见 available: isPolicyHolder // 仅投保人可见
}, },
{ {
id: 'risk-scoring', id: 'risk-scoring',
@ -32,7 +32,7 @@ function Tools() {
description: '对试验方案进行风险评分', description: '对试验方案进行风险评分',
icon: '📊', icon: '📊',
path: '/dashboard/tools/risk-scoring', path: '/dashboard/tools/risk-scoring',
available: isPolicyholder // 仅投保人可见 available: isPolicyHolder // 仅投保人可见
}, },
{ {
id: 'protocol-risk', id: 'protocol-risk',

View File

@ -27,11 +27,15 @@ import RmoMode from '@/views/RmoMode.vue'
import PostMarket from '@/views/PostMarket.vue' import PostMarket from '@/views/PostMarket.vue'
import KnowledgePVknowledge from '@/views/KnowledgePVknowledge.vue' import KnowledgePVknowledge from '@/views/KnowledgePVknowledge.vue'
import KnowledgeInsurance from '@/views/KnowledgeInsurance.vue' import KnowledgeInsurance from '@/views/KnowledgeInsurance.vue'
import KnowledgeInsuranceOverseas from '@/views/KnowledgeInsuranceOverseas.vue'
import KnowledgeInsuranceGroupStandard from '@/views/KnowledgeInsuranceGroupStandard.vue'
import KnowledgeInsuranceBasics from '@/views/KnowledgeInsuranceBasics.vue'
import KnowledgePvInsurance from '@/views/KnowledgePvInsurance.vue' import KnowledgePvInsurance from '@/views/KnowledgePvInsurance.vue'
import ResourceCenter from '@/views/ResourceCenter.vue' import ResourceCenter from '@/views/ResourceCenter.vue'
import Overseas from '@/views/Overseas.vue' import Overseas from '@/views/Overseas.vue'
import FAQ from '@/views/FAQ.vue' import FAQ from '@/views/FAQ.vue'
import LearningCenter from '@/views/LearningCenter.vue' import LearningCenter from '@/views/LearningCenter.vue'
import LearningCenterVideoDetail from '@/views/LearningCenterVideoDetail.vue'
import Contact from '@/views/Contact.vue' import Contact from '@/views/Contact.vue'
import PrivacyPolicy from '@/views/PrivacyPolicy.vue' import PrivacyPolicy from '@/views/PrivacyPolicy.vue'
import Login from '@/views/Login.vue' import Login from '@/views/Login.vue'
@ -69,9 +73,9 @@ const router = createRouter({
{ path: 'about/insurers', component: AboutInsurers }, { path: 'about/insurers', component: AboutInsurers },
{ path: 'about/brokers', component: AboutBrokers }, { path: 'about/brokers', component: AboutBrokers },
{ path: 'about/third-party', component: AboutThirdParty }, { path: 'about/third-party', component: AboutThirdParty },
{ path: 'concern', component: RiskDutiesOverview }, { path: 'RMO-About-Responsibility', component: RiskDutiesOverview },
{ path: 'sponsor', component: Sponsor }, { path: 'sponsor', component: Sponsor },
{ path: 'holder', component: Holder }, { path: 'Holder', component: Holder },
{ path: 'institution', component: Institution }, { path: 'institution', component: Institution },
{ path: 'service-provider', component: ServiceProvider }, { path: 'service-provider', component: ServiceProvider },
{ path: 'participant', component: Participant }, { path: 'participant', component: Participant },
@ -94,13 +98,14 @@ const router = createRouter({
{ path: 'knowledge/PVknowledge', component: KnowledgePVknowledge }, { path: 'knowledge/PVknowledge', component: KnowledgePVknowledge },
{ path: 'knowledge/ai-app', component: SmartAcquisition }, { path: 'knowledge/ai-app', component: SmartAcquisition },
{ path: 'knowledge/insurance', component: KnowledgeInsurance }, { path: 'knowledge/insurance', component: KnowledgeInsurance },
{ path: 'knowledge/insurance/PVknowledge', component: KnowledgePVknowledge }, { path: 'knowledge/insurance/PVknowledge', component: KnowledgeInsuranceBasics },
{ path: 'knowledge/insurance/overseas', component: Overseas }, { path: 'knowledge/insurance/overseas', component: KnowledgeInsuranceOverseas },
{ path: 'knowledge/insurance/group-standard', component: KnowledgeInsurance }, { path: 'knowledge/insurance/group-standard', component: KnowledgeInsuranceGroupStandard },
{ path: 'knowledge/pv-insurance', component: KnowledgePvInsurance }, { path: 'knowledge/pv-insurance', component: KnowledgePvInsurance },
{ path: 'knowledge/learning-center', component: LearningCenter }, { path: 'knowledge/learning-center', component: LearningCenter },
{ path: 'knowledge/learning-center/cases', component: LearningCenter }, { path: 'knowledge/learning-center/cases', component: LearningCenter },
{ path: 'knowledge/learning-center/videos', component: LearningCenter }, { path: 'knowledge/learning-center/videos', component: LearningCenter },
{ path: 'knowledge/learning-center/videos/:id', component: LearningCenterVideoDetail },
{ path: 'knowledge/learning-center/exam', component: LearningCenter }, { path: 'knowledge/learning-center/exam', component: LearningCenter },
{ path: 'system-management', component: ResourceCenter }, { path: 'system-management', component: ResourceCenter },
{ path: 'system-management/laws', component: ResourceCenter }, { path: 'system-management/laws', component: ResourceCenter },
@ -111,6 +116,7 @@ const router = createRouter({
{ path: 'faq/duty-logic', component: FAQ }, { path: 'faq/duty-logic', component: FAQ },
{ path: 'faq/coverage', component: FAQ }, { path: 'faq/coverage', component: FAQ },
{ path: 'faq/pv-insurance', component: FAQ }, { path: 'faq/pv-insurance', component: FAQ },
{ path: 'faq/ask', component: FAQ },
{ path: 'contact', component: Contact }, { path: 'contact', component: Contact },
{ path: 'privacy-policy', component: PrivacyPolicy }, { path: 'privacy-policy', component: PrivacyPolicy },
{ path: 'overseas', component: Overseas }, { path: 'overseas', component: Overseas },

View File

@ -38,9 +38,9 @@ export const useAuthStore = defineStore('auth', () => {
try { try {
await new Promise((r) => setTimeout(r, 500)) await new Promise((r) => setTimeout(r, 500))
const mockUsers: Record<string, { user: User; token: string }> = { const mockUsers: Record<string, { user: User; token: string }> = {
policyholder: { policyHolder: {
user: { id: '1', name: '投保人', email: 'policyholder@rmo.com', role: '投保人' }, user: { id: '1', name: '投保人', email: 'policyHolder@rmo.com', role: '投保人' },
token: 'mock_token_policyholder' token: 'mock_token_policyHolder'
}, },
insurer: { insurer: {
user: { id: '2', name: '保险人', email: 'insurer@rmo.com', role: '保险人' }, user: { id: '2', name: '保险人', email: 'insurer@rmo.com', role: '保险人' },

View File

@ -48,7 +48,7 @@ export const useRegistrationStore = defineStore('registration', () => {
{ id: 'org5', name: '太平洋财产保险股份有限公司' } { id: 'org5', name: '太平洋财产保险股份有限公司' }
]) ])
const reservedEmails = ['admin@rmo.com', 'policyholder@rmo.com', 'insurer@rmo.com', 'tpa@vdano.com'] const reservedEmails = ['admin@rmo.com', 'policyHolder@rmo.com', 'insurer@rmo.com', 'tpa@vdano.com']
function getPendingRegistrations(): PendingRegistration[] { function getPendingRegistrations(): PendingRegistration[] {
return loadRegistrations().filter(r => r.status === 'pending') return loadRegistrations().filter(r => r.status === 'pending')

View File

@ -156,7 +156,7 @@
<!-- 使用场景 --> <!-- 使用场景 -->
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<h2 class="section-title">使用场景</h2> <h2 class="section-title">用场景</h2>
<div class="scenarios-grid"> <div class="scenarios-grid">
<div class="scenario-item"> <div class="scenario-item">
<h4>临床试验阶段</h4> <h4>临床试验阶段</h4>

View File

@ -8,44 +8,12 @@
<div class="contact-grid"> <div class="contact-grid">
<div class="contact-card"> <div class="contact-card">
<h3>客户服务中心</h3> <h3>客户服务中心</h3>
<p>邮箱clientservice@rmo.com</p> <p>邮箱rmo@vdano.com</p>
<p>电话400-XXX-XXXX</p> <p>电话4009-606-520</p>
</div>
<div class="contact-card">
<h3>业务咨询/RFP</h3>
<p>邮箱marketing@rmo.com</p>
<p>电话400-XXX-XXXX</p>
</div>
<div class="contact-card">
<h3>媒体与投资者咨询</h3>
<p>邮箱PR@rmo.com</p>
<p>电话400-XXX-XXXX</p>
</div>
<div class="contact-card">
<h3>合规疑虑</h3>
<p>邮箱compliance@rmo.com</p>
<p>电话400-XXX-XXXX</p>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<section class="section">
<div class="container">
<h2 class="section-title">订阅我们的通讯</h2>
<p class="section-description">马上注册RMO通讯简报第一时间获取我们的企业动态白皮书行业报告</p>
<form class="newsletter-form" @submit.prevent>
<div class="form-group">
<input type="email" placeholder="请输入您的邮箱地址" class="form-input" required />
<button type="submit" class="btn btn-primary">订阅</button>
</div>
<p class="form-note">
RMO承诺对您在本网页下提供的任何信息包括您的个人信息将按照相关的法律法规及RMO
<RouterLink to="/privacy-policy" class="privacy-link">隐私政策</RouterLink>
的规定进行严格保密
</p>
</form>
</div>
</section>
</div> </div>
</div> </div>
</PageContainer> </PageContainer>

View File

@ -1,12 +1,67 @@
<template> <template>
<PageContainer> <PageContainer>
<div class="faq"> <div class="faq">
<PageHeader title="常见问题" description="FAQ列表与问题解答" /> <PageHeader :title="pageTitle" :description="pageDescription" />
<div class="page-body"> <div class="page-body">
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<div class="card main-card"> <div class="card main-card">
<h2>保险相关问题</h2> <!-- 职责逻辑页 -->
<template v-if="route.path === '/faq/duty-logic'">
<h2>申办者与持有人职责</h2>
<div class="faq-list">
<div class="faq-item">
<h3>Q: 申办者/持有人对受试者/患者有哪些职责</h3>
<p>A: 申办者承担受试者保护的主要责任包括提供法律上经济上的保险或保证建立完善的风险管理体系确保临床试验的质量和安全持有人则负责上市后药物安全建立药物警戒体系收集评价和报告药品不良反应制定并实施药品风险管理计划</p>
</div>
<div class="faq-item">
<h3>Q: 申办者为何必须为受试者购买保险或提供保证</h3>
<p>A: 根据药物临床试验质量管理规范GCP药物临床试验知情同意书伦理审查技术指导原则申办者应提供法律上经济上的保险或保证以承担因试验导致的损害救治与赔偿这是保障受试者权益分散试验风险的法定要求</p>
</div>
<div class="faq-item">
<h3>Q: 知情同意书中关于补偿的表述有何要求</h3>
<p>A: 知情同意书应明确说明补偿方式数额和计划不得含有免除申办者责任的内容格式条款应作有利于受试者的解释申办方需就该条重点说明且不得隐瞒除外责任</p>
</div>
</div>
<h2>研究中心与研究者职责</h2>
<div class="faq-list">
<div class="faq-item">
<h3>Q: 研究中心在风险管理中承担什么角色</h3>
<p>A: 研究中心是风险管理第一线直面患者安全其职责包括协助申办者开展风险管理研究者负责临床试验的执行与受试者管理伦理委员会负责伦理审查及时报告不良事件与SAE配合申办者进行安全性评估</p>
</div>
<div class="faq-item">
<h3>Q: 医院/研究者需要对受试者损害承担赔偿责任吗</h3>
<p>A: 根据民法典第925条等规定知情同意书直接约束申办者与受试者临床试验引起的赔偿或合同责任通常由申办者承担医院作为受托方一般不承担连带责任但若医院存在医疗过错或违反操作规程则可能承担相应责任</p>
</div>
</div>
<h2>受试者权益与责任</h2>
<div class="faq-list">
<div class="faq-item">
<h3>Q: 受试者作为最直接利益相关方有哪些责任</h3>
<p>A: 受试者应为自身安全与权益负责包括充分阅读并理解知情同意书内容如实告知病史与用药情况按方案配合随访与检查出现不适及时告知研究者了解退出权利及补偿与保险相关条款</p>
</div>
<div class="faq-item">
<h3>Q: 受试者使用安慰剂发生损害申办者是否仍需赔偿</h3>
<p>A: 司法实践中即使破盲显示受试者使用安慰剂损害与试验药物无直接因果关系法院仍可能基于知情同意书中的补偿承诺格式条款解释原则等酌定申办者承担一定比例的补偿责任具体需结合案情与合同条款判断</p>
</div>
</div>
<h2>RMO与风险管理职责</h2>
<div class="faq-list">
<div class="faq-item">
<h3>Q: RMO组织在职责体系中扮演什么角色</h3>
<p>A: RMO是风险管理方案与能力的提供者协助申办者持有人等履行风险管理职责其服务包括职责梳理方案设计风险评估核保谈判事件管理定损赔付风险化解等帮助各方建立并运维风险管理体系</p>
</div>
<div class="faq-item">
<h3>Q: 各方职责不清时如何厘清责任主体</h3>
<p>A: 在缺少体系的情况下所有人都负责可能导致无人负责建议通过合同约定知情同意书条款委托关系等明确各方职责边界RMO可提供职责梳理与方案设计服务帮助申办者研究中心等厘清责任主体与衔接机制</p>
</div>
</div>
<FaqAskLink />
</template>
<!-- 保障范围页 -->
<template v-else-if="route.path === '/faq/coverage'">
<h2>保险保障范围</h2>
<div class="faq-list"> <div class="faq-list">
<div class="faq-item"> <div class="faq-item">
<h3>Q: 保险保障范围包括哪些</h3> <h3>Q: 保险保障范围包括哪些</h3>
@ -51,6 +106,60 @@
<p>A: 您可以通过沟通群或电话咨询案件处理进展我们会及时向您通报案件处理状态</p> <p>A: 您可以通过沟通群或电话咨询案件处理进展我们会及时向您通报案件处理状态</p>
</div> </div>
</div> </div>
<FaqAskLink />
</template>
<!-- PV与保险页 -->
<template v-else-if="route.path === '/faq/pv-insurance'">
<h2>PV与保险</h2>
<div class="faq-list">
<div class="faq-item">
<h3>Q: PV 与保险的关系是什么</h3>
<p>A: 药物警戒PV负责识别评价和控制药品风险保险则提供经济保障以覆盖潜在赔偿二者协同PV发现的风险信息影响保险方案设计与定价保险理赔需结合PV对因果关系的评估风险管理体系将PV与保险纳入统一框架</p>
</div>
<div class="faq-item">
<h3>Q: SAE 的开始时间与治疗费用理算有何关系</h3>
<p>A: 保险通常只支付 SAE 开始后的治疗费用SAE 何时开始需要由药物警戒团队定义一般建议以<strong>症状开始时间</strong>作为 SAE 的开始时间以便合理界定可赔付的治疗费用范围</p>
</div>
<div class="faq-item">
<h3>Q: 如何确定为几个事件比如白细胞降低中性粒细胞降低是否是一个事件</h3>
<p>A: 不同报告术语 AESAESUSAR与保险理赔中的一次事故并非一一对应判断为几个事件时需综合考虑<strong>共同病理机制</strong>如白细胞降低与中性粒细胞降低均源于骨髓抑制可视为同一病理过程的不同表现<strong>时间关联性</strong><strong>MedDRA 术语层级</strong>如中性粒细胞减少症是白细胞减少症的下位词以及临床因果关系若多个表现源于同一病因具有密切关联通常可合并为一个事件进行理赔反之则可能计为多个事件具体认定应由药物警戒团队结合方案研究者判断及行业共识作出并建议在试验启动前与保司就一次事故的界定达成一致</p>
</div>
</div>
<FaqAskLink />
</template>
<!-- 发起提问页 -->
<template v-else-if="route.path === '/faq/ask'">
<FaqAskForm />
</template>
<!-- 默认常见问题页索引 -->
<template v-else>
<p class="faq-index-intro">请选择您关心的话题或发起新提问</p>
<div class="faq-nav-cards">
<RouterLink to="/faq/duty-logic" class="faq-nav-card">
<span class="faq-nav-icon">📋</span>
<h3>职责逻辑</h3>
<p>申办者持有人研究中心受试者及RMO各方的职责与责任界定</p>
</RouterLink>
<RouterLink to="/faq/coverage" class="faq-nav-card">
<span class="faq-nav-icon">🛡</span>
<h3>保障范围</h3>
<p>保险保障理赔流程保证方案及服务流程</p>
</RouterLink>
<RouterLink to="/faq/pv-insurance" class="faq-nav-card">
<span class="faq-nav-icon">🔬</span>
<h3>PV与保险</h3>
<p>药物警戒与保险的关系及协同</p>
</RouterLink>
</div>
<div class="faq-ask-section">
<h2>未找到答案</h2>
<p>您可以通过发起提问向我们咨询我们会尽快回复</p>
<RouterLink to="/faq/ask" class="faq-ask-btn">发起提问</RouterLink>
</div>
</template>
</div> </div>
</div> </div>
</section> </section>
@ -60,8 +169,32 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue' import PageHeader from '@/components/PageHeader.vue'
import FaqAskLink from '@/components/FaqAskLink.vue'
import FaqAskForm from '@/components/FaqAskForm.vue'
const route = useRoute()
const pageConfig: Record<string, { title: string; description: string }> = {
'/faq/duty-logic': { title: '职责逻辑', description: '申办者、持有人、研究中心、受试者及RMO各方的职责与责任界定' },
'/faq/coverage': { title: '保障范围', description: '保险与保证方案的保障范围相关问题' },
'/faq/pv-insurance': { title: 'PV与保险', description: '药物警戒与保险的关系及协同' },
'/faq/ask': { title: '发起提问', description: '向我们提交您的问题,我们会尽快回复' },
default: { title: '常见问题', description: '请选择您关心的话题,或发起新提问' }
}
const pageTitle = computed(() => {
const cfg = pageConfig[route.path] ?? pageConfig.default
return cfg.title
})
const pageDescription = computed(() => {
const cfg = pageConfig[route.path] ?? pageConfig.default
return cfg.description
})
</script> </script>
<style scoped> <style scoped>

View File

@ -1,7 +1,7 @@
<template> <template>
<PageContainer> <PageContainer>
<div class="holder"> <div class="holder">
<PageHeader title="持有人职责" description="负责上市后药物安全" /> <PageHeader title="持有人职责" description="MAH Holder 上市后药物安全与风险管理" />
<div class="page-body"> <div class="page-body">
<section class="section"> <section class="section">
<div class="container"> <div class="container">
@ -16,28 +16,80 @@
</div> </div>
</div> </div>
</section> </section>
<section class="section duty-summary-section">
<div class="container">
<h2 class="section-title">安全性数据的收集与递交</h2>
<div class="duty-layout">
<div class="regulation-panel">
<div class="regulation-doc">
<div class="regulation-doc-placeholder">
<span class="regulation-icon">📄</span>
<span class="regulation-name">药物警戒质量管理规范GVP</span>
</div>
</div>
</div>
<div class="duty-content">
<div class="duty-summary-card">
<p>持有人应在药品全生命周期内<strong>收集评价和递交</strong>安全性数据履行药物警戒主体责任</p>
<ul>
<li>建立信息收集途径主动开展药品上市后监测收集疑似药品不良反应及其他与用药有关的有害反应</li>
<li>取得首个药品批准证明文件后30日内在国家药品不良反应监测系统中完成信息注册</li>
<li>按规定时限提交<strong>定期安全性更新报告</strong>PSUR创新药和改良型新药每满1年提交一次直至首次再注册之后每5年报告一次</li>
<li>通过国家药品不良反应监测系统递交报告由药物警戒负责人批准同意后提交</li>
<li>确保数据的可靠性可追溯性和完整性建立数据治理责任</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section duty-summary-section">
<div class="container">
<h2 class="section-title">风险管理体系建立与运维</h2>
<div class="duty-layout">
<div class="regulation-panel">
<div class="regulation-doc">
<div class="regulation-doc-placeholder">
<span class="regulation-icon">📄</span>
<span class="regulation-name">药物警戒质量管理规范GVP组织与人员</span>
</div>
</div>
</div>
<div class="duty-content">
<div class="duty-summary-card">
<p>持有人应建立并持续运维基于风险的<strong>药物警戒与风险管理体系</strong></p>
<ul>
<li><strong>药物安全委员会的建立与维护</strong>设立药品安全委员会负责药品安全相关重大事项的审议与决策是风险管理体系的核心组织保障</li>
<li>建立专门的药物警戒部门配备具有医学药学或相关专业背景的专职人员</li>
<li>指定具有三年以上相关工作经历的<strong>药物警戒负责人</strong>负责药物警戒活动的全面管理</li>
<li>建立质量保证系统制定药物警戒质量目标和控制指标定期开展内部审核</li>
<li>对所有参与药物警戒活动的人员进行培训确保体系有效运行</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<h2 class="section-title">风险管理策略</h2>
<div class="card main-card"> <div class="card main-card">
<h2>持有人核心职责</h2> <div class="strategy-grid">
<div class="content-section"> <div class="strategy-card">
<h3>上市后药物安全</h3> <h4>自留策略</h4>
<p>药品上市许可持有人MAH是药品安全的责任主体对药品全生命周期质量安全承担主要责任持有人需建立并持续完善上市后药物安全监测与风险管理体系确保药品上市后的安全使用</p> <p>对于可接受的上市后风险采用自留方式管理</p>
<h3>持有人主要职责</h3> </div>
<ul> <div class="strategy-card">
<li> 建立药物警戒体系开展上市后安全性监测</li> <h4>保险策略</h4>
<li> 收集评价和报告药品不良反应及其他与用药有关的有害反应</li> <p>对于首要风险通过保险进行风险转移保障上市后责任</p>
<li> 开展药品上市后研究持续评估药品风险与获益</li> </div>
<li> 制定并实施药品风险管理计划及时采取风险控制措施</li> <div class="strategy-card">
<li> 按规定报告处置药品质量问题和召回等</li> <h4>保证策略</h4>
</ul> <p>向相关方提供法律上经济上的保证与风险性质和程度相适应</p>
<h3>RMO模式如何支持持有人</h3> </div>
<ul>
<li> 协助建立和完善药物警戒与风险管理体系</li>
<li> 提供上市后安全监测与报告的专业支持</li>
<li> 协助开展风险识别评估与沟通</li>
<li> 提供保险与保证方案转移上市后责任风险</li>
</ul>
</div> </div>
</div> </div>
</div> </div>

View File

@ -16,20 +16,104 @@
</div> </div>
</div> </div>
</section> </section>
<section class="section duty-summary-section">
<div class="container">
<h2 class="section-title">安全性监测与不良事件报告</h2>
<div class="duty-layout">
<div class="regulation-panel">
<div class="regulation-doc">
<div class="regulation-doc-placeholder">
<span class="regulation-icon">📄</span>
<span class="regulation-name">药物临床试验质量管理规范GCP不良事件报告</span>
</div>
</div>
</div>
<div class="duty-content">
<div class="duty-summary-card">
<p>试验机构与研究者应建立<strong>受试者安全监测和报告机制</strong>确保不良事件得到及时识别与处置</p>
<ul>
<li>建立完善的安全监测体系及时识别记录和报告不良事件及严重不良事件</li>
<li>在具备完善医疗设施和紧急救治能力的机构进行试验保障医疗救治的及时性和有效性</li>
<li>收到申办者提供的SUSAR等严重安全性信息后及时告知受试者并采取必要措施</li>
<li>配合申办者进行不良事件调查与评估按规定向伦理委员会申办者及监管部门报告</li>
<li>建立风险预警和应急响应机制配合RMO模式的风险管理服务</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section duty-summary-section">
<div class="container">
<h2 class="section-title">伦理审查与受试者保护</h2>
<div class="duty-layout">
<div class="regulation-panel">
<div class="regulation-doc">
<div class="regulation-doc-placeholder">
<span class="regulation-icon">📄</span>
<span class="regulation-name">药物临床试验伦理审查工作指导原则</span>
</div>
</div>
</div>
<div class="duty-content">
<div class="duty-summary-card">
<p>伦理委员会应独立开展<strong>伦理审查</strong>保障受试者权益与安全</p>
<ul>
<li>由医药专业人员非医药专业人员法律专家及独立人员组成至少5人且性别均衡向药品监管部门备案</li>
<li>审查研究方案设计风险受益比受试者招募知情同意书弱势群体保护等</li>
<li>批准/不批准临床试验跟踪审查已批准试验必要时终止或暂停试验</li>
<li>接收严重不良事件报告需要时召开紧急会议保护受试者</li>
<li>组织和工作不受任何参与试验者影响确保审查的独立性与公正性</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section duty-summary-section">
<div class="container">
<h2 class="section-title">机构管理体系与质量控制</h2>
<div class="duty-layout">
<div class="regulation-panel">
<div class="regulation-doc">
<div class="regulation-doc-placeholder">
<span class="regulation-icon">📄</span>
<span class="regulation-name">药物临床试验质量管理规范GCP临床试验机构</span>
</div>
</div>
</div>
<div class="duty-content">
<div class="duty-summary-card">
<p>试验机构应建立<strong>内部管理与质量控制体系</strong>保障临床试验规范开展</p>
<ul>
<li>设立相应的内部管理部门承担临床试验管理工作</li>
<li>建立伦理委员会并配置必要的条件和支持</li>
<li>建立信息化系统管理电子病历和临床试验电子数据</li>
<li>研究者应熟悉试验方案内容严格按方案执行具备行医资格和专业知识</li>
<li>向受试者充分说明试验详情获取知情同意保证充分时间完成试验</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<h2 class="section-title">风险管理策略</h2>
<div class="card main-card"> <div class="card main-card">
<h2>试验机构关注要点</h2> <div class="strategy-grid strategy-grid--2">
<div class="content-section"> <div class="strategy-card">
<h3>与受试者安全相关的内容</h3> <h4>监督研究者</h4>
<ul> <p>机构对研究者执行试验方案知情同意不良事件报告等进行监督确保合规开展</p>
<li> 受试者安全监测和报告机制</li> </div>
<li> 不良事件的及时识别和处理</li> <div class="strategy-card">
<li> 医疗救治的及时性和有效性</li> <h4>监督申办者</h4>
<li> 风险预警和应急响应</li> <p>机构对申办者提供的方案保险/保证监查等履行情况进行监督保障受试者权益</p>
</ul> </div>
<h3>机构职责说明</h3>
<p>试验机构作为临床试验的执行主体需要协助申办者进行风险管理确保试验过程中受试者的安全机构应建立完善的安全监测体系及时报告不良事件配合RMO模式的风险管理服务</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,23 +1,29 @@
<template> <template>
<PageContainer> <PageContainer>
<div class="knowledge-page"> <div class="knowledge-page insurance-overview">
<PageHeader title="保险知识" description="保险方案设计与风险管理知识" /> <PageHeader title="保险知识" description="临床试验责任保险与药品责任保险相关知识" />
<div class="page-body"> <div class="page-body">
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<p class="overview-intro">
本栏目整合保险基础知识国内外比较与团体条款标准帮助投保人理解保险本质保障范围与行业规范
</p>
<div class="knowledge-content"> <div class="knowledge-content">
<div class="knowledge-card"> <RouterLink to="/knowledge/insurance/PVknowledge" class="knowledge-card">
<h3>保险方案设计</h3> <span class="card-icon">📋</span>
<p>了解如何设计适合临床试验的保险方案包括基础保障和全面保障</p> <h3>基础知识</h3>
</div> <p>投保人需要了解的责任保险与财产险基础涵盖保险本质与分类风险与保障保费与投保流程含常见问题入口与参考资料</p>
<div class="knowledge-card"> </RouterLink>
<h3>风险管理</h3> <RouterLink to="/knowledge/insurance/overseas" class="knowledge-card">
<p>学习风险管理的最佳实践降低临床试验风险</p> <span class="card-icon">🌐</span>
</div> <h3>国外比较</h3>
<div class="knowledge-card"> <p>国内外临床试验责任保险的差异总结包括基本逻辑保障范围理赔情况等便于了解行业国际实践</p>
<h3>理赔流程</h3> </RouterLink>
<p>了解保险理赔的流程和要求确保及时获得保障</p> <RouterLink to="/knowledge/insurance/group-standard" class="knowledge-card">
</div> <span class="card-icon">📜</span>
<h3>条款标准</h3>
<p>T/GDCCA 药品责任保险条款及服务规范团体标准涵盖临床试验责任保险与持有人责任保险的保障要求与服务实施规范</p>
</RouterLink>
</div> </div>
</div> </div>
</section> </section>
@ -33,4 +39,32 @@ import PageHeader from '@/components/PageHeader.vue'
<style scoped> <style scoped>
@import '@/pages/KnowledgeBase.css'; @import '@/pages/KnowledgeBase.css';
.insurance-overview .overview-intro {
font-size: 1rem;
line-height: 1.7;
color: var(--text-color);
margin: 0 0 1.5rem 0;
}
.insurance-overview .knowledge-card {
display: block;
text-decoration: none;
position: relative;
}
.insurance-overview .knowledge-card:hover h3 {
color: var(--brand-primary);
}
.card-icon {
display: block;
font-size: 1.5rem;
margin-bottom: 0.75rem;
}
.insurance-overview .knowledge-card p {
font-size: 0.9375rem;
color: var(--text-light);
}
</style> </style>

View File

@ -0,0 +1,516 @@
<template>
<PageContainer>
<div class="knowledge-page insurance-basics">
<PageHeader title="保险基础知识" description="投保人需要知道的临床试验责任保险与财产险基础" />
<div class="page-body">
<section class="section">
<div class="container">
<!-- 1. 概述幻灯片展示 -->
<div class="overview-section">
<h2 class="section-title">概述</h2>
<p class="section-intro overview-intro">
临床试验保险归根到底是保障受试者安全与救治2020 GCP 强调申办者须提供法律与经济上的保障投保人需要清楚保险的本质保障范围与责任边界才能买得明明白白
</p>
<div class="knowledge-carousel-container">
<div class="knowledge-carousel-wrapper">
<div
class="knowledge-carousel-track"
:style="{ transform: `translateX(-${currentSlide * 100}%)` }"
>
<div
v-for="(slide, index) in slides"
:key="index"
class="knowledge-carousel-slide"
>
<div class="slide-card">
<h3 class="slide-title">{{ slide.title }}</h3>
<div class="slide-body" v-html="slide.content"></div>
</div>
</div>
</div>
</div>
<div class="carousel-controls">
<button
type="button"
class="carousel-btn carousel-btn-prev"
aria-label="上一张"
@click="prevSlide"
/>
<div class="carousel-indicators">
<button
v-for="(_, index) in slides"
:key="index"
type="button"
:class="['carousel-indicator', { active: index === currentSlide }]"
:aria-label="`第${index + 1}张`"
@click="currentSlide = index"
/>
</div>
<button
type="button"
class="carousel-btn carousel-btn-next"
aria-label="下一张"
@click="nextSlide"
/>
</div>
</div>
</div>
<!-- 2. 常见问题仅问题列表链接到常见问题页面 -->
<div class="faq-section">
<h2 class="section-title">常见问题</h2>
<p class="section-intro">以下问题可在常见问题页面查看详细解答</p>
<div class="section-block">
<ul class="faq-question-list">
<li v-for="(item, i) in faqQuestions" :key="i">
<RouterLink :to="item.to" class="faq-link">{{ item.question }}</RouterLink>
</li>
</ul>
</div>
</div>
<!-- 3. 参考资料文件名列表链接到文件管理系统 -->
<div class="references-section">
<h2 class="section-title">参考资料</h2>
<p class="section-intro">
以下资料来自资源中心保险知识 / 基础知识目录查阅完整文件请前往
<RouterLink to="/system-management" class="ref-link">文件管理系统</RouterLink>
</p>
<div class="section-block">
<ul class="reference-file-list">
<li v-for="(file, i) in referenceFiles" :key="i">
<RouterLink to="/system-management" class="file-link">{{ file.name }}</RouterLink>
</li>
</ul>
</div>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
const currentSlide = ref(0)
const slides = [
{
title: '一、保险的本质(法律定义)',
content: `
<p class="slide-para">根据中华人民共和国保险法保险是投保人根据合同约定向保险人支付保险费保险人对合同约定的可能发生的事故因其发生所造成的<strong>财产损失</strong>承担赔偿保险金责任或者当被保险人死亡伤残疾病或达到合同约定条件时承担给付保险金责任的<strong>商业保险行为</strong></p>
`,
},
{
title: '一、保险的分类',
content: `
<ul class="slide-list">
<li><strong>按保险标的</strong>财产保险 + 人身保险</li>
<li><strong>临床试验责任险</strong>属于责任保险主险保障申办方针对受试者因药械临床产品造成的 AE SAE 所承担的法定经济赔偿责任</li>
</ul>
`,
},
{
title: '一、保险特性',
content: `
<ul class="slide-list">
<li><strong>互助性</strong>一人为众众人为一</li>
<li><strong>法律性</strong>一种合同行为</li>
<li><strong>经济性</strong>一种经济保障活动</li>
<li><strong>商品性</strong>一种等价交换的经济关系</li>
<li><strong>科学性</strong>处理风险的有效措施</li>
</ul>
`,
},
{
title: '二、风险与对应保障(一)',
content: `
<p class="slide-para">临床试验存在多种风险各风险需有对应的保险或保证方案</p>
<ul class="slide-list">
<li><strong>试验药物</strong>固有属性质量问题 临床试验责任险主险扩展非预期不良事件责任险扩展预期不良反应责任险</li>
<li><strong>常规医疗行为</strong>非方案规定的医疗行为 临床试验医疗责任险</li>
<li><strong>精神损害</strong> 临床试验精神损害赔偿责任险</li>
</ul>
`,
},
{
title: '二、风险与对应保障(二)',
content: `
<ul class="slide-list">
<li><strong>意外</strong>意外事故意外怀孕等 临床试验意外伤害责任险扩展流产费用险</li>
<li><strong>项目执行</strong>研究者/SMO/CRC 非蓄意方案违背 研究者行为责任险研究团队成员行为责任险</li>
<li><strong>试验方案</strong>方案设计缺陷流程规定的操作/检查非试验药物 扩展静脉血采集/皮下注射责任险扩展实验室检测责任险非临床试验药品不良反应责任险</li>
</ul>
`,
},
{
title: '三、保障不全与主附险关系',
content: `
<ul class="slide-list">
<li><strong>保障不全</strong>多数保险未能覆盖试验操作不规范申办者/CRO/SMO 过失非受试药物伤害医疗责任等风险投保时需看清保障范围</li>
<li><strong>主险与附加险</strong>主险覆盖 SUSAR附加险 AE 补偿覆盖非 SUSAR 不良事件二者不重复赔偿</li>
</ul>
`,
},
{
title: '三、保费与投保流程',
content: `
<ul class="slide-list">
<li><strong>保费影响因素</strong>附加险类型与保障范围基础保费参保人数风险系数试验药物分类不良反应发生率与严重程度剂型用法等</li>
<li><strong>投保流程</strong>申办方与保险/经纪公司沟通 签署保密协议 提供研究者手册方案知情同意书等 评估并报价 确定方案 填写保单并支付保费 保单生效</li>
</ul>
`,
},
]
function prevSlide() {
currentSlide.value = currentSlide.value === 0 ? slides.length - 1 : currentSlide.value - 1
}
function nextSlide() {
currentSlide.value = currentSlide.value === slides.length - 1 ? 0 : currentSlide.value + 1
}
const faqQuestions = [
{ question: '保险保障范围包括哪些?', to: '/faq/coverage' },
{ question: '如何申请理赔?', to: '/faq' },
{ question: '理赔申请需要多长时间?', to: '/faq' },
{ question: '专项风险管理基金如何设立?', to: '/faq' },
{ question: '风险减量服务包括哪些内容?', to: '/faq' },
{ question: '外溢风险管理服务的响应时间是多少?', to: '/faq' },
{ question: '申办者/持有人对受试者/患者有哪些职责?', to: '/faq/duty-logic' },
{ question: 'PV 与保险的关系是什么?', to: '/faq/pv-insurance' },
]
const referenceFiles = [
{ name: '临床试验保险基础知识.pptx' },
{ name: 'subject vs participant.pdf' },
]
</script>
<style scoped>
@import '@/pages/KnowledgeBase.css';
.insurance-basics .page-body {
padding-bottom: 3rem;
}
.insurance-basics .overview-section,
.insurance-basics .faq-section,
.insurance-basics .references-section {
margin-bottom: 2.5rem;
}
.insurance-basics .overview-section:last-of-type,
.insurance-basics .faq-section:last-of-type,
.insurance-basics .references-section:last-of-type {
margin-bottom: 0;
}
.insurance-basics .section-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--text-color);
margin: 0 0 1.25rem 0;
padding-bottom: 0.75rem;
border-bottom: 2px solid var(--brand-primary);
letter-spacing: 0.02em;
}
.insurance-basics .section-intro {
font-size: 0.9375rem;
color: var(--text-light);
margin: 0 0 1.25rem 0;
line-height: 1.65;
}
.insurance-basics .overview-intro {
margin-bottom: 1.5rem;
}
/* 幻灯片轮播 */
.knowledge-carousel-container {
position: relative;
width: 100%;
margin-top: 8px;
}
.knowledge-carousel-wrapper {
position: relative;
width: 100%;
overflow: hidden;
border-radius: 12px;
}
.knowledge-carousel-track {
display: flex;
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform;
}
.knowledge-carousel-slide {
min-width: 100%;
width: 100%;
flex-shrink: 0;
padding: 0 4px;
box-sizing: border-box;
}
.slide-card {
background: linear-gradient(135deg, rgba(14, 165, 233, 0.04) 0%, var(--white) 100%);
border: 1px solid var(--border-color);
border-left: 4px solid var(--brand-primary);
border-radius: 12px;
padding: 1.75rem 2rem;
min-height: 220px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.slide-title {
font-size: 1.0625rem;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 1rem 0;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--border-color);
}
.slide-body :deep(.slide-para) {
font-size: 0.9375rem;
line-height: 1.75;
color: var(--text-color);
margin: 0 0 0.75rem 0;
}
.slide-body :deep(.slide-list) {
list-style: none;
padding: 0;
margin: 0;
}
.slide-body :deep(.slide-list li) {
font-size: 0.9375rem;
line-height: 1.7;
color: var(--text-color);
padding: 0.4rem 0 0.4rem 1.25rem;
position: relative;
}
.slide-body :deep(.slide-list li::before) {
content: '';
position: absolute;
left: 0;
top: 0.85em;
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--brand-primary);
}
/* 轮播控制 */
.carousel-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
margin-top: 20px;
}
.carousel-btn {
position: relative;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 8px;
width: 44px;
height: 44px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.25s ease;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
color: var(--brand-primary);
}
.carousel-btn:hover {
border-color: var(--brand-primary);
background: var(--brand-primary);
color: var(--white);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.25);
}
.carousel-btn-prev::before {
content: '←';
font-size: 18px;
font-weight: 600;
color: inherit;
}
.carousel-btn-next::before {
content: '→';
font-size: 18px;
font-weight: 600;
color: inherit;
}
.carousel-indicators {
display: flex;
justify-content: center;
gap: 8px;
flex-wrap: wrap;
}
.carousel-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px solid var(--brand-primary);
background: transparent;
cursor: pointer;
transition: all 0.25s ease;
padding: 0;
}
.carousel-indicator:hover {
background: rgba(14, 165, 233, 0.3);
}
.carousel-indicator.active {
background: var(--brand-primary);
width: 24px;
border-radius: 5px;
}
/* 常见问题 */
.insurance-basics .faq-section .section-block {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.25rem 1.75rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.faq-question-list {
list-style: none;
padding: 0;
margin: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 0.5rem 2rem;
}
.faq-question-list li {
padding: 0.5rem 0;
border-bottom: none;
}
.faq-link {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9375rem;
color: var(--text-color);
text-decoration: none;
padding: 0.35rem 0;
transition: color 0.2s;
}
.faq-link::before {
content: '→';
color: var(--brand-primary);
font-size: 0.875rem;
font-weight: 600;
}
.faq-link:hover {
color: var(--brand-primary);
}
/* 参考资料 */
.insurance-basics .references-section .section-block {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.25rem 1.75rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.reference-file-list {
list-style: none;
padding: 0;
margin: 0;
}
.reference-file-list li {
padding: 0.65rem 1rem;
margin-bottom: 0.5rem;
border-radius: 8px;
background: var(--bg-color, #f8fafc);
border: 1px solid transparent;
transition: border-color 0.2s, background 0.2s;
}
.reference-file-list li:last-child {
margin-bottom: 0;
}
.reference-file-list li:hover {
background: rgba(14, 165, 233, 0.06);
border-color: rgba(14, 165, 233, 0.2);
}
.file-link {
font-size: 0.9375rem;
color: var(--brand-primary);
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.file-link::before {
content: '';
font-size: 1.125rem;
font-weight: 600;
color: var(--brand-primary);
}
.file-link:hover {
text-decoration: underline;
}
.ref-link {
color: var(--brand-primary);
font-weight: 500;
text-decoration: none;
}
.ref-link:hover {
text-decoration: underline;
}
@media (max-width: 768px) {
.slide-card {
padding: 1.25rem 1.25rem;
min-height: 200px;
}
.carousel-controls {
gap: 14px;
margin-top: 16px;
}
.carousel-btn {
width: 40px;
height: 40px;
}
.faq-question-list {
grid-template-columns: 1fr;
}
}
</style>

View File

@ -0,0 +1,418 @@
<template>
<PageContainer>
<div class="knowledge-page group-standard">
<PageHeader
title="团体条款标准"
description="药品责任保险条款及服务规范T/GDCCA 2063 团体标准)"
/>
<div class="page-body">
<section class="section">
<div class="container">
<!-- 标准信息 -->
<div class="doc-meta">
<p>T/GDCCA XXXX2025 · 征求意见稿 · 广东省认证认可协会广东省生物医药创新技术协会发布</p>
</div>
<!-- 1 范围 -->
<div class="content-section">
<h2 class="section-title">1 范围</h2>
<p>本文件规定了临床试验责任保险与药品上市许可持有人责任保险的保险保障要求服务实施要求</p>
<p>本文件适用于保险公司保险经纪公司第三方管理机构TPA临床研究组织CRO药企申办者研究者申办IIT药品上市许可持有人及其境内责任人在中国境内开展的临床试验及相关保险活动并为临床试验伦理委员会和相关机构评估保障能力与风险承担能力提供参考亦可供医疗器械相关责任保险参照使用</p>
</div>
<!-- 3 术语和定义精选 -->
<div class="content-section">
<h2 class="section-title">3 术语和定义</h2>
<div class="term-list">
<div class="term-item">
<strong>临床试验责任保险</strong>
<p>以申办者研究者临床试验机构等因开展临床试验造成临床试验参与者试验相关损害而应承担的赔偿责任为保险标的的保险</p>
</div>
<div class="term-item">
<strong>可疑且非预期严重不良反应SUSAR</strong>
<p>临床表现的性质和严重程度超出了试验药物研究者手册已上市药品说明书产品特性摘要等现有资料所载信息的反应</p>
</div>
<div class="term-item">
<strong>严重不良事件SAE</strong>
<p>导致死亡危及生命需住院或延长住院永久或严重残疾先天性异常或出生缺陷或其他可能损害试验参与者需要干预的重要医学事件</p>
</div>
</div>
</div>
<!-- 4 资质与要求 -->
<div class="content-section">
<h2 class="section-title">4 资质与要求</h2>
<div class="sub-sections">
<div class="sub-section">
<h3>4.1 保险人保险公司</h3>
<ul>
<li>保证足够的赔付能力持续满足偿付能力标准</li>
<li>能在投保前对临床试验项目及持有人责任风险做出评估并合理定价</li>
<li>具备专业风险管理能力提供全流程风险预防建议和事故应对支持</li>
<li>提供专业理赔服务指定专人直接面对受试者/患者医疗机构开展理赔调查与定损</li>
<li>关注被保险人声誉及时回应各方关切减少外溢风险</li>
</ul>
</div>
<div class="sub-section">
<h3>4.2 保险经纪</h3>
<ul>
<li>诚信合法经营遵守生物医药行业合规要求</li>
<li>熟悉临床试验风险管理持有人风险管理及保险产品特性</li>
<li>担当被保险人与保险公司之间的翻译者角色准确传递信息</li>
<li>基于保障范围理赔规则多边服务保费价格等维度客观分析与比较方案</li>
</ul>
</div>
</div>
</div>
<!-- 5 保险保障要求 -->
<div class="content-section">
<h2 class="section-title">5 保险保障要求</h2>
<h3 class="sub-title">5.1 临床试验责任保险</h3>
<div class="sub-sections">
<div class="sub-section">
<h4>5.1.1 风险评估</h4>
<ul>
<li>在承保前对保险标的的风险进行评估</li>
<li>按已知风险和不可预见风险 SUSAR分别评估可能性与危害程度</li>
<li>承保报价按已知风险与 SUSAR 事件区别定价</li>
</ul>
</div>
<div class="sub-section">
<h4>5.1.2 责任界定</h4>
<ul>
<li>临床试验责任险属于财产险在知情同意书等文件中需准确描述保障性质</li>
<li>核心是转移申办者因责任导致的经济风险如临床试验参与者伤害赔偿责任</li>
<li>申办者依法需承担的赔偿责任由保险人负责承担</li>
</ul>
</div>
<div class="sub-section">
<h4>5.1.3 理赔准则</h4>
<ul>
<li>若与试验用药品或试验操作有关则考虑赔付因果关系包括二分类五分类六分类中的肯定有关可能有关很可能有关不能排除</li>
<li>评估为有关时考虑全额赔付 SAE 治疗相关费用</li>
<li>寻求无利益冲突的第三方评价意见以独立第三方评价的因果关系为准</li>
<li>对于从试验中有获益但最终因 SAE 死亡的患者一般不建议赔付死亡赔偿金</li>
</ul>
</div>
</div>
<h3 class="sub-title">5.2 保险责任示范性条款</h3>
<div class="clause-cards">
<div class="clause-card">
<h4>临床试验责任险 · 主险</h4>
<p>在保险期间或保险单载明的追溯期内被保险人在开展经政府有关部门或伦理委员会批准实施的被保险临床试验活动过程中出现 SUSAR造成临床试验参与者遭受人身伤亡由受害人或其代理人在保险期间内首次向被保险人提出索赔依照中华人民共和国法律应由被保险人承担的经济赔偿责任保险人按照本合同约定负责赔偿</p>
</div>
<div class="clause-card">
<h4>临床试验责任险 · 附加险</h4>
<p>临床试验参与者在参加主险载明的临床试验过程中因发生不良事件/不良反应导致人身伤亡由临床试验参与者或其代理人在保险期间内首次向被保险人提出损害赔偿请求对被保险人向临床试验参与者支付的损害补偿金保险人根据主险及本附加险合同约定承担赔偿责任</p>
</div>
<div class="clause-card">
<h4>持有人责任险 · 主险</h4>
<p>在保险期间或追溯期内因保险单载明的药品出现 SUSAR导致药品使用者遭受人身伤亡由受害人或其代理人在保险期间内首次向被保险人提出索赔依照中华人民共和国法律应由被保险人承担的经济赔偿责任保险人按照本合同约定负责赔偿</p>
</div>
</div>
<h3 class="sub-title">5.3 责任免除临床试验责任险</h3>
<div class="exclusion-grid">
<div class="exclusion-col">
<h4>免除情形</h4>
<ul>
<li>临床试验未经批准或批准后被撤销批件过期</li>
<li>机构研究者不具备相应资质</li>
<li>试验在境外含港澳台实施</li>
<li>违反 GCP药物警戒等规定</li>
<li>研究者偏离或违反已批准试验方案</li>
</ul>
</div>
<div class="exclusion-col">
<h4>免除原因</h4>
<ul>
<li>故意重大过失违法犯罪行为</li>
<li>与被保险临床试验无关的诊疗行为</li>
<li>医疗事故医疗过错诊疗意外</li>
<li>试验用药品/器械未达预期效果或存在质量问题</li>
</ul>
</div>
</div>
<h3 class="sub-title">5.4 保费定价原则</h3>
<ul>
<li><strong>分类定价</strong>不良事件/预期不良反应采用经验费率法SUSAR 采用综合模型法实行差异化定价</li>
<li><strong>风险对价</strong>费率与风险水平相匹配考虑试验分期药物安全特征受试人数试验周期等</li>
<li><strong>限额与免赔匹配</strong>合理设置责任限额与免赔原则上避免零免赔</li>
<li><strong>主附险搭配</strong>附加险须在投保主险基础上附加不重复保障主险已覆盖责任</li>
</ul>
</div>
<!-- 6 服务实施要求 -->
<div class="content-section">
<h2 class="section-title">6 服务实施要求</h2>
<div class="sub-sections">
<div class="sub-section">
<h3>6.1 承保准备风险评估</h3>
<ul>
<li>风险信息收集试验方案知情同意书研究者手册等</li>
<li>风险识别与评估涵盖已知/可预见风险外溢风险试验设计风险</li>
<li>设计承保方案与报价针对小风险AE及低频大风险SUSAR分别定价</li>
<li>风险改善建议可出具风险管理建议书作为承保前提或优惠依据</li>
</ul>
</div>
<div class="sub-section">
<h3>6.2 保单生效后风险减量</h3>
<ul>
<li>知情同意书修改建议风险预警与提示</li>
<li>培训与知识共享推动整改与优化</li>
</ul>
</div>
<div class="sub-section">
<h3>6.3 理赔服务</h3>
<ul>
<li>明确理赔申请材料清单与报送流程设立专人专岗</li>
<li>临床试验责任险需提交保单试验方案知情同意书试验协议IBAE/SAE 报告等</li>
<li>迅速启动调查由内部专职人员或委托独立第三方进行因果关系评估</li>
<li>加强与受试者/患者家属的沟通对属于保险责任范围内的案件及时支付赔款</li>
</ul>
</div>
</div>
</div>
<!-- 参考文献 -->
<div class="references-section">
<h2 class="section-title">参考文献</h2>
<ul class="ref-list">
<li>中华人民共和国保险法</li>
<li>中华人民共和国药品管理法</li>
<li>药品注册管理办法</li>
<li>药物临床试验质量管理规范2020 年第 57 </li>
<li>药物警戒质量管理规范2021 年第 65 </li>
<li>药物临床试验质量管理规范[ICH E6R3]</li>
<li>医疗器械临床试验质量管理规范医疗器械警戒质量管理规范</li>
</ul>
</div>
<!-- 参考资料 -->
<div class="source-section">
<p class="section-intro">
完整文件请前往
<RouterLink to="/system-management" class="ref-link">资源中心</RouterLink>
保险知识 / 条款标准 / 2063团体标准查阅
</p>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template>
<script setup lang="ts">
import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
</script>
<style scoped>
@import '@/pages/KnowledgeBase.css';
.group-standard .page-body {
padding-bottom: 3rem;
}
.doc-meta {
padding: 0.75rem 1rem;
background: var(--bg-color, #f8fafc);
border-radius: 8px;
margin-bottom: 1.5rem;
font-size: 0.875rem;
color: var(--text-light);
}
.content-section {
margin-bottom: 2rem;
}
.section-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--text-color);
margin: 0 0 1rem 0;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--brand-primary);
}
.content-section p {
font-size: 0.9375rem;
line-height: 1.7;
color: var(--text-color);
margin: 0 0 0.75rem 0;
}
.sub-title {
font-size: 1.0625rem;
font-weight: 600;
color: var(--brand-primary);
margin: 1.25rem 0 0.75rem 0;
}
.sub-sections {
margin-top: 0.5rem;
}
.sub-section {
margin-bottom: 1rem;
}
.sub-section h3,
.sub-section h4 {
font-size: 1rem;
font-weight: 600;
color: var(--text-color);
margin: 0.75rem 0 0.4rem 0;
}
.sub-section ul {
margin: 0.25rem 0 0.5rem 0;
padding-left: 1.5rem;
}
.sub-section li {
font-size: 0.9375rem;
line-height: 1.65;
color: var(--text-color);
margin-bottom: 0.25rem;
}
.term-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
.term-item {
padding: 1rem 1.25rem;
background: var(--white);
border: 1px solid var(--border-color);
border-left: 4px solid var(--brand-primary);
border-radius: 8px;
}
.term-item strong {
color: var(--brand-primary);
font-size: 0.9375rem;
}
.term-item p {
margin: 0.35rem 0 0;
font-size: 0.9rem;
color: var(--text-color);
}
.clause-cards {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 0.75rem;
}
.clause-card {
padding: 1rem 1.25rem;
background: linear-gradient(135deg, rgba(14, 165, 233, 0.05) 0%, var(--white) 100%);
border: 1px solid var(--border-color);
border-radius: 10px;
}
.clause-card h4 {
font-size: 0.9375rem;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 0.5rem 0;
}
.clause-card p {
font-size: 0.9rem;
line-height: 1.65;
margin: 0;
}
.exclusion-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1rem;
margin-top: 0.75rem;
}
.exclusion-col {
padding: 1rem;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 8px;
}
.exclusion-col h4 {
font-size: 0.9375rem;
font-weight: 600;
color: var(--text-color);
margin: 0 0 0.5rem 0;
}
.exclusion-col ul {
margin: 0;
padding-left: 1.25rem;
}
.exclusion-col li {
font-size: 0.875rem;
line-height: 1.6;
color: var(--text-color);
}
.references-section {
margin-top: 2rem;
}
.ref-list {
list-style: none;
padding: 0;
margin: 0;
}
.ref-list li {
font-size: 0.9rem;
color: var(--text-color);
padding: 0.3rem 0;
padding-left: 1rem;
position: relative;
}
.ref-list li::before {
content: '·';
position: absolute;
left: 0;
color: var(--brand-primary);
font-weight: bold;
}
.source-section {
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid var(--border-color);
}
.section-intro {
font-size: 0.9375rem;
color: var(--text-light);
margin: 0;
line-height: 1.6;
}
.ref-link {
color: var(--brand-primary);
font-weight: 500;
text-decoration: none;
}
.ref-link:hover {
text-decoration: underline;
}
</style>

View File

@ -0,0 +1,310 @@
<template>
<PageContainer>
<div class="knowledge-page insurance-overseas">
<PageHeader
title="国内外临床试验责任保险比较"
description="基本逻辑、保障范围、理赔情况的差异总结"
/>
<div class="page-body">
<section class="section">
<div class="container">
<!-- 概述 -->
<div class="overview-block">
<p class="overview-intro">
我国药物临床试验保险发展处于起步阶段存在政策不完善行业非标化保险公司专业能力不足申办方与受试者投保意识偏低等问题国际上如欧美依托 ICH 等规范制度相对成熟以下从基本逻辑保障范围理赔情况三方面总结国内外差异
</p>
</div>
<!-- 基本逻辑 -->
<div class="comparison-section">
<h2 class="section-title">基本逻辑</h2>
<div class="comparison-grid">
<div class="comparison-card domestic">
<h3>国内</h3>
<ul>
<li>保险法为基础商业保险行为</li>
<li>2020 GCP 要求申办者提供法律上经济上的保险或保证</li>
<li>伦理审批与机构合作日益要求出示保险证明</li>
<li>主险聚焦 SUSAR可疑且非预期严重不良反应所致法定经济赔偿责任</li>
<li>市场起步晚条款与定价尚未形成统一标准</li>
</ul>
</div>
<div class="comparison-card international">
<h3>国际欧美等</h3>
<ul>
<li>多遵循 ICH E6 等国际规范强调受试者保护</li>
<li>多国立法或监管明确要求申办者提供保险或补偿安排</li>
<li>保险应涵盖试验用药物相关伤害的补偿通常排除研究者自身过失</li>
<li>申办者需向受试者提供保险证明或补偿安排说明</li>
<li>产品体系成熟条款相对标准化</li>
</ul>
</div>
</div>
</div>
<!-- 保障范围 -->
<div class="comparison-section">
<h2 class="section-title">保障范围</h2>
<div class="comparison-table-wrap">
<table class="comparison-table">
<thead>
<tr>
<th>对比项</th>
<th>国内</th>
<th>国际</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>核心保障</strong></td>
<td>SUSAR 所致人身伤亡的法定经济赔偿责任附加险可扩展 AE 补偿</td>
<td>试验用药物相关伤害的补偿通常覆盖试验相关不良事件</td>
</tr>
<tr>
<td><strong>法律费用</strong></td>
<td>仲裁或诉讼费用一般可纳入经保险人书面同意的合理费用可赔</td>
<td>诉讼调解等法律费用多纳入保障</td>
</tr>
<tr>
<td><strong>常见除外</strong></td>
<td>未经批准试验资质不符违反 GCP/GVP方案偏离医疗事故精神损害除法院判决外预期副作用知情同意书约定的免责等</td>
<td>研究者过失通常除外各国具体除外条款有差异</td>
</tr>
<tr>
<td><strong>扩展险种</strong></td>
<td>部分公司提供 AE 附加险医疗责任扩展等覆盖仍有限</td>
<td>产品线较全如医疗责任意外伤害流产费用等扩展较常见</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 理赔情况 -->
<div class="comparison-section">
<h2 class="section-title">理赔情况</h2>
<div class="comparison-grid">
<div class="comparison-card domestic">
<h3>国内</h3>
<ul>
<li>保险人收到请求后及时核定情形复杂的在材料齐全后尽快核定</li>
<li>对属于保险责任的在与被保险人达成协议后<strong>十日内</strong>履行赔偿义务</li>
<li>对不属于保险责任的自核定之日起<strong>三日内</strong>发出拒赔通知书</li>
<li>六十日内对数额不能确定的可先予支付确定后补差</li>
<li>赔偿标准与因果关系认定如相关性比例影响实际赔付易引发争议</li>
</ul>
</div>
<div class="comparison-card international">
<h3>国际</h3>
<ul>
<li>多采用无过错或简化因果关系补偿机制重在保障受试者救治</li>
<li>理赔流程与时效一般有明确约定透明度较高</li>
<li>第三方 TPA经纪参与较普遍专业化程度高</li>
<li>医疗直付快速通道等增值服务较常见</li>
</ul>
</div>
</div>
</div>
<!-- 参考资料 -->
<div class="references-section">
<h2 class="section-title">参考资料</h2>
<p class="section-intro">
详细资料请前往
<RouterLink to="/system-management" class="ref-link">资源中心</RouterLink>
查阅保险知识 / 国外比较目录
</p>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template>
<script setup lang="ts">
import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
</script>
<style scoped>
@import '@/pages/KnowledgeBase.css';
.insurance-overseas .page-body {
padding-bottom: 3rem;
}
.overview-block {
margin-bottom: 2rem;
}
.overview-intro {
font-size: 1rem;
line-height: 1.75;
color: var(--text-color);
margin: 0;
padding: 1.25rem 1.5rem;
background: linear-gradient(135deg, rgba(14, 165, 233, 0.06) 0%, var(--white) 100%);
border: 1px solid var(--border-color);
border-left: 4px solid var(--brand-primary);
border-radius: 12px;
}
.comparison-section {
margin-bottom: 2.5rem;
}
.section-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--text-color);
margin: 0 0 1.25rem 0;
padding-bottom: 0.75rem;
border-bottom: 2px solid var(--brand-primary);
letter-spacing: 0.02em;
}
.comparison-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
}
.comparison-card {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem 1.75rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.comparison-card.domestic {
border-left: 4px solid var(--brand-primary);
}
.comparison-card.international {
border-left: 4px solid #0d9488;
}
.comparison-card h3 {
font-size: 1.0625rem;
font-weight: 600;
margin: 0 0 1rem 0;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--border-color);
}
.comparison-card.domestic h3 {
color: var(--brand-primary);
}
.comparison-card.international h3 {
color: #0d9488;
}
.comparison-card ul {
list-style: none;
padding: 0;
margin: 0;
}
.comparison-card li {
font-size: 0.9375rem;
line-height: 1.7;
color: var(--text-color);
padding: 0.4rem 0 0.4rem 1.25rem;
position: relative;
}
.comparison-card li::before {
content: '';
position: absolute;
left: 0;
top: 0.85em;
width: 5px;
height: 5px;
border-radius: 50%;
background: currentColor;
}
.comparison-card.domestic li::before {
color: var(--brand-primary);
}
.comparison-card.international li::before {
color: #0d9488;
}
.comparison-table-wrap {
overflow-x: auto;
border: 1px solid var(--border-color);
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.comparison-table {
width: 100%;
border-collapse: collapse;
font-size: 0.9375rem;
}
.comparison-table th,
.comparison-table td {
padding: 0.875rem 1rem;
text-align: left;
border-bottom: 1px solid var(--border-color);
vertical-align: top;
}
.comparison-table thead th {
background: var(--bg-color, #f8fafc);
font-weight: 600;
color: var(--text-color);
}
.comparison-table th:first-child,
.comparison-table td:first-child {
width: 120px;
color: var(--text-color);
}
.comparison-table tbody tr:last-child td {
border-bottom: none;
}
.comparison-table tbody tr:hover td {
background: rgba(14, 165, 233, 0.03);
}
.references-section {
margin-top: 2rem;
}
.section-intro {
font-size: 0.9375rem;
color: var(--text-light);
margin: 0;
line-height: 1.6;
}
.ref-link {
color: var(--brand-primary);
font-weight: 500;
text-decoration: none;
}
.ref-link:hover {
text-decoration: underline;
}
@media (max-width: 768px) {
.comparison-grid {
grid-template-columns: 1fr;
}
.comparison-table th:first-child,
.comparison-table td:first-child {
width: 100px;
}
}
</style>

View File

@ -1,74 +1,234 @@
<template> <template>
<PageContainer> <LearningCenterCases v-if="route.path.endsWith('/cases')" />
<LearningCenterVideos v-else-if="route.path === '/knowledge/learning-center/videos'" />
<PageContainer v-else>
<div class="learning-center-page"> <div class="learning-center-page">
<PageHeader :title="pageTitle" :description="pageDescription" /> <PageHeader :title="pageTitle" :description="pageDescription">
<template #actions>
<button type="button" class="btn-training-apply" @click="showTrainingModal = true">
预约培训申请
</button>
</template>
</PageHeader>
<div class="page-body"> <div class="page-body">
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<div class="learning-content"> <!-- 学习中心概要介绍 -->
<p class="placeholder-text">{{ placeholderText }}</p> <div class="learning-overview">
<p class="overview-intro">
学习中心是 RMO 知识资源体系中的核心模块面向申办者持有人研究中心及保险从业者提供药物临床试验责任保险与风险管理的系统化学习资源通过案例学习培训视频与考试测评帮助从业者掌握法规要求责任界定保险配置及理赔实务提升专业能力与合规水平
</p>
<div class="module-cards">
<RouterLink to="/knowledge/learning-center/cases" class="module-card">
<div class="module-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
<line x1="16" y1="13" x2="8" y2="13" />
<line x1="16" y1="17" x2="8" y2="17" />
<polyline points="10 9 9 9 8 9" />
</svg>
</div>
<h3 class="module-title">案例学习</h3>
<p class="module-desc">
基于司法裁判文书及公开报道的真实案例涵盖申办者责任知情同意书约束因果关系认定格式条款解释及赔偿比例酌定等核心议题帮助理解药物临床试验责任保险与赔偿的法律逻辑
</p>
<span class="module-link">进入案例学习 </span>
</RouterLink>
<RouterLink to="/knowledge/learning-center/videos" class="module-card">
<div class="module-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="23 7 16 12 23 17 23 7" />
<rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
</svg>
</div>
<h3 class="module-title">培训视频</h3>
<p class="module-desc">
系统化培训视频对应 PPTX 课件涵盖申办者责任本质赔偿金额测算风险管理理赔流程与案例ICF 解读药害救济国际经验保险要素纠纷处理及投保定价等主题助力专业能力提升
</p>
<span class="module-link">进入培训视频 </span>
</RouterLink>
<RouterLink to="/knowledge/learning-center/exam" class="module-card">
<div class="module-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 11l3 3L22 4" />
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" />
</svg>
</div>
<h3 class="module-title">考试中心</h3>
<p class="module-desc">
在线测评与认证考试检验学习成果支持从业者获得专业认证功能建设中敬请期待
</p>
<span class="module-link">进入考试中心 </span>
</RouterLink>
</div>
<div class="training-apply-cta">
<p>如需定制化培训或线下讲座欢迎提交预约申请我们将尽快与您联系</p>
<button type="button" class="btn btn-primary" @click="showTrainingModal = true">
预约培训申请
</button>
</div>
</div> </div>
</div> </div>
</section> </section>
</div> </div>
</div> </div>
<TrainingReservationModal v-model:open="showTrainingModal" />
</PageContainer> </PageContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { ref, watch } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue' import PageHeader from '@/components/PageHeader.vue'
import LearningCenterCases from './LearningCenterCases.vue'
import LearningCenterVideos from './LearningCenterVideos.vue'
import TrainingReservationModal from '@/components/TrainingReservationModal.vue'
const route = useRoute() const route = useRoute()
const showTrainingModal = ref(false)
const pageConfig: Record<string, { title: string; description: string; placeholder: string }> = { //
'index': { watch(
title: '学习中心', () => route.query.apply,
description: '案例学习、培训视频与考试中心', (val) => {
placeholder: '请从左侧菜单选择案例学习、培训视频或考试中心。' if (val === 'training') showTrainingModal.value = true
}, },
'cases': { { immediate: true }
title: '案例学习', )
description: '通过真实案例掌握药物警戒与保险风险管理',
placeholder: '案例学习内容建设中,敬请期待。'
},
'videos': {
title: '培训视频',
description: '系统化培训视频,助力专业能力提升',
placeholder: '培训视频内容建设中,敬请期待。'
},
'exam': {
title: '考试中心',
description: '在线测评与认证考试',
placeholder: '考试中心功能建设中,敬请期待。'
}
}
const currentKey = computed(() => { const pageTitle = '学习中心'
const path = route.path const pageDescription = '案例学习、培训视频与考试中心,助力药物临床试验责任与保险风险管理能力提升'
if (path.endsWith('/cases')) return 'cases'
if (path.endsWith('/videos')) return 'videos'
if (path.endsWith('/exam')) return 'exam'
return 'index'
})
const pageTitle = computed(() => pageConfig[currentKey.value]?.title ?? '学习中心')
const pageDescription = computed(() => pageConfig[currentKey.value]?.description ?? '')
const placeholderText = computed(() => pageConfig[currentKey.value]?.placeholder ?? '内容建设中,敬请期待。')
</script> </script>
<style scoped> <style scoped>
.learning-center-page { .learning-center-page {
min-height: 60vh; min-height: 60vh;
} }
.learning-content {
.btn-training-apply {
padding: 0.5rem 1rem;
font-size: 0.9rem;
font-weight: 500;
color: var(--white, #fff);
background: var(--brand-primary, #0ea5e9);
border: none;
border-radius: 8px;
cursor: pointer;
white-space: nowrap;
}
.btn-training-apply:hover {
background: var(--brand-primary-dark, #0284c7);
}
.learning-overview {
padding: 2rem 0; padding: 2rem 0;
} }
.placeholder-text {
color: var(--text-secondary, #666); .overview-intro {
text-align: center;
font-size: 1rem; font-size: 1rem;
line-height: 1.8;
color: var(--text-color);
margin: 0 0 2.5rem 0;
}
.module-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
margin-bottom: 2.5rem;
}
.module-card {
display: flex;
flex-direction: column;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.5rem;
text-decoration: none;
color: inherit;
transition: box-shadow 0.2s, border-color 0.2s;
}
.module-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
border-color: var(--brand-primary, #0ea5e9);
}
.module-icon {
width: 48px;
height: 48px;
color: var(--brand-primary, #0ea5e9);
margin-bottom: 1rem;
}
.module-icon svg {
width: 100%;
height: 100%;
}
.module-title {
font-size: 1.15rem;
font-weight: 600;
color: var(--text-color);
margin: 0 0 0.5rem 0;
}
.module-desc {
font-size: 0.9375rem;
line-height: 1.65;
color: var(--text-secondary, #555);
margin: 0 0 1rem 0;
flex: 1;
}
.module-link {
font-size: 0.9rem;
font-weight: 500;
color: var(--brand-primary, #0ea5e9);
}
.training-apply-cta {
padding: 1.5rem;
background: var(--bg-color, #f8fafc);
border-radius: 12px;
text-align: center;
}
.training-apply-cta p {
margin: 0 0 1rem 0;
font-size: 0.9375rem;
color: var(--text-secondary, #555);
}
.training-apply-cta .btn {
padding: 0.5rem 1.25rem;
font-size: 0.9rem;
border-radius: 8px;
border: none;
cursor: pointer;
}
.training-apply-cta .btn-primary {
background: var(--brand-primary, #0ea5e9);
color: var(--white, #fff);
}
.training-apply-cta .btn-primary:hover {
background: var(--brand-primary-dark, #0284c7);
}
@media (max-width: 768px) {
.module-cards {
grid-template-columns: 1fr;
}
} }
</style> </style>

View File

@ -0,0 +1,193 @@
<template>
<PageContainer>
<div class="learning-center-page cases-page">
<PageHeader title="案例学习" description="通过真实案例掌握药物警戒与保险风险管理" />
<div class="page-body">
<section class="section">
<div class="container">
<p class="cases-intro">
以下案例来源于司法裁判文书及公开报道供学习药物临床试验责任保险与赔偿逻辑参考
</p>
<!-- 案例一刘某群案深圳微芯/西达本胺案 -->
<div class="case-card">
<h2 class="case-title">案例一刘某群等诉某某医院深圳某某生物科技股份有限公司案</h2>
<p class="case-meta">2024湘0105民初8981号 / 2024湘01民终18580号 · 湖南省长沙市中级人民法院</p>
<div class="case-section">
<h3>事件过程</h3>
<ul>
<li>2021年10月12日患者喻某因弥漫大B细胞淋巴瘤入住某某医院作为受试者参加深圳某某生物科技股份有限公司申办方发起的西达本胺联合R-CHOP方案治疗弥漫大B细胞淋巴瘤的随机双盲安慰剂对照多中心3期试验DEB试验</li>
<li>2021年9月30日喻某签署知情同意书其中第十条载明申办方已为受试者购买保险将承担因本次试验导致的任何不适伤害致残甚至死亡的相关救治和赔偿费用</li>
<li>入院当天喻某接受临床试验用药2021年10月13日出现心率减慢意识模糊血压测不出等症状经抢救无效于12时38分死亡死亡原因记载为不明心源性猝死可能性大家属未申请尸检</li>
<li>事后破盲得知喻某分配至安慰剂组使用药物为安慰剂R-CHOP方案未服用实验药物试剂</li>
</ul>
</div>
<div class="case-section">
<h3>裁判结果</h3>
<ul>
<li>一审法院认定某某医院不承担侵权责任申办方深圳某某生物科技股份有限公司承担刘某群等人损失的<strong>20%</strong>即168,527.49</li>
<li>二审驳回上诉维持原判</li>
</ul>
</div>
<div class="case-section">
<h3>裁判逻辑</h3>
<ul>
<li><strong>责任主体</strong>根据民法典第925条知情同意书直接约束申办者与受试者DEB试验引起的赔偿或合同责任应由申办者承担医院作为受托方不承担连带责任</li>
<li><strong>因果关系</strong>破盲显示喻某使用安慰剂未服用实验药物刘某群等人无证据证明死亡与试验药品存在因果关系故不支持基于侵权或药物因果关系的赔偿主张</li>
<li><strong>格式条款解释</strong>知情同意书第十条承担因本次实验导致的任何不适伤害致残甚至死亡的相关救治和赔偿费用表述含糊易使受试者产生误解格式条款应作有利于受试者的解释申办方未就该条重点说明且无除外责任说明</li>
<li><strong>酌定比例</strong>综合考虑药物临床试验的公益性前瞻性与未知性以及破盲显示喻某之死与实验药物无直接关联酌定申办方承担20%损失</li>
</ul>
</div>
</div>
<!-- 案例二张老太诉拜耳案8旬老太试药休克案 -->
<div class="case-card">
<h2 class="case-title">案例二张老太诉拜耳医药案8旬老太试药休克案</h2>
<p class="case-meta">北京朝阳法院 · 2013 · 来源中新网等公开报道</p>
<div class="case-section">
<h3>事件过程</h3>
<ul>
<li>2006年10月84岁的张老太于北京大学人民医院参加拜耳医药公司的新药临床试验预防血栓新药 BAY59-7939用于膝关节置换术后预防血栓</li>
<li>服用试验药物后进行膝关节置换手术2006年11月7日进行静脉造影检查后发生休克被认定为严重不良事件SAE</li>
<li>拜耳医药公司虽为试验购买了保险每名受试者最高保额50万欧元但诉讼中一直拒绝出示保险合同以翻译成本高合同为德文版等理由推诿</li>
<li>张老太将拜耳医药公司告上法院索赔15万欧元</li>
</ul>
</div>
<div class="case-section">
<h3>裁判结果</h3>
<ul>
<li>2013年2月21日北京朝阳法院一审判决<strong>拜耳败诉</strong>向张老太赔偿<strong>5万欧元</strong></li>
</ul>
</div>
<div class="case-section">
<h3>裁判逻辑</h3>
<ul>
<li><strong>举证妨碍推定</strong>拜耳公司拒不提交保险合同法院据此推定该合同不利于拜耳有利于原告作出对被告不利的认定</li>
<li><strong>制度启示</strong>案件暴露监管缺陷保险措施在药品注册管理办法中未明确要求监管部门与医院伦理委员会也未对保险合同进行备案保管导致保险监管存在空白</li>
<li><strong>行业反思</strong>专家指出中国已成为全球新药试验基地但试药者风险意识不高监管不足赔偿标准偏低需完善保险与补偿制度</li>
</ul>
</div>
</div>
<!-- 参考资料 -->
<div class="references-section">
<p class="section-intro">
完整裁判文书及更多案例请前往
<RouterLink to="/system-management" class="ref-link">资源中心</RouterLink>
学习中心 / 案例学习查阅
</p>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template>
<script setup lang="ts">
import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
</script>
<style scoped>
.cases-page .page-body {
padding-bottom: 3rem;
}
.cases-intro {
font-size: 1rem;
line-height: 1.7;
color: var(--text-color);
margin: 0 0 2rem 0;
}
.case-card {
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 1.75rem 2rem;
margin-bottom: 2rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.case-card:last-of-type {
margin-bottom: 0;
}
.case-title {
font-size: 1.2rem;
font-weight: 600;
color: var(--text-color);
margin: 0 0 0.5rem 0;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--brand-primary);
}
.case-meta {
font-size: 0.875rem;
color: var(--text-light);
margin: 0 0 1.25rem 0;
}
.case-section {
margin-bottom: 1.25rem;
}
.case-section:last-child {
margin-bottom: 0;
}
.case-section h3 {
font-size: 1rem;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 0.5rem 0;
}
.case-section ul {
margin: 0.25rem 0 0;
padding-left: 1.5rem;
}
.case-section li {
font-size: 0.9375rem;
line-height: 1.7;
color: var(--text-color);
margin-bottom: 0.35rem;
}
.references-section {
margin-top: 2rem;
padding-top: 1rem;
border-top: 1px solid var(--border-color);
}
.section-intro {
font-size: 0.9375rem;
color: var(--text-light);
margin: 0;
line-height: 1.6;
}
.ref-link {
color: var(--brand-primary);
font-weight: 500;
text-decoration: none;
}
.ref-link:hover {
text-decoration: underline;
}
@media (max-width: 768px) {
.case-card {
padding: 1.25rem 1.25rem;
}
}
</style>

View File

@ -0,0 +1,141 @@
<template>
<PageContainer>
<div class="learning-center-page video-detail-page">
<div class="page-body">
<section class="section">
<div class="container">
<RouterLink to="/knowledge/learning-center/videos" class="back-link">
<svg class="back-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 12H5M12 19l-7-7 7-7" />
</svg>
返回培训视频列表
</RouterLink>
<template v-if="video">
<PageHeader :title="video.title" :description="`对应课件:${video.pptxFile}`" />
<div class="video-player-wrapper">
<video
:src="videoUrl"
controls
class="video-player"
>
您的浏览器不支持视频播放
</video>
<p class="video-hint">视频文件请放置于 <code>public/knowledge/videos/</code>文件名{{ video.videoFile }}</p>
</div>
<div class="video-summary-block">
<h3>内容摘要</h3>
<p>{{ video.summary }}</p>
</div>
</template>
<div v-else class="video-not-found">
<p>未找到该视频</p>
<RouterLink to="/knowledge/learning-center/videos" class="back-link">返回列表</RouterLink>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
import { learningVideos } from '@/data/learningVideos'
const route = useRoute()
const videoId = computed(() => route.params.id as string)
const video = computed(() => learningVideos.find((v) => v.id === videoId.value))
const videoUrl = computed(() => {
if (!video.value) return ''
return `/knowledge/videos/${encodeURIComponent(video.value.videoFile)}`
})
</script>
<style scoped>
.video-detail-page .page-body {
padding-bottom: 3rem;
}
.back-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9375rem;
color: var(--brand-primary, #2563eb);
text-decoration: none;
margin-bottom: 1.5rem;
}
.back-link:hover {
text-decoration: underline;
}
.back-icon {
width: 20px;
height: 20px;
}
.video-player-wrapper {
margin: 1.5rem 0 2rem;
border-radius: 12px;
overflow: hidden;
background: #000;
aspect-ratio: 16 / 9;
}
.video-player {
width: 100%;
height: 100%;
display: block;
}
.video-hint {
margin-top: 0.75rem;
font-size: 0.8125rem;
color: var(--text-light);
}
.video-hint code {
background: rgba(0, 0, 0, 0.06);
padding: 0.2rem 0.4rem;
border-radius: 4px;
}
.video-summary-block {
padding: 1.5rem;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
}
.video-summary-block h3 {
font-size: 1rem;
font-weight: 600;
color: var(--brand-primary);
margin: 0 0 0.75rem 0;
}
.video-summary-block p {
font-size: 0.9375rem;
line-height: 1.7;
color: var(--text-color);
margin: 0;
}
.video-not-found {
text-align: center;
padding: 3rem 0;
}
.video-not-found p {
margin-bottom: 1rem;
color: var(--text-secondary);
}
</style>

View File

@ -0,0 +1,220 @@
<template>
<PageContainer>
<div class="learning-center-page videos-page">
<PageHeader title="培训视频" description="系统化培训视频,助力专业能力提升" />
<div class="page-body">
<section class="section">
<div class="container">
<p class="videos-intro">
以下培训视频对应学习中心视频资料中的 PPTX 课程涵盖临床试验责任保险理赔及受试者权益等主题点击卡片可观看完整视频
</p>
<div
v-for="video in learningVideos"
:key="video.id"
class="video-card"
@click="goToVideo(video.id)"
>
<!-- 左侧缩略图 -->
<div class="video-thumbnail">
<img
v-if="video.thumbnail"
:src="video.thumbnail"
:alt="video.title"
class="thumbnail-img"
/>
<div v-else class="thumbnail-placeholder">
<svg class="play-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M8 5v14l11-7z" />
</svg>
<span class="video-label">培训视频</span>
</div>
</div>
<!-- 右侧摘要信息 -->
<div class="video-summary">
<h3 class="video-title">{{ video.title }}</h3>
<p class="video-desc">{{ video.summary }}</p>
<div class="video-meta">
<span class="meta-item">对应课件{{ video.pptxFile }}</span>
</div>
<span class="watch-link">
观看完整视频
<svg class="arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7" />
</svg>
</span>
</div>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
import { learningVideos } from '@/data/learningVideos'
const router = useRouter()
function goToVideo(id: string) {
router.push({ path: `/knowledge/learning-center/videos/${id}` })
}
</script>
<style scoped>
.videos-page .page-body {
padding-bottom: 3rem;
}
.videos-intro {
font-size: 1rem;
line-height: 1.7;
color: var(--text-color);
margin: 0 0 2rem 0;
}
.video-card {
display: flex;
gap: 1.5rem;
align-items: stretch;
background: var(--white);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 0;
margin-bottom: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
cursor: pointer;
transition: box-shadow 0.2s, border-color 0.2s;
overflow: hidden;
}
.video-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
border-color: var(--brand-primary, #2563eb);
}
.video-card:last-child {
margin-bottom: 0;
}
/* 左侧缩略图 */
.video-thumbnail {
flex-shrink: 0;
width: 200px;
min-height: 120px;
background: linear-gradient(135deg, #e8eef7 0%, #d4dce8 100%);
display: flex;
align-items: center;
justify-content: center;
}
.thumbnail-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.thumbnail-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
width: 100%;
height: 100%;
color: var(--brand-primary, #2563eb);
opacity: 0.85;
}
.play-icon {
width: 48px;
height: 48px;
}
.video-label {
font-size: 0.75rem;
color: var(--text-light);
}
/* 右侧摘要 */
.video-summary {
flex: 1;
padding: 1.25rem 1.5rem 1.25rem 0;
display: flex;
flex-direction: column;
justify-content: center;
min-width: 0;
}
.video-title {
font-size: 1.1rem;
font-weight: 600;
color: var(--text-color);
margin: 0 0 0.5rem 0;
line-height: 1.4;
}
.video-desc {
font-size: 0.9375rem;
line-height: 1.65;
color: var(--text-secondary, #555);
margin: 0 0 0.75rem 0;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.video-meta {
margin-bottom: 0.5rem;
}
.meta-item {
font-size: 0.8125rem;
color: var(--text-light);
}
.watch-link {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-size: 0.9rem;
font-weight: 500;
color: var(--brand-primary, #2563eb);
}
.arrow-icon {
width: 18px;
height: 18px;
transition: transform 0.2s;
}
.video-card:hover .arrow-icon {
transform: translateX(4px);
}
@media (max-width: 768px) {
.video-card {
flex-direction: column;
gap: 0;
}
.video-thumbnail {
width: 100%;
min-height: 140px;
}
.video-summary {
padding: 1.25rem 1.25rem 1.25rem 1.25rem;
}
.video-desc {
-webkit-line-clamp: 2;
}
}
</style>

View File

@ -15,12 +15,12 @@
id="username" id="username"
v-model="username" v-model="username"
type="text" type="text"
placeholder="请输入用户名或邮箱" placeHolder="请输入用户名或邮箱"
required required
:disabled="loading" :disabled="loading"
/> />
<div class="form-hint"> <div class="form-hint">
<small>测试账号admin, policyholder, insurer, tpa密码123456</small> <small>测试账号admin, policyHolder, insurer, tpa密码123456</small>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -29,7 +29,7 @@
id="password" id="password"
v-model="password" v-model="password"
type="password" type="password"
placeholder="请输入密码" placeHolder="请输入密码"
required required
:disabled="loading" :disabled="loading"
/> />

View File

@ -6,31 +6,82 @@
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<div class="card"> <div class="card">
<h2>受试者专区概述</h2> <h2>受试者职责概述</h2>
<ul class="responsibility-overview"> <ul class="responsibility-overview">
<li>了解临床试验基本知识</li> <li>了解临床试验</li>
<li>知情同意与权益保障</li> <li>关注自身安全</li>
<li>损害救济与补偿机制</li> <li>及时报告不良事件</li>
</ul> </ul>
</div> </div>
</div> </div>
</section> </section>
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<div class="card main-card"> <h2 class="section-title">了解临床试验</h2>
<h2>临床试验介绍</h2> <div class="info-cards">
<div class="content-section"> <div class="info-card">
<h3>什么是临床试验</h3> <h3>什么是临床试验</h3>
<p>临床试验是指在人体病人或健康志愿者进行的系统性研究以证实或揭示试验药物的作用不良反应及/或试验药物的吸收分布代谢和排泄目的是确定试验药物的疗效与安全性</p> <p>临床试验是指在人体病人或健康志愿者进行的系统性研究以证实或揭示试验药物的作用不良反应及/或试验药物的吸收分布代谢和排泄目的是确定试验药物的疗效与安全性</p>
</div>
<div class="info-card">
<h3>参与临床试验的意义</h3> <h3>参与临床试验的意义</h3>
<p>为医学进步做出贡献可能获得新的治疗机会获得专业的医疗监测和照护帮助更多患者获得更好的治疗</p>
</div>
<div class="info-card">
<h3>获益与风险</h3>
<p>参与试验可能获得新疗法更密切的医疗随访同时也可能面临未知的不良反应疗效不确定等风险研究者会在知情同意书中详细说明请充分阅读并理解后再决定是否参与</p>
</div>
</div>
</div>
</section>
<section class="section">
<div class="container">
<h2 class="section-title">关注自身安全</h2>
<div class="duty-summary-card">
<p>基于受试者自我教育理念您应主动参与自身照护保障安全</p>
<ul> <ul>
<li> 为医学进步做出贡献</li> <li><strong>主动发声</strong>如有不理解之处或发现异常及时向研究者提出如需翻译或书面材料解释主动要求核对用药是否正确确认是否为本人用药</li>
<li> 可能获得新的治疗机会</li> <li><strong>留心观察</strong>核对医护人员身份标识提醒医护人员洗手等感染防控措施关注治疗是否按计划进行</li>
<li> 获得专业的医疗监测和照护</li> <li><strong>自我教育</strong>了解自身病情和治疗方案向研究者索取书面信息询问治疗周期预期感受及医疗设备使用方法</li>
<li> 帮助更多患者获得更好的治疗</li> <li><strong>了解试验药物</strong>询问药物作用副作用与其他药物/维生素的相互作用核对输液袋标签确认药物与身份</li>
<li><strong>参与决策</strong>与医生讨论每一步治疗可寻求第二第三意见及时向医疗团队提供用药清单和病历副本</li>
<li><strong>倡导者支持</strong>家人或朋友可协助提问记录重要信息核对知情同意书内容但最终决策权在您</li>
</ul> </ul>
</div> </div>
</div> </div>
</section>
<section class="section">
<div class="container">
<h2 class="section-title">及时报告不良事件</h2>
<div class="duty-summary-card">
<p>受试者应<strong>自发及时</strong>向研究者报告试验期间出现的任何不适或异常</p>
<ul>
<li>无论症状轻重主动告知研究者您感受到的任何身体变化</li>
<li>包括但不限于过敏反应用药后不适原有症状加重新发症状等</li>
<li>您的报告有助于保障自身安全也为其他患者和医学研究提供重要信息</li>
</ul>
</div>
</div>
</section>
<section class="section">
<div class="container">
<h2 class="section-title">风险策略</h2>
<div class="card main-card">
<div class="strategy-grid strategy-grid--2">
<div class="strategy-card">
<h4>了解风险</h4>
<p>阅读并理解知情同意书充分了解试验目的流程可能获益与风险后再决定是否参与</p>
</div>
<div class="strategy-card">
<h4>自留风险</h4>
<p>对于知情同意书中已充分告知且可接受的轻微风险自愿参与即表示自行承担</p>
</div>
</div>
</div>
</div> </div>
</section> </section>
</div> </div>

View File

@ -1,25 +1,21 @@
<template> <template>
<PageContainer> <PostMarketOverview v-if="isOverview" />
<div class="post-market module-dashboard-container"> <PostMarketInsurance v-else-if="route.path.includes('/insurance')" />
<PageHeader variant="module" title="上市应用" description="药品上市后风险管理与药物警戒的保险与保障方案" /> <PostMarketGuarantee v-else-if="route.path.includes('/guarantee')" />
<div class="module-content"> <PostMarketOverview v-else />
<section class="section">
<div class="container">
<div class="card main-card">
<p class="building-notice">建设中</p>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import PageContainer from '@/components/PageContainer.vue' import { computed } from 'vue'
import PageHeader from '@/components/PageHeader.vue' import { useRoute } from 'vue-router'
</script> import PostMarketOverview from './PostMarketOverview.vue'
import PostMarketInsurance from './PostMarketInsurance.vue'
import PostMarketGuarantee from './PostMarketGuarantee.vue'
<style scoped> const route = useRoute()
@import '@/pages/PostMarket.css';
</style> const isOverview = computed(() => {
const p = route.path
return p === '/post-market' || p === '/post-market/'
})
</script>

View File

@ -0,0 +1,157 @@
<template>
<PageContainer>
<div class="post-market post-market-guarantee">
<PageHeader title="保证设计" description="专项基金、风险减量、外溢风险管理" />
<div class="page-body">
<!-- 上市后责任的风险分层 -->
<section class="section section-risk-split">
<div class="container">
<h2 class="section-title">上市后责任的风险分层</h2>
<p class="section-subtitle">将风险分为两部分对应不同的保障方式</p>
<div class="risk-split-cards">
<div class="risk-split-card risk-primary">
<h3>大额责任与诉讼风险</h3>
<p>身故伤残赔偿产品责任诉讼金额较大证据链清晰通过<strong>保险</strong>进行风险转移</p>
</div>
<div class="risk-split-card risk-secondary">
<h3>不良反应相关医疗费用</h3>
<p>上市后不良反应导致的高频小额医疗费用通过<strong>保证资金</strong>存入华泰经纪管理的专用基金用于及时支付</p>
</div>
</div>
</div>
</section>
<!-- 上市后责任的保证实现 -->
<section class="section section-guarantee-implementation">
<div class="container">
<h2 class="section-title">上市后责任的保证实现</h2>
<div class="guarantee-flow">
<div class="guarantee-flow-item">
<h4>1. 保证资金 · 专用基金</h4>
<p>持有人支付风险管理费资金存入由<strong>华泰经纪管理</strong>的专用基金用于支付上市后不良反应相关的高频小额医疗费用实行多退少补原则</p>
</div>
<div class="guarantee-flow-item">
<h4>2. 保险承保 · 首要风险</h4>
<p>通过产品责任保险进行大额责任诉讼风险的承保保障身故伤残等大额赔偿</p>
</div>
</div>
</div>
</section>
<!-- 保险与保证方案的比较 -->
<section class="section section-insurance-guarantee-compare">
<div class="container">
<h2 class="section-title">保险与保证方案的比较</h2>
<div class="guarantee-compare-table-wrap">
<table class="guarantee-compare-table">
<thead>
<tr>
<th>比较维度</th>
<th>保险</th>
<th>保证</th>
</tr>
</thead>
<tbody>
<tr>
<td class="compare-row-label">适用风险</td>
<td>大额责任身故伤残赔偿产品责任诉讼</td>
<td>不良反应相关医疗费用高频小额补偿</td>
</tr>
<tr>
<td class="compare-row-label">合规性</td>
<td>受保险监管约束产品责任险需满足MAH要求</td>
<td>需符合持有人内控要求资金用途与上市后场景结合</td>
</tr>
<tr>
<td class="compare-row-label">灵活性</td>
<td>保障范围保额可在投保时约定</td>
<td>基金规模使用规则可按持有人需求设计</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 华泰经纪基金管理 vs 持有人直接缴纳保证金的区别 -->
<section class="section section-fund-management-compare">
<div class="container">
<h2 class="section-title">由华泰经纪进行基金管理与持有人直接缴纳保证金的区别</h2>
<div class="guarantee-compare-table-wrap">
<table class="guarantee-compare-table">
<thead>
<tr>
<th>比较维度</th>
<th>华泰经纪管理的专用基金</th>
<th>持有人直接缴纳保证金</th>
</tr>
</thead>
<tbody>
<tr>
<td class="compare-row-label">资金管理</td>
<td>华泰经纪统一管理专业托管与对账服务多产品可设立总账户或分账户</td>
<td>持有人需向各渠道或医疗机构分别缴纳操作分散对接成本高</td>
</tr>
<tr>
<td class="compare-row-label">支付时效</td>
<td>24 小时内响应及时安排救治与结算逐步实现医疗直付</td>
<td>依托各渠道流程审批与划款环节多时效受制约</td>
</tr>
<tr>
<td class="compare-row-label">增值服务</td>
<td>风险减量服务外溢风险管理临研安协助联系安抚沟通诉求专业理赔支持</td>
<td>以事件处理为主较少提供资金托管风险减量等增值服务</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 相关服务 -->
<section class="section section-related-services">
<div class="container">
<div class="service-item card main-card">
<div class="service-header">
<span class="service-icon">🔗</span>
<h2>相关服务</h2>
</div>
<div class="service-content">
<div class="related-service-columns">
<div class="related-service-column">
<h3>风险减量服务</h3>
<ul class="service-list">
<li>针对上市后药物警戒链条进行风险点检查</li>
<li>人员培训提升风险管理能力</li>
<li>信号检测与评估流程完善建议</li>
</ul>
</div>
<div class="related-service-column">
<h3>外溢风险管理服务</h3>
<ul class="service-list">
<li>患者出险后及时联系安抚</li>
<li>安排就医和医疗救治</li>
<li>沟通诉求和合理预期</li>
<li>协调各方关系签署解决协议</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template>
<script setup lang="ts">
import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
</script>
<style scoped>
@import '@/pages/PostMarket.css';
@import '@/pages/RmoMode.css';
@import '@/pages/RmoModeGuarantee.css';
</style>

View File

@ -0,0 +1,103 @@
<template>
<PageContainer>
<div class="post-market post-market-insurance">
<PageHeader title="保险方案" description="药品上市后产品责任保险" />
<div class="page-body">
<!-- 获取报价申请理赔 -->
<section class="section section-action-links">
<div class="container">
<div class="action-links">
<RouterLink to="/dashboard/project-quotes" class="action-link">
<span class="action-link-icon">📋</span>
<span class="action-link-text">获取报价</span>
</RouterLink>
<RouterLink to="/dashboard/claims" class="action-link">
<span class="action-link-icon">📄</span>
<span class="action-link-text">申请理赔</span>
</RouterLink>
</div>
</div>
</section>
<!-- 持有人责任与产品保险 -->
<section class="section section-mah-intro">
<div class="container">
<h2 class="section-title">持有人责任与产品责任保险</h2>
<p class="section-subtitle">根据药品管理法要求持有人需有风险管理能力承担药品安全责任产品责任保险为持有人提供上市后大额责任风险转移</p>
<div class="card main-card mah-intro-card">
<h3>产品责任保险保障范围</h3>
<ul>
<li>药品上市后因不良反应等导致的患者损害赔偿责任</li>
<li>身故伤残等大额赔偿金</li>
<li>与临床试验责任险形成互补覆盖药品全生命周期</li>
</ul>
</div>
</div>
</section>
<!-- 风险管理目的在于保护患者安全 -->
<section class="section section-why-rm">
<div class="container">
<h2 class="section-title">风险管理目的在于保护患者安全</h2>
<p class="section-subtitle">从药物警戒数据监测到风险识别与评估再到保险与保证保障实现长期患者安全</p>
<div class="why-rm-images">
<div class="why-rm-image-wrap">
<img :src="imgWhyRisk" alt="风险管理:长期患者安全" class="why-rm-image" />
</div>
</div>
</div>
</section>
<!-- 保险与保证的比较 -->
<section class="section section-insurance-guarantee-compare">
<div class="container">
<h2 class="section-title">保险与保证方案的比较</h2>
<div class="guarantee-compare-table-wrap">
<table class="guarantee-compare-table">
<thead>
<tr>
<th>比较维度</th>
<th>保险</th>
<th>保证</th>
</tr>
</thead>
<tbody>
<tr>
<td class="compare-row-label">适用风险</td>
<td>上市后大额责任身故伤残赔偿产品责任诉讼</td>
<td>不良反应相关医疗费用高频小额补偿补充保障</td>
</tr>
<tr>
<td class="compare-row-label">合规性</td>
<td>受保险监管约束产品责任险需满足MAH及监管要求</td>
<td>需符合持有人内控要求资金用途与上市后场景结合</td>
</tr>
<tr>
<td class="compare-row-label">灵活性</td>
<td>保障范围保额可在投保时约定产品形态相对标准化</td>
<td>基金规模使用规则可按持有人需求设计可随产品生命周期调整</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
const imgWhyRisk = computed(() => `/pic/${encodeURIComponent('why risk management.png')}`)
</script>
<style scoped>
@import '@/pages/PostMarket.css';
@import '@/pages/RmoMode.css';
@import '@/pages/RmoModeInsurance.css';
@import '@/pages/RmoModeGuarantee.css';
</style>

View File

@ -0,0 +1,37 @@
<template>
<PageContainer>
<div class="post-market module-dashboard-container">
<PageHeader variant="module" title="上市应用:药品上市后风险管理" description="持有人责任保险与保障方案" />
<div class="module-content">
<section class="section section-intro">
<div class="container">
<h2 class="section-title">药品上市后的患者安全与责任保障</h2>
<p class="section-subtitle">持有人MAH是药品安全的责任主体需建立药物警戒与风险管理体系通过保险与保证方式承担上市后责任</p>
<div class="closed-loop-measures post-market-measures">
<RouterLink to="/post-market/insurance" class="closed-loop-measure-card">
<span class="closed-loop-measure-icon">🛡</span>
<h4>保险方案</h4>
<p>产品责任保险保障上市后大额责任风险</p>
</RouterLink>
<RouterLink to="/post-market/guarantee" class="closed-loop-measure-card">
<span class="closed-loop-measure-icon">💰</span>
<h4>保证设计</h4>
<p>专项基金风险减量外溢风险管理</p>
</RouterLink>
</div>
</div>
</section>
</div>
</div>
</PageContainer>
</template>
<script setup lang="ts">
import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
</script>
<style scoped>
@import '@/pages/PostMarket.css';
@import '@/pages/RmoMode.css';
</style>

View File

@ -46,7 +46,7 @@
id="name" id="name"
v-model="form.name" v-model="form.name"
type="text" type="text"
placeholder="请输入姓名" placeHolder="请输入姓名"
required required
:disabled="loading" :disabled="loading"
/> />
@ -57,7 +57,7 @@
id="phone" id="phone"
v-model="form.phone" v-model="form.phone"
type="tel" type="tel"
placeholder="请输入手机号" placeHolder="请输入手机号"
required required
:disabled="loading" :disabled="loading"
/> />
@ -70,7 +70,7 @@
id="email" id="email"
v-model="form.email" v-model="form.email"
type="email" type="email"
placeholder="请输入邮箱" placeHolder="请输入邮箱"
required required
:disabled="loading" :disabled="loading"
/> />
@ -81,7 +81,7 @@
id="position" id="position"
v-model="form.position" v-model="form.position"
type="text" type="text"
placeholder="请输入职位" placeHolder="请输入职位"
:disabled="loading" :disabled="loading"
/> />
</div> </div>
@ -92,7 +92,7 @@
id="department" id="department"
v-model="form.department" v-model="form.department"
type="text" type="text"
placeholder="请输入部门" placeHolder="请输入部门"
:disabled="loading" :disabled="loading"
/> />
</div> </div>

View File

@ -3,97 +3,48 @@
<div class="risk-duties-overview module-dashboard-container"> <div class="risk-duties-overview module-dashboard-container">
<PageHeader variant="module" title="风险职责" description="为临床试验各方提供专业的风险管理支持" /> <PageHeader variant="module" title="风险职责" description="为临床试验各方提供专业的风险管理支持" />
<div class="module-content"> <div class="module-content">
<section class="section">
<div class="container">
<div class="card">
<h2>关于风险职责</h2>
<p>在临床试验中不同角色承担着不同的职责和关注点RMO为各方提供针对性的风险管理支持确保临床试验过程中受试者的安全同时帮助各方更好地履行其职责</p>
<p>我们为申办者研究中心CXOCROCDMOSMO等各方提供专业的风险管理服务帮助各方识别风险评估风险管理风险确保临床试验的顺利进行</p>
</div>
</div>
</section>
<section class="section nav-section"> <section class="section nav-section">
<div class="container"> <div class="container">
<h2 class="section-title">各方职责</h2> <h2 class="section-title">之于患者安全各方风险管理职责</h2>
<p>在生命科学领域需要各方共同努力以保证受试者安全在缺少体系的情况下所有人都负责可能导致无人无法负责 </p>
<div class="nav-grid"> <div class="nav-grid">
<RouterLink to="/sponsor" class="nav-card"> <RouterLink to="/sponsor" class="nav-card">
<div class="nav-icon">💼</div> <div class="nav-icon">💼</div>
<h3>申办者职责</h3> <h3>申办者职责</h3>
<p>风险管理体系</p> <p>临床项目的发起人风险管理主要责任人</p>
</RouterLink> </RouterLink>
<RouterLink to="/holder" class="nav-card"> <RouterLink to="/Holder" class="nav-card">
<div class="nav-icon">📋</div> <div class="nav-icon">📋</div>
<h3>持有人职责</h3> <h3>持有人职责</h3>
<p>负责上市后药物安全</p> <p>上市后药物安全风险管理的主要责任人</p>
</RouterLink> </RouterLink>
<RouterLink to="/participant" class="nav-card"> <RouterLink to="/participant" class="nav-card">
<div class="nav-icon">👤</div> <div class="nav-icon">👤</div>
<h3>受试者专区</h3> <h3>受试者/患者</h3>
<p>临床试验介绍权益保障损害救济</p> <p>最直接利益相关方应为自身的安全与权益负责</p>
</RouterLink> </RouterLink>
<RouterLink to="/institution" class="nav-card"> <RouterLink to="/institution" class="nav-card">
<div class="nav-icon">🏥</div> <div class="nav-icon">🏥</div>
<h3>研究中心</h3> <h3>研究中心</h3>
<p>机构研究者伦理委员会支持</p> <p>风险管理第一线直面患者安全</p>
</RouterLink> </RouterLink>
<RouterLink to="/service-provider" class="nav-card"> <RouterLink to="/service-provider" class="nav-card">
<div class="nav-icon">🤝</div> <div class="nav-icon">🤝</div>
<h3>CXO职责</h3> <h3>RMO组织</h3>
<p>CROCDMOSMO支持服务</p> <p>风险管理方案风险管理能力的提供者</p>
</RouterLink> </RouterLink>
</div> </div>
</div> </div>
</section> </section>
<section class="section"> <section class="section diagram-section">
<div class="container"> <div class="container">
<div class="card"> <h2 class="section-title">RMO模式下风险管理职责</h2>
<h2>各方职责概述</h2> <div class="responsibility-diagram-wrap">
<div class="responsibility-grid"> <img
<div class="responsibility-item"> :src="diagramImageSrc"
<h3>申办者</h3> alt="RMO模式下风险管理职责职责梳理、方案设计、风险评估、核保谈判、事件管理、定损赔付、风险化解"
<ul> class="responsibility-diagram"
<li> 承担受试者保护的主要责任</li> />
<li> 提供法律上经济上的保险或保证</li>
<li> 建立完善的风险管理体系</li>
<li> 确保临床试验的质量和安全</li>
</ul>
</div>
<div class="responsibility-item">
<h3>持有人</h3>
<ul>
<li> 建立药物警戒体系开展上市后安全性监测</li>
<li> 收集评价和报告药品不良反应</li>
<li> 开展药品上市后研究持续评估风险与获益</li>
<li> 制定并实施药品风险管理计划</li>
</ul>
</div>
<div class="responsibility-item">
<h3>研究中心</h3>
<ul>
<li> 协助风险管理保障受试者安全</li>
<li> 研究者负责临床试验的执行</li>
<li> 伦理委员会负责伦理审查</li>
<li> 及时报告不良事件</li>
</ul>
</div>
<div class="responsibility-item">
<h3>受试者</h3>
<ul>
<li> 了解临床试验基本知识</li>
<li> 知情同意与权益保障</li>
<li> 损害救济与补偿机制</li>
</ul>
</div>
<div class="responsibility-item">
<h3>CXO服务方</h3>
<ul>
<li> CRO协助申办者进行临床试验</li>
<li> CDMO提供药物开发和生产服务</li>
<li> SMO提供现场管理服务</li>
<li> 协助风险管理流程的执行</li>
</ul>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
@ -105,6 +56,8 @@
<script setup lang="ts"> <script setup lang="ts">
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue' import PageHeader from '@/components/PageHeader.vue'
const diagramImageSrc = `/pic/${encodeURIComponent('职责与风险管理体系.jpg')}`
</script> </script>
<style scoped> <style scoped>

View File

@ -1,13 +1,153 @@
<template> <template>
<PageContainer> <PageContainer>
<div class="rmo-mode"> <div class="rmo-mode rmo-guarantee">
<PageHeader title="保证方案" description="专项基金、风险减量、外溢风险管理" /> <PageHeader title="保证方案" description="专项基金、风险减量、外溢风险管理" />
<div class="page-body"> <div class="page-body">
<section class="section"> <!-- 风险分层SUSAR/重大事件 vs AE/SAE治疗费用 -->
<section class="section section-risk-split">
<div class="container"> <div class="container">
<div class="card main-card"> <h2 class="section-title">临床试验责任的风险分层</h2>
<h2>保证方案</h2> <p class="section-subtitle">将风险分为两部分对应不同的保障方式</p>
<p>专项风险管理基金风险减量服务外溢风险管理服务</p> <div class="risk-split-cards">
<div class="risk-split-card risk-primary">
<h3>SUSAR 及其他重大事件</h3>
<p>身故伤残赔偿金责任明确金额较大证据链清晰通过<strong>保险</strong>进行风险转移</p>
</div>
<div class="risk-split-card risk-secondary">
<h3>AE / SAE 治疗费用</h3>
<p>高频发生的小额医疗费用通过<strong>保证资金</strong>存入华泰经纪管理的专用基金用于及时支付</p>
</div>
</div>
</div>
</section>
<!-- 临床试验责任的保证实现 -->
<section class="section section-guarantee-implementation">
<div class="container">
<h2 class="section-title">临床试验责任的保证实现</h2>
<div class="guarantee-flow">
<div class="guarantee-flow-item">
<h4>1. 保证资金 · 专用基金</h4>
<p>申办者支付风险管理费 35 /项目资金存入由<strong>华泰经纪管理</strong>的专用基金用于支付 AE/SAE 等高频小额治疗费用实行多退少补原则</p>
</div>
<div class="guarantee-flow-item">
<h4>2. 保险承保 · 首要风险</h4>
<p>通过保险进行 SUSAR 及其他重大事件风险的承保保障身故伤残等大额责任</p>
</div>
</div>
<div class="guarantee-logic-figure">
<h3>保证基金的基本逻辑</h3>
<img :src="imgGuaranteeLogic" alt="保证基金的基本逻辑:申办者、金融托管账号、医院与患者的关系流程" class="guarantee-logic-img" />
</div>
</div>
</section>
<!-- 保险与保证方案的比较 -->
<section class="section section-insurance-guarantee-compare">
<div class="container">
<h2 class="section-title">保险与保证方案的比较</h2>
<div class="guarantee-compare-table-wrap">
<table class="guarantee-compare-table">
<thead>
<tr>
<th>比较维度</th>
<th>保险</th>
<th>保证</th>
</tr>
</thead>
<tbody>
<tr>
<td class="compare-row-label">适用风险</td>
<td>SUSARSAE 等首要风险身故伤残赔偿金大额责任转移</td>
<td>AE/SAE 治疗费用高频小额医疗支出次要风险</td>
</tr>
<tr>
<td class="compare-row-label">合规性</td>
<td>受保险监管约束需符合保险法及银保监要求临床试验责任险需满足 GCP 及伦理审查对保障的要求</td>
<td>需符合 GCP 及机构内控要求资金用途与试验场景结合若由金融机构托管须满足金融监管与托管资质要求</td>
</tr>
<tr>
<td class="compare-row-label">灵活性</td>
<td>保障范围保额免赔等可在投保时约定产品形态相对标准化</td>
<td>基金规模使用规则触发条件可按试验与申办者需求设计可随试验进展调整拨付节奏与范围</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 华泰经纪基金管理 vs 申办者直接缴纳保证金的区别 -->
<section class="section section-fund-management-compare">
<div class="container">
<h2 class="section-title">由华泰经纪进行基金管理与申办者直接缴纳保证金的区别</h2>
<div class="guarantee-compare-table-wrap">
<table class="guarantee-compare-table">
<thead>
<tr>
<th>比较维度</th>
<th>华泰经纪管理的专用基金</th>
<th>申办者直接向机构缴纳保证金</th>
</tr>
</thead>
<tbody>
<tr>
<td class="compare-row-label">资金管理</td>
<td>华泰经纪统一管理多项目可设立总账户或分账户专业托管与对账服务资金安全用途透明</td>
<td>申办者需向各家研究中心分别缴纳每家中心独立账户操作分散对接成本高</td>
</tr>
<tr>
<td class="compare-row-label">支付时效</td>
<td>24 小时内响应华泰与医院受试者沟通及时安排救治与结算逐步实现医疗直付</td>
<td>依托院内流程申办者需对接多家中心审批与划款环节多时效受机构流程制约</td>
</tr>
<tr>
<td class="compare-row-label">增值服务</td>
<td>风险减量服务外溢风险管理临研安协助联系安抚安排就医沟通诉求专业理赔与定损支持</td>
<td>以试验执行为主较少提供资金托管对账风险减量等增值服务</td>
</tr>
<tr>
<td class="compare-row-label">适用场景</td>
<td>多中心试验需统一风险管理希望提升支付时效与患者体验希望获得专业外溢风险管理</td>
<td>单中心或中心数少机构要求申办者直接向本院专用账户存入资金</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- 相关服务风险减量外溢风险管理 -->
<section class="section section-related-services">
<div class="container">
<div class="service-item card main-card">
<div class="service-header">
<span class="service-icon">🔗</span>
<h2>相关服务</h2>
</div>
<div class="service-content">
<div class="related-service-columns">
<div class="related-service-column">
<h3>风险减量服务</h3>
<ul class="service-list">
<li>针对临床试验链条的各个环节进行风险点检查</li>
<li>人员培训提升风险管理能力</li>
<li>方案完善建议优化试验设计</li>
<li>主动识别和预防潜在风险</li>
</ul>
</div>
<div class="related-service-column">
<h3>外溢风险管理服务</h3>
<ul class="service-list">
<li>受试者出险后及时联系安抚</li>
<li>安排就医和医疗救治</li>
<li>沟通诉求和合理预期</li>
<li>正常情况下 24 小时内与医院受试者沟通并通知申办者</li>
<li>协调各方关系签署解决协议</li>
</ul>
</div>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
@ -17,10 +157,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue' import PageHeader from '@/components/PageHeader.vue'
const imgGuaranteeLogic = computed(() => `/pic/${encodeURIComponent('保证基金的基本逻辑.png')}`)
</script> </script>
<style scoped> <style scoped>
@import '@/pages/RmoMode.css'; @import '@/pages/RmoMode.css';
@import '@/pages/RmoModeGuarantee.css';
</style> </style>

View File

@ -1,36 +1,92 @@
<template> <template>
<PageContainer> <PageContainer>
<div class="rmo-mode"> <div class="rmo-mode rmo-insurance">
<PageHeader title="保险方案" description="为申办者提供全面的保险保障服务" /> <PageHeader title="保险方案" description="为申办者提供全面的保险保障服务" />
<div class="page-body"> <div class="page-body">
<section class="section section-get-quotes"> <!-- 获取报价申请理赔 -->
<section class="section section-action-links">
<div class="container"> <div class="container">
<div class="get-quotes-card card main-card"> <div class="action-links">
<h3 class="get-quotes-title">获取报价</h3> <RouterLink to="/dashboard/project-quotes" class="action-link">
<p class="get-quotes-desc"> <span class="action-link-icon">📋</span>
填写项目方案编号项目标题申办者项目分期可手动填写或上传项目方案由 AI 识别生成报价后可向各保司获取精准报价 <span class="action-link-text">获取报价</span>
</p> </RouterLink>
<div class="get-quotes-actions"> <RouterLink to="/dashboard/claims" class="action-link">
<button type="button" class="btn btn-primary get-quotes-btn" @click="handleGetQuote"> <span class="action-link-icon">📄</span>
获取报价 <span class="action-link-text">申请理赔</span>
</button> </RouterLink>
<button </div>
v-if="auth.isAuthenticated" </div>
type="button" </section>
class="btn btn-secondary get-quotes-btn"
@click="router.push('/dashboard/project-quotes')" <!-- 风险管理目的在于保护患者安全 -->
> <section class="section section-why-rm">
前往报价页面 <div class="container">
</button> <h2 class="section-title">风险管理目的在于保护患者安全</h2>
<p class="section-subtitle">从医疗机构的患者照护到研究团队的数据分析再到科学准确的研究结果验证最终实现长期患者安全</p>
<div class="why-rm-images">
<div class="why-rm-image-wrap">
<img :src="imgWhyRisk" alt="风险管理:项目中的患者安全、研究结果科学准确、长期患者安全" class="why-rm-image" />
</div>
</div>
<div class="insurance-flow-captions">
<span>项目中的患者安全</span>
<span>研究结果科学准确</span>
<span>长期患者安全</span>
</div>
</div>
</section>
<!-- 从保险到风险管理的逻辑变更 -->
<section class="section section-rmo-model">
<div class="container">
<h2 class="section-title">从保险到风险管理的逻辑变更</h2>
<p class="section-subtitle">生物医药临床试验风险管理 RMO 模式申办者受试者研究机构CRO保险公司经纪公司专业第三方协同保障</p>
<div class="insurance-image-wrap">
<img :src="imgRmoLogic" alt="从保险到风险管理的逻辑变更" class="insurance-diagram" />
</div>
<div class="rmo-roles-grid">
<div class="rmo-role-card">
<h4>保险公司</h4>
<p>提供清晰合理的主要风险保障体系和规则</p>
</div>
<div class="rmo-role-card">
<h4>经纪公司</h4>
<p>代表客户与保司进行承保风险翻译评估谈判管理外溢风险</p>
</div>
<div class="rmo-role-card">
<h4>CRO</h4>
<p>确保临床研究有效执行</p>
</div>
<div class="rmo-role-card">
<h4>药物安全公益基金会</h4>
<p>提供第三方独立评估意见</p>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<section class="section">
<!-- 从承保前的评估到出现后的介入处理全流程解决方案 -->
<section class="section section-claims-flow">
<div class="container"> <div class="container">
<div class="card main-card"> <h2 class="section-title">从承保前的评估到出现后的介入处理全流程解决方案总览</h2>
<h2>保险方案</h2> <p class="section-subtitle">临床试验外溢风险管理操作保险理赔标准操作的全流程协同</p>
<p>基础保障全面保障保险条款标准核心内容等</p> <div class="insurance-image-wrap">
<img :src="imgClaimsFlow" alt="理赔全流程:从承保前评估到出现后介入处理" class="insurance-diagram" />
</div>
<div class="claims-flow-summary">
<div class="flow-step-item">
<h4>临床试验</h4>
<p>受试者主诉 研究中心 SAE 报告申请理赔 申办者 SUSAR 报告 国家药监局</p>
</div>
<div class="flow-step-item">
<h4>外溢风险管理</h4>
<p>临研安收集信息处理建议与华泰经纪沟通谈判签署解决协议统一咨询服务电话4009-606-520</p>
</div>
<div class="flow-step-item">
<h4>保险理赔</h4>
<p>药盾公益基金独立评估保险公司接收理赔建议与评估完成定损赔付</p>
</div>
</div> </div>
</div> </div>
</section> </section>
@ -40,25 +96,16 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRouter } from 'vue-router' import { computed } from 'vue'
import { useAuthStore } from '@/stores/auth'
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue' import PageHeader from '@/components/PageHeader.vue'
const router = useRouter() const imgWhyRisk = computed(() => `/pic/${encodeURIComponent('why risk management.png')}`)
const auth = useAuthStore() const imgRmoLogic = computed(() => `/pic/${encodeURIComponent('从保险到风险管理页面的调整.png')}`)
const imgClaimsFlow = computed(() => `/pic/${encodeURIComponent('理赔全流程.png')}`)
function handleGetQuote() {
if (!auth.isAuthenticated) {
if (window.confirm('获取报价需先登录,是否前往登录?')) {
router.push({ path: '/login', query: { from: '/dashboard/project-quotes' } })
}
return
}
router.push('/dashboard/project-quotes')
}
</script> </script>
<style scoped> <style scoped>
@import '@/pages/RmoMode.css'; @import '@/pages/RmoMode.css';
@import '@/pages/RmoModeInsurance.css';
</style> </style>

View File

@ -1,14 +1,74 @@
<template> <template>
<PageContainer> <PageContainer>
<div class="solutions-page"> <div class="solutions-page solutions-clinical-insurance">
<PageHeader title="临床保险" description="为临床试验提供全面的保险保障服务" /> <PageHeader title="临床保险" description="为临床试验提供全面的保险保障服务" />
<div class="page-body"> <div class="page-body">
<!-- GCP 中的内容 -->
<section class="section gcp-section">
<div class="container">
<h2 class="section-title">GCP要求申办者提供保险或保证</h2>
<div class="gcp-card card">
<p class="gcp-source">药物临床试验质量管理规范第三十九条</p>
<p class="gcp-content">
申办者应当向研究者和临床试验机构提供与临床试验相关的法律上经济上的保险或者保证并与临床试验的风险性质和风险程度相适应但不包括研究者和临床试验机构自身的过失所致的损害
</p>
</div>
</div>
</section>
<!-- 清晰的保险与保证越来越重要 - 轮播 -->
<section class="section carousel-section">
<div class="container">
<h2 class="section-title">清晰的保险与保证越来越重要</h2>
<div class="company-carousel-container">
<div class="company-carousel-wrapper">
<div
class="company-carousel-track"
:style="{ transform: `translateX(-${currentSlide * 100}%)` }"
>
<div
v-for="(item, index) in companySummaries"
:key="index"
class="company-carousel-slide"
>
<div class="company-summary-card">
<h4 class="company-name">{{ item.company }}</h4>
<p class="company-summary">{{ item.summary }}</p>
</div>
</div>
</div>
</div>
<div class="carousel-controls">
<button
type="button"
class="carousel-btn carousel-btn-prev"
aria-label="上一项"
@click="prevSlide"
/>
<div class="carousel-indicators">
<button
v-for="(_, index) in companySummaries"
:key="index"
type="button"
:class="['carousel-indicator', { active: index === currentSlide }]"
:aria-label="`第${index + 1}项`"
@click="currentSlide = index"
/>
</div>
<button
type="button"
class="carousel-btn carousel-btn-next"
aria-label="下一项"
@click="nextSlide"
/>
</div>
</div>
</div>
</section>
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<div class="solutions-nav">
<RouterLink to="/rmo-mode/insurance" class="solution-nav-link">保险方案</RouterLink>
<RouterLink to="/rmo-mode/guarantee" class="solution-nav-link">保证设计</RouterLink>
</div>
<div class="solutions-content"> <div class="solutions-content">
<div class="solution-item"> <div class="solution-item">
<h3>基础保障</h3> <h3>基础保障</h3>
@ -25,16 +85,49 @@
</div> </div>
</div> </div>
</section> </section>
</div> </div>
</div> </div>
</PageContainer> </PageContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue' import PageHeader from '@/components/PageHeader.vue'
const currentSlide = ref(0)
const companySummaries = [
{
company: 'XX创新药企章总',
summary: '所有的临床试验都需要购买保险,以前我们对临床试验责任保险关注不多,也没发生过赔偿的案例,保险只是为伦理审批必须买;但随着创新药越来越多,我们也常常遇到参加试验后额外的治疗费用如何获得理赔?肿瘤患者死亡了,需要付赔偿金吗?如果我们不及时处理,研究中心可能会暂停我们的项目。'
},
{
company: 'XX药物上市许可持有人 质量部 李经理',
summary: '监管机构要求企业需要有风险赔偿能力,但如何才能算有风险赔偿能力?一个患者因不良反应要求赔偿,应该如何处理?如何保证企业合规、符合监管要求的风险赔偿能力?'
},
{
company: 'XX疫苗企业 药物警戒负责人 王总',
summary: '疫苗的赔偿责任比药品清晰,也需要有鉴定,但这些事件处理过程往往需要大量的人力、时间。我们很需要有独立第三方协助处理此类的患者纠纷。'
},
{
company: '上海XX医院',
summary: '我们希望申办者能配置专用账户用于患者的紧急救治比如患者用药后发生AE/SAE需要及时治疗。需要每个申办者向本院专用账号存入资金作为及时救治的资金保证。'
}
]
function prevSlide() {
currentSlide.value = currentSlide.value === 0 ? companySummaries.length - 1 : currentSlide.value - 1
}
function nextSlide() {
currentSlide.value = currentSlide.value === companySummaries.length - 1 ? 0 : currentSlide.value + 1
}
</script> </script>
<style scoped> <style scoped>
@import '@/pages/Solutions.css'; @import '@/pages/Solutions.css';
@import '@/pages/SolutionsClinicalInsurance.css';
</style> </style>

View File

@ -1,26 +1,84 @@
<template> <template>
<PageContainer> <PageContainer>
<div class="solutions-page"> <div class="solutions-page solutions-product-insurance">
<PageHeader title="产品责任" description="上市后药物安全与风险管理保障" /> <PageHeader title="产品保险" description="药品上市后的责任保险与风险管理保障" />
<div class="page-body"> <div class="page-body">
<section class="section"> <div class="section">
<div class="container"> <div class="container">
<div class="solutions-nav"> <div class="solutions-nav">
<RouterLink to="/post-market/insurance" class="solution-nav-link">保险方案</RouterLink> <RouterLink to="/post-market/insurance" class="solution-nav-link">保险方案</RouterLink>
<RouterLink to="/post-market/guarantee" class="solution-nav-link">保证设计</RouterLink> <RouterLink to="/post-market/guarantee" class="solution-nav-link">保证设计</RouterLink>
</div> </div>
</div>
</div>
<!-- 法规依据持有人责任 -->
<section class="section gcp-section">
<div class="container">
<h2 class="section-title">法规依据持有人责任</h2>
<div class="gcp-card card">
<p class="gcp-source">药品管理法</p>
<p class="gcp-content">
针对药品上市后的持有人责任持有人需要有风险管理能力承担药品安全责任产品保险为持有人提供上市后责任风险转移与保障
</p>
</div>
</div>
</section>
<!-- 上市后保险与保证越来越重要 - 轮播 -->
<section class="section carousel-section">
<div class="container">
<h2 class="section-title">上市后保险与保证越来越重要</h2>
<div class="company-carousel-container">
<div class="company-carousel-wrapper">
<div
class="company-carousel-track"
:style="{ transform: `translateX(-${currentSlide * 100}%)` }"
>
<div
v-for="(item, index) in companySummaries"
:key="index"
class="company-carousel-slide"
>
<div class="company-summary-card">
<h4 class="company-name">{{ item.company }}</h4>
<p class="company-summary">{{ item.summary }}</p>
</div>
</div>
</div>
</div>
<div class="carousel-controls">
<button type="button" class="carousel-btn carousel-btn-prev" aria-label="上一项" @click="prevSlide" />
<div class="carousel-indicators">
<button
v-for="(_, index) in companySummaries"
:key="index"
type="button"
:class="['carousel-indicator', { active: index === currentSlide }]"
:aria-label="`第${index + 1}项`"
@click="currentSlide = index"
/>
</div>
<button type="button" class="carousel-btn carousel-btn-next" aria-label="下一项" @click="nextSlide" />
</div>
</div>
</div>
</section>
<section class="section">
<div class="container">
<div class="solutions-content"> <div class="solutions-content">
<div class="solution-item"> <div class="solution-item">
<h3>上市后监测</h3> <h3>上市后监测</h3>
<p>持续监测上市后药物的安全性</p> <p>药物警戒体系持续监测上市后药品安全性为保险与保证提供数据支撑</p>
</div> </div>
<div class="solution-item"> <div class="solution-item">
<h3>风险管理</h3> <h3>风险管理</h3>
<p>系统化的风险管理措施和保障方案</p> <p>系统化的上市后风险管理措施保险与保证结合形成完整保障</p>
</div> </div>
<div class="solution-item"> <div class="solution-item">
<h3>合规保障</h3> <h3>合规保障</h3>
<p>确保符合上市后监管要求</p> <p>确保符合MAH制度及上市后监管要满足持有人责任保障需</p>
</div> </div>
</div> </div>
</div> </div>
@ -31,10 +89,37 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue' import PageHeader from '@/components/PageHeader.vue'
const currentSlide = ref(0)
const companySummaries = [
{
company: 'XX药物上市许可持有人 质量部 李经理',
summary: '监管机构要求企业需要有风险赔偿能力,但如何才能算有风险赔偿能力?一个患者因不良反应要求赔偿,应该如何处理?如何保证企业合规、符合监管要求的风险赔偿能力?'
},
{
company: 'XX疫苗企业 药物警戒负责人 王总',
summary: '疫苗的赔偿责任比药品清晰,也需要有鉴定,但这些事件处理过程往往需要大量的人力、时间。我们很需要有独立第三方协助处理此类的患者纠纷。'
},
{
company: 'XX创新药企 上市后质量团队',
summary: '药品上市后责任风险与临床试验不同我们需要针对MAH的产品责任保险方案同时希望有保证基金等方式应对日常不良反应相关的医疗费用补偿。'
}
]
function prevSlide() {
currentSlide.value = currentSlide.value === 0 ? companySummaries.length - 1 : currentSlide.value - 1
}
function nextSlide() {
currentSlide.value = currentSlide.value === companySummaries.length - 1 ? 0 : currentSlide.value + 1
}
</script> </script>
<style scoped> <style scoped>
@import '@/pages/Solutions.css'; @import '@/pages/Solutions.css';
@import '@/pages/SolutionsClinicalInsurance.css';
</style> </style>

View File

@ -16,56 +16,97 @@
</div> </div>
</div> </div>
</section> </section>
<section class="section duty-summary-section">
<div class="container">
<h2 class="section-title">安全性数据管理</h2>
<div class="duty-layout">
<div class="regulation-panel">
<div class="regulation-doc">
<!-- 可替换为实际法规文件图片<img src="@/assets/regulations/safety-report.png" alt="临床试验中安全性加速报告要求" /> -->
<div class="regulation-doc-placeHolder">
<span class="regulation-icon">📄</span>
<span class="regulation-name">临床试验中安全性加速报告要求</span>
</div>
</div>
</div>
<div class="duty-content">
<div class="duty-summary-card">
<p>申办者应当在药物临床试验期间进行<strong>持续的安全性评估</strong>并按照要求和时限进行报告</p>
<ul>
<li>收到任何来源的安全性相关信息后立即分析评估严重性与试验药物的相关性是否为预期事件</li>
<li>将SUSAR其他潜在的严重安全性风险信息<strong>快速报告</strong>药监部门主要研究者和伦理委员会</li>
<li>制定并更新<strong>研究者手册</strong>向研究者和伦理委员会提供试验方案及最新安全性资料</li>
<li>药物研发期间安全性更新报告DSUR需包含风险与获益评估</li>
<li>确保数据的可靠性可追溯性和安全性建立数据治理责任</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section duty-summary-section">
<div class="container">
<h2 class="section-title">知情同意书撰写</h2>
<div class="duty-layout">
<div class="regulation-panel">
<div class="regulation-doc">
<div class="regulation-doc-placeHolder">
<span class="regulation-icon">📄</span>
<span class="regulation-name">药物临床试验知情同意书伦理审查技术指导原则</span>
</div>
</div>
</div>
<div class="duty-content">
<div class="duty-summary-card">
<p>申办者应确保知情同意书等材料符合法规要求保障受试者充分知情</p>
<ul>
<li>向伦理委员会提交<strong>知情同意书</strong>等文件供审查</li>
<li>知情同意书应说明<strong>补偿方式数额和计划</strong>不得含有免除申办者责任的内容</li>
<li>剩余标本保存及数据保密等事项应在知情同意书中明确说明</li>
<li>可借助RMO的<strong>知情同意书审阅修改建议</strong>服务确保内容合规风险提示充分</li>
<li>获知可能影响受试者继续参加试验的新信息时及时告知并必要时再次签署</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section duty-summary-section">
<div class="container">
<h2 class="section-title">风险管理体系建立与运维</h2>
<div class="duty-layout">
<div class="regulation-panel">
<div class="regulation-doc">
<div class="regulation-doc-placeHolder">
<span class="regulation-icon">📄</span>
<span class="regulation-name">药物临床试验质量管理规范GCP质量保证与质量控制</span>
</div>
</div>
</div>
<div class="duty-content">
<div class="duty-summary-card">
<p>申办者应建立并持续运维基于风险的<strong>质量管理与风险管理体系</strong></p>
<ul>
<li>在试验开始前和整个试验过程中<strong>识别</strong>对关键质量因素有影响的风险<strong>评估</strong>其可能性与影响</li>
<li>建立实施和及时更新<strong>质量保证与质量控制</strong>相关书面SOP</li>
<li>委派合格监查员对临床试验实施监查采用基于风险的质量控制方法</li>
<li>记录已识别的风险和缓解措施与相关人员<strong>沟通</strong>定期<strong>审查</strong>风险控制措施</li>
<li>对临床试验全过程进行监督确保符合试验方案GCP及监管要求</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<h2 class="section-title">风险管理策略</h2>
<div class="card main-card"> <div class="card main-card">
<h2>风险管理体系</h2>
<div class="risk-section">
<h3>风险识别</h3>
<div class="risk-categories">
<div class="risk-category">
<h4>从临床试验药物角度</h4>
<ul>
<li><strong>AE不良事件</strong>为证明与药物相关</li>
<li><strong>ADR药物不良反应</strong>已证明与药物相关
<ul>
<li>SADR严重药物不良反应严重的ADR</li>
<li>ADR写入IB的RSI参考安全信息</li>
</ul>
</li>
<li><strong>SUSAR可疑且非预期的严重不良反应</strong>
<ul>
<li>新发现的严重不良反应需及时报告</li>
<li>未写入RSI的严重不良反应属于SUSAR</li>
</ul>
</li>
</ul>
</div>
<div class="risk-category">
<h4>从临床试验操作角度</h4>
<ul>
<li>临床试验方案合理性</li>
<li>临床试验方案操作是否符合规定</li>
<li>医疗行为是否合理</li>
<li>临床试验组织管理是否合理</li>
</ul>
</div>
<div class="risk-category">
<h4>其他相关风险</h4>
<ul>
<li>行为原则</li>
<li>心理原因</li>
<li>其他</li>
</ul>
</div>
</div>
</div>
<div class="risk-section">
<h3>风险评估</h3>
<p>华泰经纪协助申办者基于项目复杂度和风险度厘清首要风险与次要风险通过专业的风险评估工具和方法为项目制定合适的风险管理策略</p>
</div>
<div class="risk-section">
<h3>风险管理策略</h3>
<div class="strategy-grid"> <div class="strategy-grid">
<div class="strategy-card"> <div class="strategy-card">
<h4>自留策略</h4> <h4>自留策略</h4>
@ -80,37 +121,11 @@
<p>通过合同约定明确各方责任和风险分担</p> <p>通过合同约定明确各方责任和风险分担</p>
</div> </div>
</div> </div>
</div>
</div>
</div>
</section>
<section class="section process-section">
<div class="container">
<h2 class="section-title">操作流程</h2>
<div class="process-steps">
<div class="process-step">
<div class="step-number">1</div>
<h3>项目风险评估</h3>
<p>华泰经纪协助申办者基于项目复杂度和风险度厘清首要风险与次要风险</p>
</div>
<div class="process-step">
<div class="step-number">2</div>
<h3>投保流程</h3>
<p>通过华泰保险经纪完成首要风险投保</p>
</div>
<div class="process-step">
<div class="step-number">3</div>
<h3>专项风险管理基金设立</h3>
<p>申办者支付风险管理费设立专项风险管理基金3-5/项目</p>
</div>
<div class="process-step">
<div class="step-number">4</div>
<h3>理赔/补偿流程</h3>
<p>受试者出险后24小时内响应快速处理理赔和补偿</p>
</div>
</div> </div>
</div> </div>
</section> </section>
</div> </div>
</div> </div>
</PageContainer> </PageContainer>

View File

@ -14,7 +14,7 @@
<div class="stat-value">{{ data.inquiryProjects }}</div> <div class="stat-value">{{ data.inquiryProjects }}</div>
<div class="stat-details"><span>待处理询价项目</span></div> <div class="stat-details"><span>待处理询价项目</span></div>
</div> </div>
<RouterLink :to="isPolicyholder ? '/dashboard/projects' : '/dashboard/inquiries'" class="stat-link">查看详情 </RouterLink> <RouterLink :to="isPolicyHolder ? '/dashboard/projects' : '/dashboard/inquiries'" class="stat-link">查看详情 </RouterLink>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<div class="stat-icon">🛡</div> <div class="stat-icon">🛡</div>
@ -23,7 +23,7 @@
<div class="stat-value">{{ data.activeCoverage }}</div> <div class="stat-value">{{ data.activeCoverage }}</div>
<div class="stat-details"><span>已生效保障数量</span></div> <div class="stat-details"><span>已生效保障数量</span></div>
</div> </div>
<RouterLink :to="isPolicyholder ? '/dashboard/projects' : '/dashboard/inquiries'" class="stat-link">查看详情 </RouterLink> <RouterLink :to="isPolicyHolder ? '/dashboard/projects' : '/dashboard/inquiries'" class="stat-link">查看详情 </RouterLink>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<div class="stat-icon">📋</div> <div class="stat-icon">📋</div>
@ -32,10 +32,10 @@
<div class="stat-value">{{ data.allProjects }}</div> <div class="stat-value">{{ data.allProjects }}</div>
<div class="stat-details"><span>包括生效中及已失效的项目</span></div> <div class="stat-details"><span>包括生效中及已失效的项目</span></div>
</div> </div>
<RouterLink :to="isPolicyholder ? '/dashboard/projects' : '/dashboard/inquiries'" class="stat-link">查看详情 </RouterLink> <RouterLink :to="isPolicyHolder ? '/dashboard/projects' : '/dashboard/inquiries'" class="stat-link">查看详情 </RouterLink>
</div> </div>
</div> </div>
<section v-if="isPolicyholder" class="section"> <section v-if="isPolicyHolder" class="section">
<div class="container"> <div class="container">
<h2 class="section-title">快捷方式</h2> <h2 class="section-title">快捷方式</h2>
<div class="quick-actions"> <div class="quick-actions">
@ -107,10 +107,10 @@ import PageHeader from '@/components/PageHeader.vue'
const auth = useAuthStore() const auth = useAuthStore()
const isPolicyholder = computed(() => auth.user?.role === '投保人') const isPolicyHolder = computed(() => auth.user?.role === '投保人')
const isInsurer = computed(() => auth.user?.role === '保险人') const isInsurer = computed(() => auth.user?.role === '保险人')
const policyholderData = { const policyHolderData = {
inquiryProjects: 5, inquiryProjects: 5,
activeCoverage: 8, activeCoverage: 8,
allProjects: 15, allProjects: 15,
@ -130,7 +130,7 @@ const insurerData = {
] ]
} }
const data = computed(() => (isPolicyholder.value ? policyholderData : isInsurer.value ? insurerData : policyholderData)) const data = computed(() => (isPolicyHolder.value ? policyHolderData : isInsurer.value ? insurerData : policyHolderData))
</script> </script>
<style scoped> <style scoped>

View File

@ -28,15 +28,15 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="premium-coverageAmount">保障金额</label> <label for="premium-coverageAmount">保障金额</label>
<input id="premium-coverageAmount" v-model="formData.coverageAmount" type="number" placeholder="请输入保障金额" required /> <input id="premium-coverageAmount" v-model="formData.coverageAmount" type="number" placeHolder="请输入保障金额" required />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="premium-participantCount">受试者人数</label> <label for="premium-participantCount">受试者人数</label>
<input id="premium-participantCount" v-model="formData.participantCount" type="number" placeholder="请输入受试者人数" required /> <input id="premium-participantCount" v-model="formData.participantCount" type="number" placeHolder="请输入受试者人数" required />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="premium-duration">试验周期</label> <label for="premium-duration">试验周期</label>
<input id="premium-duration" v-model="formData.duration" type="number" placeholder="请输入试验周期" required /> <input id="premium-duration" v-model="formData.duration" type="number" placeHolder="请输入试验周期" required />
</div> </div>
<div class="privacy-commitment"> <div class="privacy-commitment">
<label class="privacy-checkbox-label"> <label class="privacy-checkbox-label">

View File

@ -40,35 +40,35 @@
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
<label for="pv-name">姓名 <span class="required">*</span></label> <label for="pv-name">姓名 <span class="required">*</span></label>
<input id="pv-name" v-model="pvForm.name" type="text" placeholder="请输入姓名" required /> <input id="pv-name" v-model="pvForm.name" type="text" placeHolder="请输入姓名" required />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="pv-email">电子邮箱 <span class="required">*</span></label> <label for="pv-email">电子邮箱 <span class="required">*</span></label>
<input id="pv-email" v-model="pvForm.email" type="email" placeholder="请输入电子邮箱" required /> <input id="pv-email" v-model="pvForm.email" type="email" placeHolder="请输入电子邮箱" required />
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
<label for="pv-phone">联系电话 <span class="required">*</span></label> <label for="pv-phone">联系电话 <span class="required">*</span></label>
<input id="pv-phone" v-model="pvForm.phone" type="tel" placeholder="请输入联系电话" required /> <input id="pv-phone" v-model="pvForm.phone" type="tel" placeHolder="请输入联系电话" required />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="pv-company">公司 <span class="required">*</span></label> <label for="pv-company">公司 <span class="required">*</span></label>
<input id="pv-company" v-model="pvForm.company" type="text" placeholder="请输入公司名称" required /> <input id="pv-company" v-model="pvForm.company" type="text" placeHolder="请输入公司名称" required />
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="pv-position">职位</label> <label for="pv-position">职位</label>
<input id="pv-position" v-model="pvForm.position" type="text" placeholder="请输入职位" /> <input id="pv-position" v-model="pvForm.position" type="text" placeHolder="请输入职位" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="pv-question">业务相关问题 <span class="required">*</span></label> <label for="pv-question">业务相关问题 <span class="required">*</span></label>
<textarea id="pv-question" v-model="pvForm.question" placeholder="请描述您的业务问题或需求" rows="4" required></textarea> <textarea id="pv-question" v-model="pvForm.question" placeHolder="请描述您的业务问题或需求" rows="4" required></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="pv-captcha">验证码 <span class="required">*</span></label> <label for="pv-captcha">验证码 <span class="required">*</span></label>
<div class="captcha-row"> <div class="captcha-row">
<input id="pv-captcha" v-model="pvForm.captcha" type="text" placeholder="请输入验证码" maxlength="4" required /> <input id="pv-captcha" v-model="pvForm.captcha" type="text" placeHolder="请输入验证码" maxlength="4" required />
<span class="captcha-code">{{ captchaCode }}</span> <span class="captcha-code">{{ captchaCode }}</span>
<button type="button" class="btn-captcha-refresh" @click="refreshCaptcha">刷新</button> <button type="button" class="btn-captcha-refresh" @click="refreshCaptcha">刷新</button>
</div> </div>
@ -125,23 +125,23 @@
<div class="form-grid form-grid-4"> <div class="form-grid form-grid-4">
<div class="form-group compact"> <div class="form-group compact">
<label>项目方案编号</label> <label>项目方案编号</label>
<input v-model="clinicalForm.projectCode" placeholder="CT-2025-001" /> <input v-model="clinicalForm.projectCode" placeHolder="CT-2025-001" />
</div> </div>
<div class="form-group compact"> <div class="form-group compact">
<label>项目标题</label> <label>项目标题</label>
<input v-model="clinicalForm.projectTitle" placeholder="试验方案标题" /> <input v-model="clinicalForm.projectTitle" placeHolder="试验方案标题" />
</div> </div>
<div class="form-group compact"> <div class="form-group compact">
<label>投保人</label> <label>投保人</label>
<input v-model="clinicalForm.policyholder" placeholder="投保人名称" /> <input v-model="clinicalForm.policyHolder" placeHolder="投保人名称" />
</div> </div>
<div class="form-group compact"> <div class="form-group compact">
<label>申办者</label> <label>申办者</label>
<input v-model="clinicalForm.sponsor" placeholder="申办者名称" /> <input v-model="clinicalForm.sponsor" placeHolder="申办者名称" />
</div> </div>
<div class="form-group compact"> <div class="form-group compact">
<label>受试药物</label> <label>受试药物</label>
<input v-model="clinicalForm.drugName" placeholder="受试药物名称" /> <input v-model="clinicalForm.drugName" placeHolder="受试药物名称" />
</div> </div>
<div class="form-group compact"> <div class="form-group compact">
<label>项目分期</label> <label>项目分期</label>
@ -156,7 +156,7 @@
</div> </div>
<div class="form-group compact"> <div class="form-group compact">
<label>受试者人数</label> <label>受试者人数</label>
<input v-model.number="clinicalForm.subjectCount" type="number" placeholder="人数" min="1" /> <input v-model.number="clinicalForm.subjectCount" type="number" placeHolder="人数" min="1" />
</div> </div>
<div class="form-group compact"> <div class="form-group compact">
<label>报价用途</label> <label>报价用途</label>
@ -185,9 +185,9 @@
<option value="30">30</option> <option value="30">30</option>
<option value="50">50</option> <option value="50">50</option>
</select> </select>
<input v-model="scheme.aggregateLimit" class="scheme-input" placeholder="累计限额(万)" /> <input v-model="scheme.aggregateLimit" class="scheme-input" placeHolder="累计限额(万)" />
<input v-model="scheme.deductible" class="scheme-input" placeholder="免赔额" /> <input v-model="scheme.deductible" class="scheme-input" placeHolder="免赔额" />
<input v-model.number="scheme.insuredCount" type="number" class="scheme-input scheme-num" placeholder="拟投保人数" /> <input v-model.number="scheme.insuredCount" type="number" class="scheme-input scheme-num" placeHolder="拟投保人数" />
</div> </div>
<button v-if="clinicalForm.schemes.length > 1" type="button" class="btn-remove" @click="removeScheme(idx)"></button> <button v-if="clinicalForm.schemes.length > 1" type="button" class="btn-remove" @click="removeScheme(idx)"></button>
</div> </div>
@ -221,7 +221,7 @@
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
<label for="product-productName">产品名称</label> <label for="product-productName">产品名称</label>
<input id="product-productName" v-model="productForm.productName" type="text" placeholder="请输入产品名称" /> <input id="product-productName" v-model="productForm.productName" type="text" placeHolder="请输入产品名称" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="product-productType">产品类型</label> <label for="product-productType">产品类型</label>
@ -236,16 +236,16 @@
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
<label for="product-coverage">保障金额</label> <label for="product-coverage">保障金额</label>
<input id="product-coverage" v-model="productForm.coverageAmount" type="number" placeholder="请输入保障金额" /> <input id="product-coverage" v-model="productForm.coverageAmount" type="number" placeHolder="请输入保障金额" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="product-sales">年销售额</label> <label for="product-sales">年销售额</label>
<input id="product-sales" v-model="productForm.annualSales" type="number" placeholder="请输入年销售额" /> <input id="product-sales" v-model="productForm.annualSales" type="number" placeHolder="请输入年销售额" />
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="product-remark">备注说明</label> <label for="product-remark">备注说明</label>
<textarea id="product-remark" v-model="productForm.remark" placeholder="请输入其他需求说明" rows="3"></textarea> <textarea id="product-remark" v-model="productForm.remark" placeHolder="请输入其他需求说明" rows="3"></textarea>
</div> </div>
<div class="privacy-commitment"> <div class="privacy-commitment">
<label class="privacy-checkbox-label"> <label class="privacy-checkbox-label">
@ -314,7 +314,7 @@ const hasProtocolFile = computed(() => !!protocolFile.value)
const clinicalForm = ref({ const clinicalForm = ref({
projectCode: '', projectCode: '',
projectTitle: '', projectTitle: '',
policyholder: '', policyHolder: '',
sponsor: '', sponsor: '',
drugName: '', drugName: '',
phase: '', phase: '',
@ -347,7 +347,7 @@ const productSubmitting = ref(false)
const canGenerateClinical = computed(() => const canGenerateClinical = computed(() =>
clinicalForm.value.projectCode.trim() && clinicalForm.value.projectCode.trim() &&
clinicalForm.value.projectTitle.trim() && clinicalForm.value.projectTitle.trim() &&
clinicalForm.value.policyholder.trim() && clinicalForm.value.policyHolder.trim() &&
clinicalForm.value.sponsor.trim() && clinicalForm.value.sponsor.trim() &&
clinicalForm.value.drugName.trim() && clinicalForm.value.drugName.trim() &&
clinicalForm.value.phase && clinicalForm.value.phase &&
@ -382,7 +382,7 @@ async function handleAiRecognize() {
await new Promise(r => setTimeout(r, 1200)) await new Promise(r => setTimeout(r, 1200))
clinicalForm.value.projectCode = 'CT-2025-' + Math.floor(1000 + Math.random() * 9000) clinicalForm.value.projectCode = 'CT-2025-' + Math.floor(1000 + Math.random() * 9000)
clinicalForm.value.projectTitle = protocolFile.value.name.replace(/\.[^.]+$/, '') || '临床试验方案' clinicalForm.value.projectTitle = protocolFile.value.name.replace(/\.[^.]+$/, '') || '临床试验方案'
clinicalForm.value.policyholder = clinicalForm.value.policyholder || '示例投保人' clinicalForm.value.policyHolder = clinicalForm.value.policyHolder || '示例投保人'
clinicalForm.value.sponsor = clinicalForm.value.sponsor || '示例申办者' clinicalForm.value.sponsor = clinicalForm.value.sponsor || '示例申办者'
clinicalForm.value.drugName = clinicalForm.value.drugName || '示例药物' clinicalForm.value.drugName = clinicalForm.value.drugName || '示例药物'
clinicalForm.value.phase = clinicalForm.value.phase || 'I' clinicalForm.value.phase = clinicalForm.value.phase || 'I'

View File

@ -40,10 +40,10 @@ import PageHeader from '@/components/PageHeader.vue'
const auth = useAuthStore() const auth = useAuthStore()
const isPolicyholder = computed(() => auth.user?.role === '投保人') const isPolicyHolder = computed(() => auth.user?.role === '投保人')
const tools = computed(() => { const tools = computed(() => {
const p = isPolicyholder.value const p = isPolicyHolder.value
return [ return [
{ id: 'premium-calculator', name: '保费测算工具', description: '根据项目信息、风险等级等参数计算保费', icon: '💰', path: '/dashboard/tools/premium-calculator', available: true }, { id: 'premium-calculator', name: '保费测算工具', description: '根据项目信息、风险等级等参数计算保费', icon: '💰', path: '/dashboard/tools/premium-calculator', available: true },
{ id: 'icf-editor', name: 'ICF智能修改', description: '智能辅助修改知情同意书ICF内容', icon: '📝', path: '/dashboard/tools/icf-editor', available: p }, { id: 'icf-editor', name: 'ICF智能修改', description: '智能辅助修改知情同意书ICF内容', icon: '📝', path: '/dashboard/tools/icf-editor', available: p },

View File

@ -71,7 +71,7 @@
<div class="info-grid"> <div class="info-grid">
<div class="info-item"> <div class="info-item">
<span class="info-label">投保人</span> <span class="info-label">投保人</span>
<span class="info-value">{{ task.policyholder }}</span> <span class="info-value">{{ task.policyHolder }}</span>
</div> </div>
<div class="info-item"> <div class="info-item">
<span class="info-label">被保险人/申办者</span> <span class="info-label">被保险人/申办者</span>
@ -124,7 +124,7 @@
</div> </div>
<div class="info-item"> <div class="info-item">
<span class="info-label">投保人</span> <span class="info-label">投保人</span>
<input v-model="standardQuote.policyholder" class="info-input" /> <input v-model="standardQuote.policyHolder" class="info-input" />
</div> </div>
<div class="info-item"> <div class="info-item">
<span class="info-label">被保险人</span> <span class="info-label">被保险人</span>
@ -231,7 +231,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label>综合评估说明</label> <label>综合评估说明</label>
<textarea v-model="riskForm.remark" rows="4" placeholder="补充说明"></textarea> <textarea v-model="riskForm.remark" rows="4" placeHolder="补充说明"></textarea>
</div> </div>
<div class="form-actions"> <div class="form-actions">
<button type="button" class="btn btn-primary" @click="submitRiskAssessment">提交</button> <button type="button" class="btn btn-primary" @click="submitRiskAssessment">提交</button>
@ -290,7 +290,7 @@
:value="getInsurerValue(q, dim.key)" :value="getInsurerValue(q, dim.key)"
@input="setInsurerValue(q, dim.key, ($event.target as HTMLInputElement).value)" @input="setInsurerValue(q, dim.key, ($event.target as HTMLInputElement).value)"
class="cell-input" class="cell-input"
placeholder="待填" placeHolder="待填"
/> />
</template> </template>
</td> </td>
@ -341,15 +341,15 @@
<p>填写 {{ task?.insurer }} 返回的正式报价数据</p> <p>填写 {{ task?.insurer }} 返回的正式报价数据</p>
<div class="form-group"> <div class="form-group">
<label>年保费</label> <label>年保费</label>
<input v-model="organizeForm.premium" type="text" placeholder="如¥12,800/年" /> <input v-model="organizeForm.premium" type="text" placeHolder="如¥12,800/年" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label>每人责任限额</label> <label>每人责任限额</label>
<input v-model="organizeForm.perPersonLimit" type="text" placeholder="如100万" /> <input v-model="organizeForm.perPersonLimit" type="text" placeHolder="如100万" />
</div> </div>
<div class="form-group"> <div class="form-group">
<label>累计责任限额</label> <label>累计责任限额</label>
<input v-model="organizeForm.aggregateLimit" type="text" placeholder="如500万" /> <input v-model="organizeForm.aggregateLimit" type="text" placeHolder="如500万" />
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@ -369,7 +369,7 @@
<div class="modal-body"> <div class="modal-body">
<div class="email-mockup"> <div class="email-mockup">
<p class="mockup-hint">此处接入 Smart-OPS 邮件管理界面</p> <p class="mockup-hint">此处接入 Smart-OPS 邮件管理界面</p>
<div class="mockup-placeholder"> <div class="mockup-placeHolder">
<p>收件人保司邮箱</p> <p>收件人保司邮箱</p>
<p>主题临床试验责任保险询价 - {{ task?.projectCode }}</p> <p>主题临床试验责任保险询价 - {{ task?.projectCode }}</p>
<p>正文标准报价信息将作为附件发送</p> <p>正文标准报价信息将作为附件发送</p>
@ -419,7 +419,7 @@
<p>确认最终方案和保司将报价结果返回给投保人并同步到项目报价页面</p> <p>确认最终方案和保司将报价结果返回给投保人并同步到项目报价页面</p>
<div class="form-group"> <div class="form-group">
<label>补充说明</label> <label>补充说明</label>
<textarea v-model="returnForm.remark" rows="3" placeholder="可选"></textarea> <textarea v-model="returnForm.remark" rows="3" placeHolder="可选"></textarea>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@ -439,7 +439,7 @@ import PageHeader from '@/components/PageHeader.vue'
interface QuoteTaskDetail { interface QuoteTaskDetail {
id: string id: string
policyholder: string policyHolder: string
sponsor: string sponsor: string
projectTitle: string projectTitle: string
projectCode: string projectCode: string
@ -469,7 +469,7 @@ const detailTab = ref<'quote' | 'risk' | 'organized'>('quote')
const standardQuote = ref({ const standardQuote = ref({
insuranceType: '临床试验责任保险', insuranceType: '临床试验责任保险',
policyholder: '', policyHolder: '',
sponsor: '', sponsor: '',
projectTitle: '', projectTitle: '',
projectCode: '', projectCode: '',
@ -667,7 +667,7 @@ function loadTask() {
// //
task.value = { task.value = {
id, id,
policyholder: '示例制药', policyHolder: '示例制药',
sponsor: '示例制药有限公司', sponsor: '示例制药有限公司',
projectTitle: 'XXX药物III期临床试验', projectTitle: 'XXX药物III期临床试验',
projectCode: 'CT-2025-1001', projectCode: 'CT-2025-1001',
@ -686,7 +686,7 @@ function loadTask() {
if (t) { if (t) {
standardQuote.value = { standardQuote.value = {
insuranceType: '临床试验责任保险', insuranceType: '临床试验责任保险',
policyholder: t.policyholder, policyHolder: t.policyHolder,
sponsor: t.sponsor, sponsor: t.sponsor,
projectTitle: t.projectTitle, projectTitle: t.projectTitle,
projectCode: t.projectCode, projectCode: t.projectCode,
@ -852,7 +852,7 @@ onMounted(loadTask)
margin: 0 0 12px 0; margin: 0 0 12px 0;
} }
.mockup-placeholder { .mockup-placeHolder {
padding: 16px; padding: 16px;
background: var(--bg-color, #f9fafb); background: var(--bg-color, #f9fafb);
border-radius: 8px; border-radius: 8px;

View File

@ -13,7 +13,7 @@
<input <input
v-model="keyword" v-model="keyword"
type="text" type="text"
placeholder="项目编号 / 项目标题" placeHolder="项目编号 / 项目标题"
class="search-input" class="search-input"
/> />
<select v-model="statusFilter" class="status-select"> <select v-model="statusFilter" class="status-select">
@ -54,7 +54,7 @@
</thead> </thead>
<tbody> <tbody>
<tr v-for="t in filteredTasks" :key="t.id"> <tr v-for="t in filteredTasks" :key="t.id">
<td>{{ t.policyholder }}</td> <td>{{ t.policyHolder }}</td>
<td>{{ t.projectCode }}</td> <td>{{ t.projectCode }}</td>
<td class="project-title">{{ t.projectTitle }}</td> <td class="project-title">{{ t.projectTitle }}</td>
<td>{{ t.mainLimit }}</td> <td>{{ t.mainLimit }}</td>
@ -183,9 +183,9 @@
<tr v-for="t in filteredTasks" :key="t.id"> <tr v-for="t in filteredTasks" :key="t.id">
<td>{{ t.insurer }}</td> <td>{{ t.insurer }}</td>
<td><span :class="['status-badge', t.status]">{{ statusText[t.status] }}</span></td> <td><span :class="['status-badge', t.status]">{{ statusText[t.status] }}</span></td>
<td><input v-model="organizeData[t.id].premium" type="text" placeholder="填写" class="organize-input" /></td> <td><input v-model="organizeData[t.id].premium" type="text" placeHolder="填写" class="organize-input" /></td>
<td><input v-model="organizeData[t.id].perPerson" type="text" placeholder="填写" class="organize-input" /></td> <td><input v-model="organizeData[t.id].perPerson" type="text" placeHolder="填写" class="organize-input" /></td>
<td><input v-model="organizeData[t.id].aggregate" type="text" placeholder="填写" class="organize-input" /></td> <td><input v-model="organizeData[t.id].aggregate" type="text" placeHolder="填写" class="organize-input" /></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -208,7 +208,7 @@ import PageHeader from '@/components/PageHeader.vue'
interface QuoteTask { interface QuoteTask {
id: string id: string
policyholder: string policyHolder: string
projectCode: string projectCode: string
projectTitle: string projectTitle: string
mainLimit: string mainLimit: string
@ -253,10 +253,10 @@ const filteredTasks = computed(() => {
function loadTasks() { function loadTasks() {
// //
tasks.value = [ tasks.value = [
{ id: '1', policyholder: '示例制药', projectCode: 'CT-2025-1001', projectTitle: 'XXX药物III期临床试验', mainLimit: '100万/人', addOnLimit: '500万', deductible: '0', insurer: '太平洋', status: 'completed' }, { id: '1', policyHolder: '示例制药', projectCode: 'CT-2025-1001', projectTitle: 'XXX药物III期临床试验', mainLimit: '100万/人', addOnLimit: '500万', deductible: '0', insurer: '太平洋', status: 'completed' },
{ id: '2', policyholder: '示例制药', projectCode: 'CT-2025-1001', projectTitle: 'XXX药物III期临床试验', mainLimit: '100万/人', addOnLimit: '500万', deductible: '0', insurer: '大地', status: 'formal' }, { id: '2', policyHolder: '示例制药', projectCode: 'CT-2025-1001', projectTitle: 'XXX药物III期临床试验', mainLimit: '100万/人', addOnLimit: '500万', deductible: '0', insurer: '大地', status: 'formal' },
{ id: '3', policyholder: '示例制药', projectCode: 'CT-2025-1001', projectTitle: 'XXX药物III期临床试验', mainLimit: '80万/人', addOnLimit: '400万', deductible: '0', insurer: '太平', status: 'preliminary' }, { id: '3', policyHolder: '示例制药', projectCode: 'CT-2025-1001', projectTitle: 'XXX药物III期临床试验', mainLimit: '80万/人', addOnLimit: '400万', deductible: '0', insurer: '太平', status: 'preliminary' },
{ id: '4', policyholder: '示例生物', projectCode: 'CT-2025-1002', projectTitle: 'YYY疫苗I期临床试验', mainLimit: '50万/人', addOnLimit: '200万', deductible: '0', insurer: '太保', status: 'created' } { id: '4', policyHolder: '示例生物', projectCode: 'CT-2025-1002', projectTitle: 'YYY疫苗I期临床试验', mainLimit: '50万/人', addOnLimit: '200万', deductible: '0', insurer: '太保', status: 'created' }
] ]
} }

View File

@ -104,7 +104,7 @@
<textarea <textarea
v-model="rejectReason" v-model="rejectReason"
rows="3" rows="3"
placeholder="请输入驳回原因,便于用户补充或修改" placeHolder="请输入驳回原因,便于用户补充或修改"
class="reject-textarea" class="reject-textarea"
/> />
</div> </div>

BIN
网站内容.xlsx Normal file

Binary file not shown.