feat: 应急就医多端原型优化、大屏管理模块新增及PRD文档截图更新

- 操作人员端:救护车支援toast+800ms跳转、工作台Tab红点徽标
- 员工端:资源地图拖动搜索、单位联系人移除删除按钮、聊天页参与人员移除
- 管理端应急资源:AED地图重构(搜索/缩放/定位/拖动搜索)、布点/急救包新增布放图片、
  培演记录附件改为网格上传、取证人员表单简化、布点列表移除批量导入
- 管理端应急管理:应急中心表单字段调整(上班时间必填/短信发送电话)、
  工单新增from=call模式、大病就医详情已确认状态支持取消预约、
  新增大屏管理二级菜单(大屏数据配置+大屏通话记录)、侧边栏全量同步
- 其他:异常结果定义文案更新、专家推荐文案调整、聊天工具栏移除语音通话按钮
- PRD文档同步更新、全部修改页面截图重新截取

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-09 18:12:16 +08:00
co-authored by Claude Opus 4.6
parent 80ead4d72b
commit fb6821b279
55 changed files with 979 additions and 248 deletions
+6 -4
View File
@@ -140,13 +140,15 @@ stateDiagram-v2
- 模拟急救面板提供心梗、猝死、脑梗/脑出血三种疾病场景
**交互说明:**
- 默认显示当前员工位置(未获取到员工位置以长庆油田公司机关大楼为中心点)附近50公里内的资源,如果50公里内没有数据,那么就查最近距离前10的资源
- 地图拖动后,地图上方显示“在此区域内搜索”的按钮,点击后查询该区域内(以地图中心点)50公里内的资源
- 点击地图标记点,弹出底部资源概要卡片
- 点击顶部Tab栏切换资源类型,跳转到对应资源地图页面
- 点击资源卡片中的"联系电话"按钮,调起系统拨号
- 点击资源卡片中的"地图导航"按钮,调起地图导航
- 点击"模拟急救"按钮,展开疾病选择面板
- 点击疾病标签(心梗/猝死/脑梗),跳转到对应急救提示书页面
- 点击右侧定位按钮,重新定位到当前位置
- 点击右侧定位按钮,重新定位到当前位置,并重新调取附近资源
- 点击 +/ 按钮,缩放地图
---
@@ -293,9 +295,9 @@ stateDiagram-v2
- 参与本次应急视频通话的操作人员和专业人员选定规则:
* 根据员工所在单位反查所属的应急中心(若该单位未分配到指定的应急中心则使用默认应急中心)
* 查询当前应急中心的排班人员加到应急通话(若当前应急中心未设置排班则使用默认应急中心的排班设置)
* 若应急中心下同时存在多个班人员优先查找处于空闲状态下的班人员加入视频通话
* 若多个班人员都处于空闲状态使用轮询策略
* 若当前应急中心和默认应急中心都没有设置班人员 则无法发起应急通话,提示“暂无应急值班人员”
* 若应急中心下同时存在多个班人员优先查找处于空闲状态下的班人员加入视频通话
* 若多个班人员都处于空闲状态使用轮询策略
* 若当前应急中心和默认应急中心都没有设置班人员 则无法发起应急通话,提示“暂无应急值班人员”
- 聊天区域展示三种消息类型:系统消息(居中灰色)、员工消息(右侧蓝色气泡)、对方消息(左侧白色气泡)
- 参与人员列表展示通话参与者的姓名和角色(如”吴凤-主治医师”、”张旭-操作人员”)
- 底部输入栏包含【+更多功能】、文字输入、语音输入以及发送按钮
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 33 KiB

@@ -129,24 +129,6 @@
</div>
</div>
<!-- 参与人员 -->
<div class="participants">
<div class="participants__list">
<div class="participant">
<div class="participant__avatar">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5"><circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 4-7 8-7s8 3 8 7"/></svg>
</div>
<div class="participant__name">吴凤(主治医师)</div>
<!-- <div class="participant__status">正在处理</div>-->
</div>
<div class="participant">
<div class="participant__avatar">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5"><circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 4-7 8-7s8 3 8 7"/></svg>
</div>
<div class="participant__name">张旭(操作人员)</div>
</div>
</div>
</div>
<!-- 底部输入区域 -->
<div class="chat-input-area">
@@ -32,6 +32,10 @@
.map-me { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 4; }
.map-me__dot { width: 14px; height: 14px; background: #1677ff; border: 3px solid #fff; border-radius: 50%; box-shadow: 0 0 0 6px rgba(22,119,255,0.2); }
/* 拖动搜索按钮 */
.map-search-btn { position: absolute; top: 12px; left: 50%; transform: translateX(-50%); z-index: 6; background: #fff; border: none; border-radius: 20px; padding: 8px 16px; font-size: 13px; color: #1677ff; font-weight: 600; box-shadow: 0 2px 12px rgba(0,0,0,0.15); cursor: pointer; display: none; align-items: center; gap: 4px; white-space: nowrap; }
.map-search-btn--visible { display: flex; }
/* 地图控件 */
.map-locate { position: absolute; top: 12px; right: 12px; width: 36px; height: 36px; background: #fff; border-radius: 50%; box-shadow: 0 2px 8px rgba(0,0,0,0.15); display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 5; }
.map-zoom { position: absolute; top: 56px; right: 12px; display: flex; flex-direction: column; background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.15); overflow: hidden; z-index: 5; }
@@ -68,6 +72,10 @@
.sim-tag:active { background: #e8f4ff; border-color: #1677ff; color: #1677ff; }
.simulate-panel__close { font-size: 13px; color: #1677ff; cursor: pointer; text-align: center; }
/* 新搜索结果标记 */
.map-pin--new { animation: pinDrop 0.3s ease; }
@keyframes pinDrop { from { opacity: 0; transform: translate(-50%, -120%) scale(0.6); } to { opacity: 1; transform: translate(-50%, -100%) scale(1); } }
.bottom-spacer { height: 34px; flex-shrink: 0; }
</style>
</head>
@@ -108,7 +116,9 @@
</div>
<!-- 地图区域 -->
<div class="map-area">
<div class="map-area" id="mapArea">
<!-- 拖动后搜索按钮 -->
<button class="map-search-btn" id="searchBtn" onclick="searchInArea()">在此区域内搜索🔍</button>
<!-- 地图控件 -->
<div class="map-locate"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#1677ff" stroke-width="2"><circle cx="12" cy="12" r="4"/><path d="M12 2v4M12 18v4M2 12h4M18 12h4"/></svg></div>
<div class="map-zoom">
@@ -140,5 +150,55 @@
</div>
</div>
</div>
<script>
(function() {
var mapArea = document.getElementById('mapArea');
var searchBtn = document.getElementById('searchBtn');
var isDragging = false;
var startX, startY, hasMoved = false;
mapArea.addEventListener('mousedown', function(e) { startDrag(e.clientX, e.clientY); });
mapArea.addEventListener('touchstart', function(e) { var t = e.touches[0]; startDrag(t.clientX, t.clientY); }, { passive: true });
document.addEventListener('mousemove', function(e) { onMove(e.clientX, e.clientY); });
document.addEventListener('touchmove', function(e) { var t = e.touches[0]; onMove(t.clientX, t.clientY); }, { passive: true });
document.addEventListener('mouseup', endDrag);
document.addEventListener('touchend', endDrag);
function startDrag(x, y) { isDragging = true; startX = x; startY = y; hasMoved = false; }
function onMove(x, y) {
if (!isDragging) return;
if (Math.abs(x - startX) > 10 || Math.abs(y - startY) > 10) hasMoved = true;
}
function endDrag() {
if (isDragging && hasMoved) {
searchBtn.classList.add('map-search-btn--visible');
}
isDragging = false;
}
window.searchInArea = function() {
searchBtn.classList.remove('map-search-btn--visible');
// 移除旧的搜索结果标记
document.querySelectorAll('.map-pin--new').forEach(function(el) { el.remove(); });
// 模拟新搜索结果
var results = [
{ type: 'hospital', top: '28%', left: '65%', label: '+' },
{ type: 'clinic', top: '55%', left: '30%', label: '诊' },
{ type: 'aed', top: '42%', left: '72%', label: 'A' }
];
results.forEach(function(r) {
var pin = document.createElement('div');
pin.className = 'map-pin map-pin--' + r.type + ' map-pin--new';
pin.style.top = r.top;
pin.style.left = r.left;
pin.innerHTML = '<div class="map-pin__dot"><svg width="28" height="34" viewBox="0 0 28 34"><path d="M14 0C6.3 0 0 6.3 0 14c0 10.5 14 20 14 20s14-9.5 14-20C28 6.3 21.7 0 14 0z" fill="currentColor"/><text x="14" y="18" text-anchor="middle" fill="#fff" font-size="12" font-weight="700">' + r.label + '</text></svg></div>';
mapArea.appendChild(pin);
});
};
})();
</script>
</body>
</html>
+1 -1
View File
@@ -190,7 +190,7 @@ function render() {
if (unitContact) {
html += '<div class="contact-card">';
html += '<div class="contact-card__row"><span class="contact-card__name">' + unitContact.name + '</span>';
html += '<div class="contact-card__actions"><span class="contact-card__btn contact-card__btn--edit" onclick="editUnit()">编辑</span><span class="contact-card__btn contact-card__btn--delete" onclick="deleteContact(\'unit\',-1)">删除</span></div></div>';
html += '<div class="contact-card__actions"><span class="contact-card__btn contact-card__btn--edit" onclick="editUnit()">编辑</span></div></div>';
html += '<div class="contact-card__row"><span class="contact-card__label">单位</span><span class="contact-card__value">' + unitContact.company + '</span></div>';
html += '<div class="contact-card__row"><span class="contact-card__label">部门</span><span class="contact-card__value">' + unitContact.dept + '</span></div>';
html += '<div class="contact-card__row"><span class="contact-card__label">电话</span><span class="contact-card__value">' + unitContact.phone + '</span></div>';