feat(login): 添加租户名称获取功能并优化前端布局
- 在登录控制器中注入租户服务并获取租户名称信息 - 添加租户名称到登录响应结果中 - 更新样式变量定义侧边栏宽度和Logo高度 - 重构公告面板组件统一公告通知显示逻辑 - 简化公告类型图标和样式映射关系 - 更新侧边栏为垂直菜单布局并添加折叠功能 - 优化Logo组件显示租户名称和系统标题 - 调整导航栏布局结构和响应式样式 - 重构主应用容器样式和标签页显示逻辑
@@ -1,39 +0,0 @@
|
|||||||
import java.io.IOException;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
public class TestDeleteInspectionType {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
try {
|
|
||||||
// 测试删除ID为1的检验类型
|
|
||||||
long inspectionTypeId = 1;
|
|
||||||
URL url = new URL("http://localhost:8080/system/inspection-type/" + inspectionTypeId);
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
||||||
connection.setRequestMethod("DELETE");
|
|
||||||
connection.setRequestProperty("Content-Type", "application/json");
|
|
||||||
connection.setRequestProperty("Accept", "application/json");
|
|
||||||
|
|
||||||
// 发送请求
|
|
||||||
int responseCode = connection.getResponseCode();
|
|
||||||
System.out.println("响应代码: " + responseCode);
|
|
||||||
|
|
||||||
// 读取响应
|
|
||||||
Scanner scanner;
|
|
||||||
if (responseCode >= 200 && responseCode < 300) {
|
|
||||||
scanner = new Scanner(connection.getInputStream());
|
|
||||||
} else {
|
|
||||||
scanner = new Scanner(connection.getErrorStream());
|
|
||||||
}
|
|
||||||
|
|
||||||
String response = scanner.useDelimiter("\\A").next();
|
|
||||||
System.out.println("响应内容: " + response);
|
|
||||||
|
|
||||||
scanner.close();
|
|
||||||
connection.disconnect();
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,7 @@ import com.core.framework.web.service.SysLoginService;
|
|||||||
import com.core.framework.web.service.SysPermissionService;
|
import com.core.framework.web.service.SysPermissionService;
|
||||||
import com.core.framework.web.service.TokenService;
|
import com.core.framework.web.service.TokenService;
|
||||||
import com.core.system.service.ISysMenuService;
|
import com.core.system.service.ISysMenuService;
|
||||||
|
import com.core.system.service.ISysTenantService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -39,6 +40,9 @@ public class SysLoginController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TokenService tokenService;
|
private TokenService tokenService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ISysTenantService tenantService;
|
||||||
|
|
||||||
/**已评审
|
/**已评审
|
||||||
* 登录方法
|
* 登录方法
|
||||||
*
|
*
|
||||||
@@ -72,12 +76,21 @@ public class SysLoginController {
|
|||||||
loginUser.setPermissions(permissions);
|
loginUser.setPermissions(permissions);
|
||||||
tokenService.refreshToken(loginUser);
|
tokenService.refreshToken(loginUser);
|
||||||
}
|
}
|
||||||
|
// 获取租户名称
|
||||||
|
String tenantName = null;
|
||||||
|
if (loginUser.getTenantId() != null) {
|
||||||
|
com.core.system.domain.SysTenant tenant = tenantService.getById(loginUser.getTenantId());
|
||||||
|
if (tenant != null) {
|
||||||
|
tenantName = tenant.getTenantName();
|
||||||
|
}
|
||||||
|
}
|
||||||
AjaxResult ajax = AjaxResult.success();
|
AjaxResult ajax = AjaxResult.success();
|
||||||
ajax.put("optionJson", loginUser.getOptionJson());
|
ajax.put("optionJson", loginUser.getOptionJson());
|
||||||
ajax.put("practitionerId", String.valueOf(loginUser.getPractitionerId()));
|
ajax.put("practitionerId", String.valueOf(loginUser.getPractitionerId()));
|
||||||
ajax.put("user", user);
|
ajax.put("user", user);
|
||||||
ajax.put("roles", roles);
|
ajax.put("roles", roles);
|
||||||
ajax.put("permissions", permissions);
|
ajax.put("permissions", permissions);
|
||||||
|
ajax.put("tenantName", tenantName);
|
||||||
return ajax;
|
return ajax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.openhis.web.clinicalmanage.appservice;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import com.openhis.web.clinicalmanage.dto.SurgeryDto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术管理应用Service接口
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-12-30
|
||||||
|
*/
|
||||||
|
public interface ISurgeryAppService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询手术列表
|
||||||
|
*
|
||||||
|
* @param surgeryDto 查询条件
|
||||||
|
* @param pageNo 当前页
|
||||||
|
* @param pageSize 每页条数
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
IPage<SurgeryDto> getSurgeryPage(SurgeryDto surgeryDto, Integer pageNo, Integer pageSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询手术详情
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 手术详情
|
||||||
|
*/
|
||||||
|
R<SurgeryDto> getSurgeryDetail(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增手术信息
|
||||||
|
*
|
||||||
|
* @param surgeryDto 手术信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
R<?> addSurgery(SurgeryDto surgeryDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改手术信息
|
||||||
|
*
|
||||||
|
* @param surgeryDto 手术信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
R<?> updateSurgery(SurgeryDto surgeryDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除手术信息
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
R<?> deleteSurgery(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新手术状态
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @param statusEnum 状态
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
R<?> updateSurgeryStatus(Long id, Integer statusEnum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
package com.openhis.web.clinicalmanage.appservice.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
|
import com.openhis.administration.domain.Patient;
|
||||||
|
import com.openhis.administration.service.IPatientService;
|
||||||
|
import com.openhis.common.constant.PromptMsgConstant;
|
||||||
|
import com.openhis.clinical.domain.Surgery;
|
||||||
|
import com.openhis.clinical.service.ISurgeryService;
|
||||||
|
import com.openhis.common.utils.HisQueryUtils;
|
||||||
|
import com.openhis.web.clinicalmanage.appservice.ISurgeryAppService;
|
||||||
|
import com.openhis.web.clinicalmanage.dto.SurgeryDto;
|
||||||
|
import com.openhis.web.clinicalmanage.mapper.SurgeryAppMapper;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术管理应用Service业务层处理
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-12-30
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SurgeryAppMapper surgeryAppMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ISurgeryService surgeryService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IPatientService patientService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询手术列表
|
||||||
|
*
|
||||||
|
* @param surgeryDto 查询条件
|
||||||
|
* @param pageNo 当前页
|
||||||
|
* @param pageSize 每页条数
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IPage<SurgeryDto> getSurgeryPage(SurgeryDto surgeryDto, Integer pageNo, Integer pageSize) {
|
||||||
|
QueryWrapper<SurgeryDto> queryWrapper = HisQueryUtils.buildQueryWrapper(surgeryDto, null,
|
||||||
|
new HashSet<String>() {{
|
||||||
|
add("surgery_no");
|
||||||
|
add("surgery_name");
|
||||||
|
add("patient_name");
|
||||||
|
add("main_surgeon_name");
|
||||||
|
add("anesthetist_name");
|
||||||
|
add("org_name");
|
||||||
|
}}, null);
|
||||||
|
queryWrapper.orderByDesc("create_time");
|
||||||
|
return surgeryAppMapper.getSurgeryPage(new Page<>(pageNo, pageSize), queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询手术详情
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 手术详情
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public R<SurgeryDto> getSurgeryDetail(Long id) {
|
||||||
|
SurgeryDto surgeryDto = surgeryAppMapper.getSurgeryDetail(id);
|
||||||
|
if (surgeryDto == null) {
|
||||||
|
return R.fail("手术信息不存在");
|
||||||
|
}
|
||||||
|
return R.ok(surgeryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增手术信息
|
||||||
|
*
|
||||||
|
* @param surgeryDto 手术信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public R<?> addSurgery(SurgeryDto surgeryDto) {
|
||||||
|
// 校验患者是否存在
|
||||||
|
Patient patient = patientService.getById(surgeryDto.getPatientId());
|
||||||
|
if (patient == null) {
|
||||||
|
return R.fail("患者信息不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为实体对象
|
||||||
|
Surgery surgery = new Surgery();
|
||||||
|
BeanUtils.copyProperties(surgeryDto, surgery);
|
||||||
|
|
||||||
|
Long surgeryId = surgeryService.insertSurgery(surgery);
|
||||||
|
return R.ok(surgeryId, MessageUtils.createMessage(PromptMsgConstant.Common.M00001, new Object[]{"手术信息"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改手术信息
|
||||||
|
*
|
||||||
|
* @param surgeryDto 手术信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public R<?> updateSurgery(SurgeryDto surgeryDto) {
|
||||||
|
// 校验手术是否存在
|
||||||
|
Surgery existSurgery = surgeryService.getById(surgeryDto.getId());
|
||||||
|
if (existSurgery == null) {
|
||||||
|
return R.fail("手术信息不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为实体对象
|
||||||
|
Surgery surgery = new Surgery();
|
||||||
|
BeanUtils.copyProperties(surgeryDto, surgery);
|
||||||
|
|
||||||
|
surgeryService.updateSurgery(surgery);
|
||||||
|
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00002, new Object[]{"手术信息"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除手术信息
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public R<?> deleteSurgery(Long id) {
|
||||||
|
// 校验手术是否存在
|
||||||
|
Surgery existSurgery = surgeryService.getById(id);
|
||||||
|
if (existSurgery == null) {
|
||||||
|
return R.fail("手术信息不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已完成的手术不能删除
|
||||||
|
if (existSurgery.getStatusEnum() == 3) {
|
||||||
|
return R.fail("已完成的手术不能删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
surgeryService.deleteSurgery(id);
|
||||||
|
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00005, new Object[]{"手术信息"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新手术状态
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @param statusEnum 状态
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public R<?> updateSurgeryStatus(Long id, Integer statusEnum) {
|
||||||
|
// 校验手术是否存在
|
||||||
|
Surgery existSurgery = surgeryService.getById(id);
|
||||||
|
if (existSurgery == null) {
|
||||||
|
return R.fail("手术信息不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
surgeryService.updateSurgeryStatus(id, statusEnum);
|
||||||
|
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[]{"手术状态"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.openhis.web.clinicalmanage.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import com.openhis.web.clinicalmanage.appservice.ISurgeryAppService;
|
||||||
|
import com.openhis.web.clinicalmanage.dto.SurgeryDto;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术管理Controller业务层处理
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-12-30
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/clinical-manage/surgery")
|
||||||
|
@Slf4j
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SurgeryController {
|
||||||
|
|
||||||
|
private final ISurgeryAppService surgeryAppService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询手术列表
|
||||||
|
*
|
||||||
|
* @param surgeryDto 查询条件
|
||||||
|
* @param pageNo 当前页
|
||||||
|
* @param pageSize 每页条数
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
@GetMapping(value = "/surgery-page")
|
||||||
|
public R<IPage<SurgeryDto>> getSurgeryPage(SurgeryDto surgeryDto,
|
||||||
|
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||||
|
IPage<SurgeryDto> page = surgeryAppService.getSurgeryPage(surgeryDto, pageNo, pageSize);
|
||||||
|
return R.ok(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询手术详情
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 手术详情
|
||||||
|
*/
|
||||||
|
@GetMapping(value = "/surgery-detail")
|
||||||
|
public R<SurgeryDto> getSurgeryDetail(@RequestParam Long id) {
|
||||||
|
return surgeryAppService.getSurgeryDetail(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增手术信息
|
||||||
|
*
|
||||||
|
* @param surgeryDto 手术信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@PostMapping(value = "/surgery")
|
||||||
|
public R<?> addSurgery(@RequestBody SurgeryDto surgeryDto) {
|
||||||
|
return surgeryAppService.addSurgery(surgeryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改手术信息
|
||||||
|
*
|
||||||
|
* @param surgeryDto 手术信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@PutMapping(value = "/surgery")
|
||||||
|
public R<?> updateSurgery(@RequestBody SurgeryDto surgeryDto) {
|
||||||
|
return surgeryAppService.updateSurgery(surgeryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除手术信息
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@DeleteMapping(value = "/surgery")
|
||||||
|
public R<?> deleteSurgery(@RequestParam Long id) {
|
||||||
|
return surgeryAppService.deleteSurgery(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新手术状态
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @param statusEnum 状态
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@PutMapping(value = "/surgery-status")
|
||||||
|
public R<?> updateSurgeryStatus(@RequestParam Long id, @RequestParam Integer statusEnum) {
|
||||||
|
return surgeryAppService.updateSurgeryStatus(id, statusEnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
package com.openhis.web.clinicalmanage.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import com.openhis.common.annotation.Dict;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术管理DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class SurgeryDto {
|
||||||
|
|
||||||
|
/** ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 手术编号 */
|
||||||
|
private String surgeryNo;
|
||||||
|
|
||||||
|
/** 患者ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long patientId;
|
||||||
|
|
||||||
|
/** 患者姓名 */
|
||||||
|
private String patientName;
|
||||||
|
|
||||||
|
/** 患者性别 */
|
||||||
|
private String patientGender;
|
||||||
|
|
||||||
|
/** 患者年龄 */
|
||||||
|
private String patientAge;
|
||||||
|
|
||||||
|
/** 就诊ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long encounterId;
|
||||||
|
|
||||||
|
/** 就诊流水号 */
|
||||||
|
private String encounterNo;
|
||||||
|
|
||||||
|
/** 手术名称 */
|
||||||
|
private String surgeryName;
|
||||||
|
|
||||||
|
/** 手术编码 */
|
||||||
|
private String surgeryCode;
|
||||||
|
|
||||||
|
/** 手术类型编码 */
|
||||||
|
@Dict(dictCode = "surgery_type")
|
||||||
|
private Integer surgeryTypeEnum;
|
||||||
|
private String surgeryTypeEnum_dictText;
|
||||||
|
|
||||||
|
/** 手术等级 */
|
||||||
|
@Dict(dictCode = "surgery_level")
|
||||||
|
private Integer surgeryLevel;
|
||||||
|
private String surgeryLevel_dictText;
|
||||||
|
|
||||||
|
/** 手术状态 */
|
||||||
|
@Dict(dictCode = "surgery_status")
|
||||||
|
private Integer statusEnum;
|
||||||
|
private String statusEnum_dictText;
|
||||||
|
|
||||||
|
/** 计划手术时间 */
|
||||||
|
private Date plannedTime;
|
||||||
|
|
||||||
|
/** 实际开始时间 */
|
||||||
|
private Date actualStartTime;
|
||||||
|
|
||||||
|
/** 实际结束时间 */
|
||||||
|
private Date actualEndTime;
|
||||||
|
|
||||||
|
/** 主刀医生ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long mainSurgeonId;
|
||||||
|
|
||||||
|
/** 主刀医生姓名 */
|
||||||
|
private String mainSurgeonName;
|
||||||
|
|
||||||
|
/** 助手1 ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long assistant1Id;
|
||||||
|
|
||||||
|
/** 助手1 姓名 */
|
||||||
|
private String assistant1Name;
|
||||||
|
|
||||||
|
/** 助手2 ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long assistant2Id;
|
||||||
|
|
||||||
|
/** 助手2 姓名 */
|
||||||
|
private String assistant2Name;
|
||||||
|
|
||||||
|
/** 麻醉医生ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long anesthetistId;
|
||||||
|
|
||||||
|
/** 麻醉医生姓名 */
|
||||||
|
private String anesthetistName;
|
||||||
|
|
||||||
|
/** 巡回护士ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long scrubNurseId;
|
||||||
|
|
||||||
|
/** 巡回护士姓名 */
|
||||||
|
private String scrubNurseName;
|
||||||
|
|
||||||
|
/** 麻醉方式编码 */
|
||||||
|
@Dict(dictCode = "anesthesia_type")
|
||||||
|
private Integer anesthesiaTypeEnum;
|
||||||
|
private String anesthesiaTypeEnum_dictText;
|
||||||
|
|
||||||
|
/** 手术部位 */
|
||||||
|
private String bodySite;
|
||||||
|
|
||||||
|
/** 手术切口等级 */
|
||||||
|
@Dict(dictCode = "incision_level")
|
||||||
|
private Integer incisionLevel;
|
||||||
|
private String incisionLevel_dictText;
|
||||||
|
|
||||||
|
/** 手术切口愈合等级 */
|
||||||
|
@Dict(dictCode = "healing_level")
|
||||||
|
private Integer healingLevel;
|
||||||
|
private String healingLevel_dictText;
|
||||||
|
|
||||||
|
/** 手术室 */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long operatingRoomId;
|
||||||
|
|
||||||
|
/** 手术室名称 */
|
||||||
|
private String operatingRoomName;
|
||||||
|
|
||||||
|
/** 执行科室ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long orgId;
|
||||||
|
|
||||||
|
/** 执行科室名称 */
|
||||||
|
private String orgName;
|
||||||
|
|
||||||
|
/** 术前诊断 */
|
||||||
|
private String preoperativeDiagnosis;
|
||||||
|
|
||||||
|
/** 术后诊断 */
|
||||||
|
private String postoperativeDiagnosis;
|
||||||
|
|
||||||
|
/** 手术经过描述 */
|
||||||
|
private String surgeryDescription;
|
||||||
|
|
||||||
|
/** 术后医嘱 */
|
||||||
|
private String postoperativeAdvice;
|
||||||
|
|
||||||
|
/** 并发症描述 */
|
||||||
|
private String complications;
|
||||||
|
|
||||||
|
/** 手术费用 */
|
||||||
|
private BigDecimal surgeryFee;
|
||||||
|
|
||||||
|
/** 麻醉费用 */
|
||||||
|
private BigDecimal anesthesiaFee;
|
||||||
|
|
||||||
|
/** 总费用 */
|
||||||
|
private BigDecimal totalFee;
|
||||||
|
|
||||||
|
/** 备注信息 */
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/** 创建时间 */
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/** 更新时间 */
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.openhis.web.clinicalmanage.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.openhis.web.clinicalmanage.dto.SurgeryDto;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术管理应用Mapper
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface SurgeryAppMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询手术列表
|
||||||
|
*
|
||||||
|
* @param page 分页参数
|
||||||
|
* @param queryWrapper 查询条件
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
IPage<SurgeryDto> getSurgeryPage(@Param("page") Page<SurgeryDto> page,
|
||||||
|
@Param(Constants.WRAPPER) QueryWrapper<SurgeryDto> queryWrapper);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询手术详情
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 手术详情
|
||||||
|
*/
|
||||||
|
SurgeryDto getSurgeryDetail(@Param("id") Long id);
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ spring:
|
|||||||
druid:
|
druid:
|
||||||
# 主库数据源
|
# 主库数据源
|
||||||
master:
|
master:
|
||||||
url: jdbc:postgresql://47.116.196.11:15432/postgresql?currentSchema=hisdev&characterEncoding=UTF-8&client_encoding=UTF-8
|
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=hisdev&characterEncoding=UTF-8&client_encoding=UTF-8
|
||||||
username: postgresql
|
username: postgresql
|
||||||
password: Jchl1528
|
password: Jchl1528
|
||||||
# 从库数据源
|
# 从库数据源
|
||||||
@@ -64,9 +64,9 @@ spring:
|
|||||||
# redis 配置
|
# redis 配置
|
||||||
redis:
|
redis:
|
||||||
# 地址
|
# 地址
|
||||||
host: 47.116.196.11
|
host: 192.168.110.252
|
||||||
# 端口,默认为6379
|
# 端口,默认为6379
|
||||||
port: 26379
|
port: 6379
|
||||||
# 数据库索引
|
# 数据库索引
|
||||||
database: 1
|
database: 1
|
||||||
# 密码
|
# 密码
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.openhis.clinical.mapper.SurgeryMapper">
|
||||||
|
|
||||||
|
<resultMap type="com.openhis.clinical.domain.Surgery" id="SurgeryResult">
|
||||||
|
<result property="id" column="id" />
|
||||||
|
<result property="surgeryNo" column="surgery_no" />
|
||||||
|
<result property="patientId" column="patient_id" />
|
||||||
|
<result property="patientName" column="patient_name" />
|
||||||
|
<result property="encounterId" column="encounter_id" />
|
||||||
|
<result property="surgeryName" column="surgery_name" />
|
||||||
|
<result property="surgeryCode" column="surgery_code" />
|
||||||
|
<result property="surgeryTypeEnum" column="surgery_type_enum" />
|
||||||
|
<result property="surgeryLevel" column="surgery_level" />
|
||||||
|
<result property="statusEnum" column="status_enum" />
|
||||||
|
<result property="plannedTime" column="planned_time" />
|
||||||
|
<result property="actualStartTime" column="actual_start_time" />
|
||||||
|
<result property="actualEndTime" column="actual_end_time" />
|
||||||
|
<result property="mainSurgeonId" column="main_surgeon_id" />
|
||||||
|
<result property="mainSurgeonName" column="main_surgeon_name" />
|
||||||
|
<result property="assistant1Id" column="assistant_1_id" />
|
||||||
|
<result property="assistant1Name" column="assistant_1_name" />
|
||||||
|
<result property="assistant2Id" column="assistant_2_id" />
|
||||||
|
<result property="assistant2Name" column="assistant_2_name" />
|
||||||
|
<result property="anesthetistId" column="anesthetist_id" />
|
||||||
|
<result property="anesthetistName" column="anesthetist_name" />
|
||||||
|
<result property="scrubNurseId" column="scrub_nurse_id" />
|
||||||
|
<result property="scrubNurseName" column="scrub_nurse_name" />
|
||||||
|
<result property="anesthesiaTypeEnum" column="anesthesia_type_enum" />
|
||||||
|
<result property="bodySite" column="body_site" />
|
||||||
|
<result property="incisionLevel" column="incision_level" />
|
||||||
|
<result property="healingLevel" column="healing_level" />
|
||||||
|
<result property="operatingRoomId" column="operating_room_id" />
|
||||||
|
<result property="operatingRoomName" column="operating_room_name" />
|
||||||
|
<result property="orgId" column="org_id" />
|
||||||
|
<result property="orgName" column="org_name" />
|
||||||
|
<result property="preoperativeDiagnosis" column="preoperative_diagnosis" />
|
||||||
|
<result property="postoperativeDiagnosis" column="postoperative_diagnosis" />
|
||||||
|
<result property="surgeryDescription" column="surgery_description" />
|
||||||
|
<result property="postoperativeAdvice" column="postoperative_advice" />
|
||||||
|
<result property="complications" column="complications" />
|
||||||
|
<result property="surgeryFee" column="surgery_fee" />
|
||||||
|
<result property="anesthesiaFee" column="anesthesia_fee" />
|
||||||
|
<result property="totalFee" column="total_fee" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
<result property="createBy" column="create_by" />
|
||||||
|
<result property="createTime" column="create_time" />
|
||||||
|
<result property="updateBy" column="update_by" />
|
||||||
|
<result property="updateTime" column="update_time" />
|
||||||
|
<result property="deleteFlag" column="delete_flag" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="selectSurgeryVo">
|
||||||
|
SELECT
|
||||||
|
id, surgery_no, patient_id, patient_name, encounter_id, surgery_name, surgery_code,
|
||||||
|
surgery_type_enum, surgery_level, status_enum, planned_time, actual_start_time, actual_end_time,
|
||||||
|
main_surgeon_id, main_surgeon_name, assistant_1_id, assistant_1_name, assistant_2_id, assistant_2_name,
|
||||||
|
anesthetist_id, anesthetist_name, scrub_nurse_id, scrub_nurse_name, anesthesia_type_enum,
|
||||||
|
body_site, incision_level, healing_level, operating_room_id, operating_room_name,
|
||||||
|
org_id, org_name, preoperative_diagnosis, postoperative_diagnosis, surgery_description,
|
||||||
|
postoperative_advice, complications, surgery_fee, anesthesia_fee, total_fee, remark,
|
||||||
|
create_by, create_time, update_by, update_time, delete_flag
|
||||||
|
FROM cli_surgery
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectByPatientId" parameterType="Long" resultMap="SurgeryResult">
|
||||||
|
<include refid="selectSurgeryVo"/>
|
||||||
|
WHERE patient_id = #{patientId} AND delete_flag = '0'
|
||||||
|
ORDER BY create_time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectByEncounterId" parameterType="Long" resultMap="SurgeryResult">
|
||||||
|
<include refid="selectSurgeryVo"/>
|
||||||
|
WHERE encounter_id = #{encounterId} AND delete_flag = '0'
|
||||||
|
ORDER BY create_time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectBySurgeryNo" parameterType="String" resultMap="SurgeryResult">
|
||||||
|
<include refid="selectSurgeryVo"/>
|
||||||
|
WHERE surgery_no = #{surgeryNo} AND delete_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.openhis.web.clinicalmanage.mapper.SurgeryAppMapper">
|
||||||
|
|
||||||
|
<resultMap type="com.openhis.web.clinicalmanage.dto.SurgeryDto" id="SurgeryResult">
|
||||||
|
<result property="id" column="id" />
|
||||||
|
<result property="surgeryNo" column="surgery_no" />
|
||||||
|
<result property="patientId" column="patient_id" />
|
||||||
|
<result property="patientName" column="patient_name" />
|
||||||
|
<result property="patientGender" column="patient_gender" />
|
||||||
|
<result property="patientAge" column="patient_age" />
|
||||||
|
<result property="encounterId" column="encounter_id" />
|
||||||
|
<result property="encounterNo" column="encounter_no" />
|
||||||
|
<result property="surgeryName" column="surgery_name" />
|
||||||
|
<result property="surgeryCode" column="surgery_code" />
|
||||||
|
<result property="surgeryTypeEnum" column="surgery_type_enum" />
|
||||||
|
<result property="surgeryTypeEnum_dictText" column="surgery_type_enum_dictText" />
|
||||||
|
<result property="surgeryLevel" column="surgery_level" />
|
||||||
|
<result property="surgeryLevel_dictText" column="surgery_level_dictText" />
|
||||||
|
<result property="statusEnum" column="status_enum" />
|
||||||
|
<result property="statusEnum_dictText" column="status_enum_dictText" />
|
||||||
|
<result property="plannedTime" column="planned_time" />
|
||||||
|
<result property="actualStartTime" column="actual_start_time" />
|
||||||
|
<result property="actualEndTime" column="actual_end_time" />
|
||||||
|
<result property="mainSurgeonId" column="main_surgeon_id" />
|
||||||
|
<result property="mainSurgeonName" column="main_surgeon_name" />
|
||||||
|
<result property="assistant1Id" column="assistant_1_id" />
|
||||||
|
<result property="assistant1Name" column="assistant_1_name" />
|
||||||
|
<result property="assistant2Id" column="assistant_2_id" />
|
||||||
|
<result property="assistant2Name" column="assistant_2_name" />
|
||||||
|
<result property="anesthetistId" column="anesthetist_id" />
|
||||||
|
<result property="anesthetistName" column="anesthetist_name" />
|
||||||
|
<result property="scrubNurseId" column="scrub_nurse_id" />
|
||||||
|
<result property="scrubNurseName" column="scrub_nurse_name" />
|
||||||
|
<result property="anesthesiaTypeEnum" column="anesthesia_type_enum" />
|
||||||
|
<result property="anesthesiaTypeEnum_dictText" column="anesthesia_type_enum_dictText" />
|
||||||
|
<result property="bodySite" column="body_site" />
|
||||||
|
<result property="incisionLevel" column="incision_level" />
|
||||||
|
<result property="incisionLevel_dictText" column="incision_level_dictText" />
|
||||||
|
<result property="healingLevel" column="healing_level" />
|
||||||
|
<result property="healingLevel_dictText" column="healing_level_dictText" />
|
||||||
|
<result property="operatingRoomId" column="operating_room_id" />
|
||||||
|
<result property="operatingRoomName" column="operating_room_name" />
|
||||||
|
<result property="orgId" column="org_id" />
|
||||||
|
<result property="orgName" column="org_name" />
|
||||||
|
<result property="preoperativeDiagnosis" column="preoperative_diagnosis" />
|
||||||
|
<result property="postoperativeDiagnosis" column="postoperative_diagnosis" />
|
||||||
|
<result property="surgeryDescription" column="surgery_description" />
|
||||||
|
<result property="postoperativeAdvice" column="postoperative_advice" />
|
||||||
|
<result property="complications" column="complications" />
|
||||||
|
<result property="surgeryFee" column="surgery_fee" />
|
||||||
|
<result property="anesthesiaFee" column="anesthesia_fee" />
|
||||||
|
<result property="totalFee" column="total_fee" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
<result property="createTime" column="create_time" />
|
||||||
|
<result property="updateTime" column="update_time" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="selectSurgeryVo">
|
||||||
|
SELECT
|
||||||
|
s.id,
|
||||||
|
s.surgery_no,
|
||||||
|
s.patient_id,
|
||||||
|
p.name as patient_name,
|
||||||
|
CASE p.gender_enum WHEN 1 THEN '男' WHEN 2 THEN '女' ELSE '未知' END as patient_gender,
|
||||||
|
EXTRACT(YEAR FROM AGE(p.birth_date)) as patient_age,
|
||||||
|
s.encounter_id,
|
||||||
|
e.bus_no as encounter_no,
|
||||||
|
s.surgery_name,
|
||||||
|
s.surgery_code,
|
||||||
|
s.surgery_type_enum,
|
||||||
|
s.surgery_type_enum as surgery_type_enum_dictText,
|
||||||
|
s.surgery_level,
|
||||||
|
s.surgery_level as surgery_level_dictText,
|
||||||
|
s.status_enum,
|
||||||
|
s.status_enum as status_enum_dictText,
|
||||||
|
s.planned_time,
|
||||||
|
s.actual_start_time,
|
||||||
|
s.actual_end_time,
|
||||||
|
s.main_surgeon_id,
|
||||||
|
s.main_surgeon_name,
|
||||||
|
s.assistant_1_id,
|
||||||
|
s.assistant_1_name,
|
||||||
|
s.assistant_2_id,
|
||||||
|
s.assistant_2_name,
|
||||||
|
s.anesthetist_id,
|
||||||
|
s.anesthetist_name,
|
||||||
|
s.scrub_nurse_id,
|
||||||
|
s.scrub_nurse_name,
|
||||||
|
s.anesthesia_type_enum,
|
||||||
|
s.anesthesia_type_enum as anesthesia_type_enum_dictText,
|
||||||
|
s.body_site,
|
||||||
|
s.incision_level,
|
||||||
|
s.incision_level as incision_level_dictText,
|
||||||
|
s.healing_level,
|
||||||
|
s.healing_level as healing_level_dictText,
|
||||||
|
s.operating_room_id,
|
||||||
|
s.operating_room_name,
|
||||||
|
s.org_id,
|
||||||
|
o.name as org_name,
|
||||||
|
s.preoperative_diagnosis,
|
||||||
|
s.postoperative_diagnosis,
|
||||||
|
s.surgery_description,
|
||||||
|
s.postoperative_advice,
|
||||||
|
s.complications,
|
||||||
|
s.surgery_fee,
|
||||||
|
s.anesthesia_fee,
|
||||||
|
s.total_fee,
|
||||||
|
s.remark,
|
||||||
|
s.create_time,
|
||||||
|
s.update_time
|
||||||
|
FROM cli_surgery s
|
||||||
|
LEFT JOIN adm_patient p ON s.patient_id = p.id
|
||||||
|
LEFT JOIN adm_encounter e ON s.encounter_id = e.id
|
||||||
|
LEFT JOIN adm_organization o ON s.org_id = o.id
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="getSurgeryPage" parameterType="com.baomidou.mybatisplus.core.conditions.query.QueryWrapper" resultMap="SurgeryResult">
|
||||||
|
<include refid="selectSurgeryVo"/>
|
||||||
|
<where>
|
||||||
|
s.delete_flag = '0'
|
||||||
|
<if test="ew.sqlSegment != null and ew.sqlSegment != ''">
|
||||||
|
AND ${ew.sqlSegment}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getSurgeryDetail" parameterType="Long" resultMap="SurgeryResult">
|
||||||
|
<include refid="selectSurgeryVo"/>
|
||||||
|
WHERE s.id = #{id} AND s.delete_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.openhis.common.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 麻醉方式枚举
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum AnesthesiaTypeEnum {
|
||||||
|
|
||||||
|
/** 局部麻醉 */
|
||||||
|
LOCAL(1, "局部麻醉"),
|
||||||
|
|
||||||
|
/** 区域麻醉 */
|
||||||
|
REGIONAL(2, "区域麻醉"),
|
||||||
|
|
||||||
|
/** 全身麻醉 */
|
||||||
|
GENERAL(3, "全身麻醉"),
|
||||||
|
|
||||||
|
/** 脊椎麻醉 */
|
||||||
|
SPINAL(4, "脊椎麻醉"),
|
||||||
|
|
||||||
|
/** 硬膜外麻醉 */
|
||||||
|
EPIDURAL(5, "硬膜外麻醉"),
|
||||||
|
|
||||||
|
/** 表面麻醉 */
|
||||||
|
SURFACE(6, "表面麻醉"),
|
||||||
|
|
||||||
|
/** 无麻醉 */
|
||||||
|
NONE(0, "无麻醉");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String info;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.openhis.common.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术切口愈合等级枚举
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum HealingLevelEnum {
|
||||||
|
|
||||||
|
/** 甲级愈合 */
|
||||||
|
GRADE_A(1, "甲级愈合"),
|
||||||
|
|
||||||
|
/** 乙级愈合 */
|
||||||
|
GRADE_B(2, "乙级愈合"),
|
||||||
|
|
||||||
|
/** 丙级愈合 */
|
||||||
|
GRADE_C(3, "丙级愈合");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String info;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.openhis.common.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术切口等级枚举
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum IncisionLevelEnum {
|
||||||
|
|
||||||
|
/** I级切口 */
|
||||||
|
LEVEL_I(1, "I级切口"),
|
||||||
|
|
||||||
|
/** II级切口 */
|
||||||
|
LEVEL_II(2, "II级切口"),
|
||||||
|
|
||||||
|
/** III级切口 */
|
||||||
|
LEVEL_III(3, "III级切口"),
|
||||||
|
|
||||||
|
/** IV级切口 */
|
||||||
|
LEVEL_IV(4, "IV级切口");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String info;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.openhis.common.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术等级枚举
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum SurgeryLevelEnum {
|
||||||
|
|
||||||
|
/** 一级手术 */
|
||||||
|
LEVEL_1(1, "一级手术"),
|
||||||
|
|
||||||
|
/** 二级手术 */
|
||||||
|
LEVEL_2(2, "二级手术"),
|
||||||
|
|
||||||
|
/** 三级手术 */
|
||||||
|
LEVEL_3(3, "三级手术"),
|
||||||
|
|
||||||
|
/** 四级手术 */
|
||||||
|
LEVEL_4(4, "四级手术"),
|
||||||
|
|
||||||
|
/** 特级手术 */
|
||||||
|
SPECIAL(5, "特级手术");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String info;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.openhis.common.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术状态枚举
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum SurgeryStatusEnum {
|
||||||
|
|
||||||
|
/** 待排期 */
|
||||||
|
PENDING_SCHEDULE(0, "待排期"),
|
||||||
|
|
||||||
|
/** 已排期 */
|
||||||
|
SCHEDULED(1, "已排期"),
|
||||||
|
|
||||||
|
/** 手术中 */
|
||||||
|
IN_PROGRESS(2, "手术中"),
|
||||||
|
|
||||||
|
/** 已完成 */
|
||||||
|
COMPLETED(3, "已完成"),
|
||||||
|
|
||||||
|
/** 已取消 */
|
||||||
|
CANCELLED(4, "已取消"),
|
||||||
|
|
||||||
|
/** 暂停 */
|
||||||
|
SUSPENDED(5, "暂停");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String info;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.openhis.common.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术类型枚举
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum SurgeryTypeEnum {
|
||||||
|
|
||||||
|
/** 门诊手术 */
|
||||||
|
OUTPATIENT(1, "门诊手术"),
|
||||||
|
|
||||||
|
/** 住院手术 */
|
||||||
|
INPATIENT(2, "住院手术"),
|
||||||
|
|
||||||
|
/** 急诊手术 */
|
||||||
|
EMERGENCY(3, "急诊手术"),
|
||||||
|
|
||||||
|
/** 择期手术 */
|
||||||
|
ELECTIVE(4, "择期手术");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String info;
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package com.openhis.clinical.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.core.common.core.domain.HisBaseEntity;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术管理Entity实体
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-12-30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("cli_surgery")
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class Surgery extends HisBaseEntity {
|
||||||
|
|
||||||
|
/** ID */
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 手术编号 */
|
||||||
|
private String surgeryNo;
|
||||||
|
|
||||||
|
/** 患者ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long patientId;
|
||||||
|
|
||||||
|
/** 就诊ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long encounterId;
|
||||||
|
|
||||||
|
/** 手术名称 */
|
||||||
|
private String surgeryName;
|
||||||
|
|
||||||
|
/** 手术编码 */
|
||||||
|
private String surgeryCode;
|
||||||
|
|
||||||
|
/** 手术类型编码 */
|
||||||
|
private Integer surgeryTypeEnum;
|
||||||
|
|
||||||
|
/** 手术等级 */
|
||||||
|
private Integer surgeryLevel;
|
||||||
|
|
||||||
|
/** 手术状态 */
|
||||||
|
private Integer statusEnum;
|
||||||
|
|
||||||
|
/** 计划手术时间 */
|
||||||
|
private Date plannedTime;
|
||||||
|
|
||||||
|
/** 实际开始时间 */
|
||||||
|
private Date actualStartTime;
|
||||||
|
|
||||||
|
/** 实际结束时间 */
|
||||||
|
private Date actualEndTime;
|
||||||
|
|
||||||
|
/** 主刀医生ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long mainSurgeonId;
|
||||||
|
|
||||||
|
/** 主刀医生姓名 */
|
||||||
|
private String mainSurgeonName;
|
||||||
|
|
||||||
|
/** 助手1 ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long assistant1Id;
|
||||||
|
|
||||||
|
/** 助手1 姓名 */
|
||||||
|
private String assistant1Name;
|
||||||
|
|
||||||
|
/** 助手2 ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long assistant2Id;
|
||||||
|
|
||||||
|
/** 助手2 姓名 */
|
||||||
|
private String assistant2Name;
|
||||||
|
|
||||||
|
/** 麻醉医生ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long anesthetistId;
|
||||||
|
|
||||||
|
/** 麻醉医生姓名 */
|
||||||
|
private String anesthetistName;
|
||||||
|
|
||||||
|
/** 巡回护士ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long scrubNurseId;
|
||||||
|
|
||||||
|
/** 巡回护士姓名 */
|
||||||
|
private String scrubNurseName;
|
||||||
|
|
||||||
|
/** 麻醉方式编码 */
|
||||||
|
private Integer anesthesiaTypeEnum;
|
||||||
|
|
||||||
|
/** 手术部位 */
|
||||||
|
private String bodySite;
|
||||||
|
|
||||||
|
/** 手术切口等级 */
|
||||||
|
private Integer incisionLevel;
|
||||||
|
|
||||||
|
/** 手术切口愈合等级 */
|
||||||
|
private Integer healingLevel;
|
||||||
|
|
||||||
|
/** 手术室 */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long operatingRoomId;
|
||||||
|
|
||||||
|
/** 手术室名称 */
|
||||||
|
private String operatingRoomName;
|
||||||
|
|
||||||
|
/** 执行科室ID */
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long orgId;
|
||||||
|
|
||||||
|
/** 执行科室名称 */
|
||||||
|
private String orgName;
|
||||||
|
|
||||||
|
/** 术前诊断 */
|
||||||
|
private String preoperativeDiagnosis;
|
||||||
|
|
||||||
|
/** 术后诊断 */
|
||||||
|
private String postoperativeDiagnosis;
|
||||||
|
|
||||||
|
/** 手术经过描述 */
|
||||||
|
private String surgeryDescription;
|
||||||
|
|
||||||
|
/** 术后医嘱 */
|
||||||
|
private String postoperativeAdvice;
|
||||||
|
|
||||||
|
/** 并发症描述 */
|
||||||
|
private String complications;
|
||||||
|
|
||||||
|
/** 手术费用 */
|
||||||
|
private BigDecimal surgeryFee;
|
||||||
|
|
||||||
|
/** 麻醉费用 */
|
||||||
|
private BigDecimal anesthesiaFee;
|
||||||
|
|
||||||
|
/** 总费用 */
|
||||||
|
private BigDecimal totalFee;
|
||||||
|
|
||||||
|
/** 备注信息 */
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/** 租户ID(表不存在此字段,仅用于继承基类) */
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer tenantId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.openhis.clinical.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.openhis.clinical.domain.Surgery;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术管理Mapper接口
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-12-30
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface SurgeryMapper extends BaseMapper<Surgery> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据患者ID查询手术列表
|
||||||
|
*
|
||||||
|
* @param patientId 患者ID
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
List<Surgery> selectByPatientId(@Param("patientId") Long patientId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据就诊ID查询手术列表
|
||||||
|
*
|
||||||
|
* @param encounterId 就诊ID
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
List<Surgery> selectByEncounterId(@Param("encounterId") Long encounterId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据手术编号查询
|
||||||
|
*
|
||||||
|
* @param surgeryNo 手术编号
|
||||||
|
* @return 手术信息
|
||||||
|
*/
|
||||||
|
Surgery selectBySurgeryNo(@Param("surgeryNo") String surgeryNo);
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.openhis.clinical.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.openhis.clinical.domain.Surgery;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术管理Service接口
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-12-30
|
||||||
|
*/
|
||||||
|
public interface ISurgeryService extends IService<Surgery> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增手术信息
|
||||||
|
*
|
||||||
|
* @param surgery 手术信息
|
||||||
|
* @return 手术ID
|
||||||
|
*/
|
||||||
|
Long insertSurgery(Surgery surgery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改手术信息
|
||||||
|
*
|
||||||
|
* @param surgery 手术信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
boolean updateSurgery(Surgery surgery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除手术信息
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
boolean deleteSurgery(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询手术信息
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 手术信息
|
||||||
|
*/
|
||||||
|
Surgery getSurgeryById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据患者ID查询手术列表
|
||||||
|
*
|
||||||
|
* @param patientId 患者ID
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
List<Surgery> getSurgeryListByPatientId(Long patientId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据就诊ID查询手术列表
|
||||||
|
*
|
||||||
|
* @param encounterId 就诊ID
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
List<Surgery> getSurgeryListByEncounterId(Long encounterId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新手术状态
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @param statusEnum 状态
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
boolean updateSurgeryStatus(Long id, Integer statusEnum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package com.openhis.clinical.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.core.common.utils.AssignSeqUtil;
|
||||||
|
import com.openhis.clinical.domain.Surgery;
|
||||||
|
import com.openhis.clinical.mapper.SurgeryMapper;
|
||||||
|
import com.openhis.clinical.service.ISurgeryService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手术管理Service业务层处理
|
||||||
|
*
|
||||||
|
* @author system
|
||||||
|
* @date 2025-12-30
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class SurgeryServiceImpl extends ServiceImpl<SurgeryMapper, Surgery> implements ISurgeryService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SurgeryMapper surgeryMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AssignSeqUtil assignSeqUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增手术信息
|
||||||
|
*
|
||||||
|
* @param surgery 手术信息
|
||||||
|
* @return 手术ID
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Long insertSurgery(Surgery surgery) {
|
||||||
|
// 生成手术编号
|
||||||
|
String surgeryNo = assignSeqUtil.getSeq("SS", 10);
|
||||||
|
surgery.setSurgeryNo(surgeryNo);
|
||||||
|
surgery.setCreateTime(new Date());
|
||||||
|
surgery.setUpdateTime(new Date());
|
||||||
|
surgery.setDeleteFlag("0");
|
||||||
|
|
||||||
|
// 默认状态为待排期
|
||||||
|
if (surgery.getStatusEnum() == null) {
|
||||||
|
surgery.setStatusEnum(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
surgeryMapper.insert(surgery);
|
||||||
|
return surgery.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改手术信息
|
||||||
|
*
|
||||||
|
* @param surgery 手术信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean updateSurgery(Surgery surgery) {
|
||||||
|
surgery.setUpdateTime(new Date());
|
||||||
|
return surgeryMapper.updateById(surgery) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除手术信息
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean deleteSurgery(Long id) {
|
||||||
|
return surgeryMapper.deleteById(id) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询手术信息
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @return 手术信息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Surgery getSurgeryById(Long id) {
|
||||||
|
return surgeryMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据患者ID查询手术列表
|
||||||
|
*
|
||||||
|
* @param patientId 患者ID
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Surgery> getSurgeryListByPatientId(Long patientId) {
|
||||||
|
LambdaQueryWrapper<Surgery> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(Surgery::getPatientId, patientId)
|
||||||
|
.eq(Surgery::getDeleteFlag, "0")
|
||||||
|
.orderByDesc(Surgery::getCreateTime);
|
||||||
|
return surgeryMapper.selectList(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据就诊ID查询手术列表
|
||||||
|
*
|
||||||
|
* @param encounterId 就诊ID
|
||||||
|
* @return 手术列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Surgery> getSurgeryListByEncounterId(Long encounterId) {
|
||||||
|
return surgeryMapper.selectByEncounterId(encounterId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新手术状态
|
||||||
|
*
|
||||||
|
* @param id 手术ID
|
||||||
|
* @param statusEnum 状态
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean updateSurgeryStatus(Long id, Integer statusEnum) {
|
||||||
|
Surgery surgery = new Surgery();
|
||||||
|
surgery.setId(id);
|
||||||
|
surgery.setStatusEnum(statusEnum);
|
||||||
|
surgery.setUpdateTime(new Date());
|
||||||
|
|
||||||
|
// 如果状态为手术中,更新开始时间
|
||||||
|
if (statusEnum == 2) {
|
||||||
|
surgery.setActualStartTime(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果状态为已完成,更新结束时间
|
||||||
|
if (statusEnum == 3) {
|
||||||
|
surgery.setActualEndTime(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
return surgeryMapper.updateById(surgery) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,3 +13,4 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
80
openhis-ui-vue3/src/api/surgerymanage.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询手术列表
|
||||||
|
* @param queryParams 查询参数
|
||||||
|
* @returns {AxiosPromise}
|
||||||
|
*/
|
||||||
|
export function getSurgeryPage(queryParams) {
|
||||||
|
return request({
|
||||||
|
url: '/clinical-manage/surgery/surgery-page',
|
||||||
|
method: 'get',
|
||||||
|
params: queryParams
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询手术详情
|
||||||
|
* @param id 手术ID
|
||||||
|
* @returns {AxiosPromise}
|
||||||
|
*/
|
||||||
|
export function getSurgeryDetail(id) {
|
||||||
|
return request({
|
||||||
|
url: '/clinical-manage/surgery/surgery-detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增手术信息
|
||||||
|
* @param data 手术信息
|
||||||
|
* @returns {AxiosPromise}
|
||||||
|
*/
|
||||||
|
export function addSurgery(data) {
|
||||||
|
return request({
|
||||||
|
url: '/clinical-manage/surgery/surgery',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改手术信息
|
||||||
|
* @param data 手术信息
|
||||||
|
* @returns {AxiosPromise}
|
||||||
|
*/
|
||||||
|
export function updateSurgery(data) {
|
||||||
|
return request({
|
||||||
|
url: '/clinical-manage/surgery/surgery',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除手术信息
|
||||||
|
* @param id 手术ID
|
||||||
|
* @returns {AxiosPromise}
|
||||||
|
*/
|
||||||
|
export function deleteSurgery(id) {
|
||||||
|
return request({
|
||||||
|
url: '/clinical-manage/surgery/surgery',
|
||||||
|
method: 'delete',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新手术状态
|
||||||
|
* @param id 手术ID
|
||||||
|
* @param statusEnum 状态
|
||||||
|
* @returns {AxiosPromise}
|
||||||
|
*/
|
||||||
|
export function updateSurgeryStatus(id, statusEnum) {
|
||||||
|
return request({
|
||||||
|
url: '/clinical-manage/surgery/surgery-status',
|
||||||
|
method: 'put',
|
||||||
|
params: { id, statusEnum }
|
||||||
|
})
|
||||||
|
}
|
||||||
4
openhis-ui-vue3/src/assets/icons/drug.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M704 192H320c-35.2 0-64 28.8-64 64v576c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V256c0-35.2-28.8-64-64-64z m-64 576H384V320h256v448z" fill="currentColor"/>
|
||||||
|
<path d="M416 384h192v128H416z m0 192h128v64H416z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 331 B |
4
openhis-ui-vue3/src/assets/icons/svg/appointment.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M768 192H256c-35.2 0-64 28.8-64 64v576c0 35.2 28.8 64 64 64h512c35.2 0 64-28.8 64-64V256c0-35.2-28.8-64-64-64z m-64 576H320V320h384v448z" fill="currentColor"/>
|
||||||
|
<path d="M384 384h256v64H384z m0 128h192v64H384z m0 128h128v64H384z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 349 B |
4
openhis-ui-vue3/src/assets/icons/svg/billing.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M640 128H384c-35.2 0-64 28.8-64 64v640c0 35.2 28.8 64 64 64h256c35.2 0 64-28.8 64-64V192c0-35.2-28.8-64-64-64z m-64 640H448V256h128v512z" fill="currentColor"/>
|
||||||
|
<path d="M480 320h64v128h-64z m-64 192h192v64H416z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 332 B |
4
openhis-ui-vue3/src/assets/icons/svg/diagnosis.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M832 192H192c-35.2 0-64 28.8-64 64v512c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64V256c0-35.2-28.8-64-64-64z m-64 512H256V320h512v384z" fill="currentColor"/>
|
||||||
|
<path d="M320 384h128v128H320z m256 0h128v128H576z m-256 192h128v64H320z m256 0h128v64H576z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 373 B |
4
openhis-ui-vue3/src/assets/icons/svg/doctor.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M512 64c-106.04 0-192 85.96-192 192 0 106.04 85.96 192 192 192 106.04 0 192-85.96 192-192 0-106.04-85.96-192-192-192z m0 320c-70.58 0-128-57.42-128-128 0-70.58 57.42-128 128-128 70.58 0 128 57.42 128 128 0 70.58-57.42 128-128 128z" fill="currentColor"/>
|
||||||
|
<path d="M736 640h-64c-17.68 0-32 14.32-32 32v256c0 17.68 14.32 32 32 32h64c17.68 0 32-14.32 32-32v-256c0-17.68-14.32-32-32-32z m-192 0h-64c-17.68 0-32 14.32-32 32v256c0 17.68 14.32 32 32 32h64c17.68 0 32-14.32 32-32v-256c0-17.68-14.32-32-32-32z m-192 0h-64c-17.68 0-32 14.32-32 32v256c0 17.68 14.32 32 32 32h64c17.68 0 32-14.32 32-32v-256c0-17.68-14.32-32-32-32z m416-128c-17.68 0-32-14.32-32-32v-64H288v64c0 17.68-14.32 32-32 32h-64c-17.68 0-32-14.32-32-32V352c0-17.68 14.32-32 32-32h544c17.68 0 32 14.32 32 32v128c0 17.68-14.32 32-32 32z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 916 B |
4
openhis-ui-vue3/src/assets/icons/svg/emergency.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M512 64C262.4 64 64 262.4 64 512s198.4 448 448 448 448-198.4 448-448S761.6 64 512 64z m0 768c-176.8 0-320-143.2-320-320s143.2-320 320-320 320 143.2 320 320-143.2 320-320 320z" fill="currentColor"/>
|
||||||
|
<path d="M480 352h64v192h-64z m0 256h64v64h-64z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 367 B |
4
openhis-ui-vue3/src/assets/icons/svg/insurance.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M512 64L128 256v256c0 224 144 416 384 448 240-32 384-224 384-448V256L512 64z m0 832c-176-32-288-192-288-384V288l288-160 288 160v224c0 192-112 352-288 384z" fill="currentColor"/>
|
||||||
|
<path d="M480 320h64v256h-64z m0 320h64v64h-64z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 347 B |
4
openhis-ui-vue3/src/assets/icons/svg/inventory.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M832 256H192c-35.2 0-64 28.8-64 64v512c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64V320c0-35.2-28.8-64-64-64z m-64 512H256V384h512v384z" fill="currentColor"/>
|
||||||
|
<path d="M320 416h128v64H320z m256 0h128v64H576z m-256 128h128v64H320z m256 0h128v64H576z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 371 B |
4
openhis-ui-vue3/src/assets/icons/svg/laboratory.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M768 192H256c-35.2 0-64 28.8-64 64v512c0 35.2 28.8 64 64 64h512c35.2 0 64-28.8 64-64V256c0-35.2-28.8-64-64-64z m-64 512H320V320h384v384z" fill="currentColor"/>
|
||||||
|
<path d="M384 416h256v64H384z m0 128h192v64H384z m0 128h128v64H384z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 349 B |
4
openhis-ui-vue3/src/assets/icons/svg/medical.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M896 160H768v-64c0-35.2-28.8-64-64-64s-64 28.8-64 64v64H384v-64c0-35.2-28.8-64-64-64s-64 28.8-64 64v64H128c-35.2 0-64 28.8-64 64v640c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64z m0 640H128V288h768v512z" fill="currentColor"/>
|
||||||
|
<path d="M640 448h-128c-17.6 0-32 14.4-32 32v128c0 17.6 14.4 32 32 32h128c17.6 0 32-14.4 32-32V480c0-17.6-14.4-32-32-32z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 494 B |
4
openhis-ui-vue3/src/assets/icons/svg/nurse.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M512 64c-88.4 0-160 71.6-160 160 0 88.4 71.6 160 160 160 88.4 0 160-71.6 160-160 0-88.4-71.6-160-160-160z m0 256c-52.94 0-96-43.06-96-96 0-52.94 43.06-96 96-96 52.94 0 96 43.06 96 96 0 52.94-43.06 96-96 96z" fill="currentColor"/>
|
||||||
|
<path d="M800 640h-96v-64c0-17.68-14.32-32-32-32h-320c-17.68 0-32 14.32-32 32v64h-96c-17.68 0-32 14.32-32 32v256c0 17.68 14.32 32 32 32h576c17.68 0 32-14.32 32-32v-256c0-17.68-14.32-32-32-32z m-224 0h-128v-64h128v64z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 568 B |
4
openhis-ui-vue3/src/assets/icons/svg/patient.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M512 128c-70.69 0-128 57.31-128 128 0 70.69 57.31 128 128 70.69 0 128-57.31 128-128 0-70.69-57.31-128-128-128z m0 192c-35.34 0-64-28.66-64-64 0-35.34 28.66-64 64-64 35.34 0 64 28.66 64 64 0 35.34-28.66 64-64 64z" fill="currentColor"/>
|
||||||
|
<path d="M832 832c0-88.36-28.7-172.6-80.4-242.4C702 521 608 480 512 480s-190 41-239.6 109.6C221 659.4 192 743.64 192 832v64h640v-64z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 489 B |
5
openhis-ui-vue3/src/assets/icons/svg/pharmacy.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M736 288H288c-17.68 0-32 14.32-32 32v416c0 17.68 14.32 32 32 32h448c17.68 0 32-14.32 32-32v-416c0-17.68-14.32-32-32-32z m-64 384H352V384h320v288z" fill="currentColor"/>
|
||||||
|
<path d="M480 192h64v224h-64z m-128 0h64v224h-64z m256 0h64v224h-64z" fill="currentColor"/>
|
||||||
|
<path d="M544 64h-64c-17.68 0-32 14.32-32 32v64h-64c-17.68 0-32 14.32-32 32v64h320v-64c0-17.68-14.32-32-32-32h-64v-64c0-17.68-14.32-32-32-32z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 526 B |
4
openhis-ui-vue3/src/assets/icons/svg/prescription.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M704 128H320c-35.2 0-64 28.8-64 64v640c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V192c0-35.2-28.8-64-64-64z m-64 640H384V256h256v512z" fill="currentColor"/>
|
||||||
|
<path d="M416 320h192v64H416z m0 128h192v64H416z m0 128h128v64H416z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 349 B |
4
openhis-ui-vue3/src/assets/icons/svg/receipt.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M704 64H320c-35.2 0-64 28.8-64 64v768c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V128c0-35.2-28.8-64-64-64z m-64 768H384V192h256v640z" fill="currentColor"/>
|
||||||
|
<path d="M416 256h192v64H416z m0 128h192v64H416z m0 128h128v64H416z m0 128h128v64H416z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 367 B |
4
openhis-ui-vue3/src/assets/icons/svg/surgery.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M736 256H288c-35.2 0-64 28.8-64 64v416c0 35.2 28.8 64 64 64h448c35.2 0 32-28.8 32-64V320c0-35.2-3.2-64-32-64z m-64 384H352V384h320v256z" fill="currentColor"/>
|
||||||
|
<path d="M384 416h256v64H384z m0 128h192v64H384z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 329 B |
4
openhis-ui-vue3/src/assets/icons/svg/ward.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M896 192H128c-35.2 0-64 28.8-64 64v576c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64V256c0-35.2-28.8-64-64-64z m-64 576H192V320h640v448z" fill="currentColor"/>
|
||||||
|
<path d="M320 416h128v64h-128z m256 0h128v64h-128z m-256 128h128v64h-128z m256 0h128v64h-128z m-256 128h128v64h-128z m256 0h128v64h-128z" fill="currentColor"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 418 B |
@@ -42,7 +42,12 @@ $--color-warning: #E6A23C;
|
|||||||
$--color-danger: #F56C6C;
|
$--color-danger: #F56C6C;
|
||||||
$--color-info: #909399;
|
$--color-info: #909399;
|
||||||
|
|
||||||
$base-sidebar-width: 100%;
|
// 侧边栏宽度(垂直菜单)
|
||||||
|
$sideBarWidth: 200px;
|
||||||
|
$base-sidebar-width: $sideBarWidth;
|
||||||
|
|
||||||
|
// Logo高度
|
||||||
|
$logoHeight: 50px;
|
||||||
|
|
||||||
// the :export directive is the magic sauce for webpack
|
// the :export directive is the magic sauce for webpack
|
||||||
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
||||||
|
|||||||
@@ -1,49 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-drawer v-model="noticeVisible" title="公告" direction="rtl" size="400px" destroy-on-close>
|
<el-drawer v-model="noticeVisible" title="公告/通知" direction="rtl" size="400px" destroy-on-close>
|
||||||
<el-tabs v-model="activeTab">
|
<el-empty v-if="noticeList.length === 0" description="暂无公告/通知" />
|
||||||
<el-tab-pane label="公告" name="notice">
|
<div v-else class="notice-list">
|
||||||
<el-empty v-if="noticeList.length === 0" description="暂无公告" />
|
<div v-for="item in noticeList" :key="item.noticeId" class="notice-item" :class="{ 'is-read': isRead(item.noticeId), 'unread': !isRead(item.noticeId) }" @click="viewDetail(item)">
|
||||||
<div v-else class="notice-list">
|
<div class="notice-title">
|
||||||
<div v-for="item in noticeList" :key="item.noticeId" class="notice-item" :class="{ 'is-read': isRead(item.noticeId) }" @click="viewDetail(item)">
|
<span v-if="!isRead(item.noticeId)" class="unread-dot"></span>
|
||||||
<div class="notice-title">
|
{{ item.noticeTitle }}
|
||||||
<span v-if="!isRead(item.noticeId)" class="unread-dot"></span>
|
|
||||||
{{ item.noticeTitle }}
|
|
||||||
</div>
|
|
||||||
<div class="notice-info">
|
|
||||||
<span class="notice-type">
|
|
||||||
<dict-tag :options="sys_notice_type" :value="item.noticeType" />
|
|
||||||
</span>
|
|
||||||
<span class="notice-time">{{ parseTime(item.createTime, '{y}-{m}-{d}') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
<div class="notice-info">
|
||||||
<el-tab-pane label="通知" name="notification">
|
<span class="notice-type">
|
||||||
<el-empty v-if="notificationList.length === 0" description="暂无通知" />
|
<el-tag :type="getNoticeTypeTagType(item.noticeType)" size="small">
|
||||||
<div v-else class="notice-list">
|
{{ getNoticeTypeText(item.noticeType) }}
|
||||||
<div v-for="item in notificationList" :key="item.noticeId" class="notice-item" :class="{ 'is-read': isRead(item.noticeId) }" @click="viewDetail(item)">
|
</el-tag>
|
||||||
<div class="notice-title">
|
</span>
|
||||||
<span v-if="!isRead(item.noticeId)" class="unread-dot"></span>
|
<span class="notice-priority" v-if="item.priority">
|
||||||
{{ item.noticeTitle }}
|
<el-tag :type="getPriorityTagType(item.priority)" size="small" effect="plain">
|
||||||
</div>
|
{{ getPriorityText(item.priority) }}
|
||||||
<div class="notice-info">
|
</el-tag>
|
||||||
<span class="notice-type">
|
</span>
|
||||||
<dict-tag :options="sys_notice_type" :value="item.noticeType" />
|
<span class="notice-time">{{ parseTime(item.createTime, '{y}-{m}-{d}') }}</span>
|
||||||
</span>
|
|
||||||
<span class="notice-time">{{ parseTime(item.createTime, '{y}-{m}-{d}') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</div>
|
||||||
</el-tabs>
|
</div>
|
||||||
|
|
||||||
<!-- 公告/通知详情对话框 -->
|
<!-- 公告/通知详情对话框 -->
|
||||||
<el-dialog v-model="detailVisible" :title="currentNotice.noticeTitle" width="800px" append-to-body>
|
<el-dialog v-model="detailVisible" :title="currentNotice.noticeTitle" width="800px" append-to-body>
|
||||||
<div class="notice-detail">
|
<div class="notice-detail">
|
||||||
<div class="detail-header">
|
<div class="detail-header">
|
||||||
<span class="detail-type">
|
<div class="detail-type">
|
||||||
<dict-tag :options="sys_notice_type" :value="currentNotice.noticeType" />
|
<el-tag :type="getNoticeTypeTagType(currentNotice.noticeType)" size="small">
|
||||||
</span>
|
{{ getNoticeTypeText(currentNotice.noticeType) }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag :type="getPriorityTagType(currentNotice.priority)" size="small" effect="plain" style="margin-left: 8px;">
|
||||||
|
{{ getPriorityText(currentNotice.priority) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
<span class="detail-time">{{ parseTime(currentNotice.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
<span class="detail-time">{{ parseTime(currentNotice.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-content" v-html="currentNotice.noticeContent"></div>
|
<div class="detail-content" v-html="currentNotice.noticeContent"></div>
|
||||||
@@ -57,19 +48,13 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {ref} from 'vue'
|
import {ref} from 'vue'
|
||||||
import {getPublicNoticeList, getReadNoticeIds, getUserNotices, markAsRead} from '@/api/system/notice'
|
import {getReadNoticeIds, getUserNotices, markAsRead} from '@/api/system/notice'
|
||||||
import useUserStore from '@/store/modules/user'
|
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance()
|
|
||||||
const { sys_notice_type } = proxy.useDict('sys_notice_type')
|
|
||||||
const emit = defineEmits(['updateUnreadCount'])
|
const emit = defineEmits(['updateUnreadCount'])
|
||||||
const userStore = useUserStore()
|
|
||||||
|
|
||||||
const noticeVisible = ref(false)
|
const noticeVisible = ref(false)
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
const activeTab = ref('notice')
|
|
||||||
const noticeList = ref([])
|
const noticeList = ref([])
|
||||||
const notificationList = ref([])
|
|
||||||
const currentNotice = ref({})
|
const currentNotice = ref({})
|
||||||
const readNoticeIds = ref(new Set())
|
const readNoticeIds = ref(new Set())
|
||||||
|
|
||||||
@@ -101,42 +86,82 @@ function loadReadNoticeIds() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 排序:未读的排前面,已读的排后面,同类型按时间倒序
|
// 排序:未读的排前面,已读的排后面,同状态按优先级和时间排序
|
||||||
function sortNoticeList(list) {
|
function sortNoticeList(list) {
|
||||||
return list.sort((a, b) => {
|
return list.sort((a, b) => {
|
||||||
const aRead = isRead(a.noticeId)
|
const aRead = isRead(a.noticeId)
|
||||||
const bRead = isRead(b.noticeId)
|
const bRead = isRead(b.noticeId)
|
||||||
|
|
||||||
// 未读排在前面
|
// 未读排在前面
|
||||||
if (aRead !== bRead) {
|
if (aRead !== bRead) {
|
||||||
return aRead ? 1 : -1
|
return aRead ? 1 : -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同类型按创建时间倒序(最新的在前)
|
// 同状态按优先级排序(1高 2中 3低)
|
||||||
|
const priorityA = a.priority || '3'
|
||||||
|
const priorityB = b.priority || '3'
|
||||||
|
if (priorityA !== priorityB) {
|
||||||
|
return priorityA.localeCompare(priorityB)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同优先级按创建时间倒序(最新的在前)
|
||||||
return new Date(b.createTime) - new Date(a.createTime)
|
return new Date(b.createTime) - new Date(a.createTime)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载公告和通知
|
// 加载公告和通知(统一从一个接口获取)
|
||||||
function loadNotices() {
|
function loadNotices() {
|
||||||
// 加载公告列表
|
|
||||||
getPublicNoticeList({ pageNum: 1, pageSize: 10 }).then(response => {
|
|
||||||
let list = response.rows || response.data || []
|
|
||||||
noticeList.value = sortNoticeList(list)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 加载通知列表
|
|
||||||
getUserNotices().then(response => {
|
getUserNotices().then(response => {
|
||||||
let list = response.data || []
|
let list = response.data || []
|
||||||
notificationList.value = sortNoticeList(list)
|
noticeList.value = sortNoticeList(list)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取公告类型标签类型
|
||||||
|
// noticeType: 1=通知, 2=公告
|
||||||
|
function getNoticeTypeTagType(type) {
|
||||||
|
const typeMap = {
|
||||||
|
'1': 'primary', // 通知
|
||||||
|
'2': 'success' // 公告
|
||||||
|
}
|
||||||
|
return typeMap[type] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取公告类型文本
|
||||||
|
function getNoticeTypeText(type) {
|
||||||
|
const textMap = {
|
||||||
|
'1': '通知',
|
||||||
|
'2': '公告'
|
||||||
|
}
|
||||||
|
return textMap[type] || '公告'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取优先级标签类型
|
||||||
|
// priority: 1=高, 2=中, 3=低
|
||||||
|
function getPriorityTagType(priority) {
|
||||||
|
const typeMap = {
|
||||||
|
'1': 'danger', // 高优先级 - 红色
|
||||||
|
'2': 'warning', // 中优先级 - 橙色
|
||||||
|
'3': 'info' // 低优先级 - 灰色
|
||||||
|
}
|
||||||
|
return typeMap[priority] || 'info'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取优先级文本
|
||||||
|
function getPriorityText(priority) {
|
||||||
|
const textMap = {
|
||||||
|
'1': '高',
|
||||||
|
'2': '中',
|
||||||
|
'3': '低'
|
||||||
|
}
|
||||||
|
return textMap[priority] || '中'
|
||||||
|
}
|
||||||
|
|
||||||
// 查看详情
|
// 查看详情
|
||||||
function viewDetail(item) {
|
function viewDetail(item) {
|
||||||
currentNotice.value = item
|
currentNotice.value = item
|
||||||
detailVisible.value = true
|
detailVisible.value = true
|
||||||
|
|
||||||
// 标记为已读
|
// 标记为已读
|
||||||
if (!readNoticeIds.value.has(item.noticeId)) {
|
if (!readNoticeIds.value.has(item.noticeId)) {
|
||||||
markAsRead(item.noticeId).then(() => {
|
markAsRead(item.noticeId).then(() => {
|
||||||
@@ -192,6 +217,10 @@ defineExpose({
|
|||||||
color: #909399;
|
color: #909399;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.unread {
|
||||||
|
background-color: #fffbe6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-title {
|
.notice-title {
|
||||||
@@ -219,6 +248,12 @@ defineExpose({
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #909399;
|
color: #909399;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.notice-type,
|
||||||
|
.notice-priority {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-header {
|
.detail-header {
|
||||||
@@ -228,6 +263,11 @@ defineExpose({
|
|||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
border-bottom: 1px solid #EBEEF5;
|
border-bottom: 1px solid #EBEEF5;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.detail-type {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-time {
|
.detail-time {
|
||||||
|
|||||||
@@ -111,12 +111,11 @@ const unreadCount = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 获取公告类型图标
|
// 获取公告类型图标
|
||||||
|
// noticeType: 1=通知, 2=公告
|
||||||
const getNoticeTypeIcon = (type) => {
|
const getNoticeTypeIcon = (type) => {
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
'1': Bell, // 通知
|
'1': Bell, // 通知
|
||||||
'2': Warning, // 紧急
|
'2': InfoFilled // 公告
|
||||||
'3': InfoFilled, // 信息
|
|
||||||
'4': CircleCheck // 成功
|
|
||||||
}
|
}
|
||||||
return iconMap[type] || InfoFilled
|
return iconMap[type] || InfoFilled
|
||||||
}
|
}
|
||||||
@@ -152,36 +151,33 @@ const getPriorityTagType = (priority) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取公告类型样式类
|
// 获取公告类型样式类
|
||||||
|
// noticeType: 1=通知, 2=公告
|
||||||
const getNoticeTypeClass = (type) => {
|
const getNoticeTypeClass = (type) => {
|
||||||
const classMap = {
|
const classMap = {
|
||||||
'1': 'type-notice',
|
'1': 'type-notice',
|
||||||
'2': 'type-urgent',
|
'2': 'type-announcement'
|
||||||
'3': 'type-info',
|
|
||||||
'4': 'type-success'
|
|
||||||
}
|
}
|
||||||
return classMap[type] || 'type-info'
|
return classMap[type] || 'type-announcement'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取公告类型标签类型
|
// 获取公告类型标签类型
|
||||||
|
// noticeType: 1=通知, 2=公告
|
||||||
const getNoticeTypeTagType = (type) => {
|
const getNoticeTypeTagType = (type) => {
|
||||||
const typeMap = {
|
const typeMap = {
|
||||||
'1': '',
|
'1': 'primary',
|
||||||
'2': 'danger',
|
'2': 'success'
|
||||||
'3': 'info',
|
|
||||||
'4': 'success'
|
|
||||||
}
|
}
|
||||||
return typeMap[type] || ''
|
return typeMap[type] || 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取公告类型文本
|
// 获取公告类型文本
|
||||||
|
// noticeType: 1=通知, 2=公告
|
||||||
const getNoticeTypeText = (type) => {
|
const getNoticeTypeText = (type) => {
|
||||||
const textMap = {
|
const textMap = {
|
||||||
'1': '通知',
|
'1': '通知',
|
||||||
'2': '紧急',
|
'2': '公告'
|
||||||
'3': '信息',
|
|
||||||
'4': '成功'
|
|
||||||
}
|
}
|
||||||
return textMap[type] || '信息'
|
return textMap[type] || '公告'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化相对时间
|
// 格式化相对时间
|
||||||
@@ -390,21 +386,11 @@ defineExpose({
|
|||||||
background: #e6f7ff;
|
background: #e6f7ff;
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.type-urgent {
|
&.type-announcement {
|
||||||
background: #fff1f0;
|
|
||||||
color: #ff4d4f;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.type-info {
|
|
||||||
background: #f6ffed;
|
background: #f6ffed;
|
||||||
color: #52c41a;
|
color: #52c41a;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.type-success {
|
|
||||||
background: #f9f0ff;
|
|
||||||
color: #722ed1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-item-content {
|
.notice-item-content {
|
||||||
|
|||||||
@@ -127,12 +127,11 @@ const hasUnread = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 获取公告类型图标
|
// 获取公告类型图标
|
||||||
|
// noticeType: 1=通知, 2=公告
|
||||||
const getNoticeTypeIcon = (type) => {
|
const getNoticeTypeIcon = (type) => {
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
'1': Bell, // 通知
|
'1': Bell, // 通知
|
||||||
'2': Warning, // 紧急
|
'2': InfoFilled // 公告
|
||||||
'3': InfoFilled, // 信息
|
|
||||||
'4': CircleCheck // 成功
|
|
||||||
}
|
}
|
||||||
return iconMap[type] || InfoFilled
|
return iconMap[type] || InfoFilled
|
||||||
}
|
}
|
||||||
@@ -178,36 +177,33 @@ const getPriorityIcon = (priority) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取公告类型样式类
|
// 获取公告类型样式类
|
||||||
|
// noticeType: 1=通知, 2=公告
|
||||||
const getNoticeTypeClass = (type) => {
|
const getNoticeTypeClass = (type) => {
|
||||||
const classMap = {
|
const classMap = {
|
||||||
'1': 'type-notice',
|
'1': 'type-notice',
|
||||||
'2': 'type-urgent',
|
'2': 'type-announcement'
|
||||||
'3': 'type-info',
|
|
||||||
'4': 'type-success'
|
|
||||||
}
|
}
|
||||||
return classMap[type] || 'type-info'
|
return classMap[type] || 'type-announcement'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取公告类型标签类型
|
// 获取公告类型标签类型
|
||||||
|
// noticeType: 1=通知, 2=公告
|
||||||
const getNoticeTypeTagType = (type) => {
|
const getNoticeTypeTagType = (type) => {
|
||||||
const typeMap = {
|
const typeMap = {
|
||||||
'1': '',
|
'1': 'primary',
|
||||||
'2': 'danger',
|
'2': 'success'
|
||||||
'3': 'info',
|
|
||||||
'4': 'success'
|
|
||||||
}
|
}
|
||||||
return typeMap[type] || ''
|
return typeMap[type] || 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取公告类型文本
|
// 获取公告类型文本
|
||||||
|
// noticeType: 1=通知, 2=公告
|
||||||
const getNoticeTypeText = (type) => {
|
const getNoticeTypeText = (type) => {
|
||||||
const textMap = {
|
const textMap = {
|
||||||
'1': '通知',
|
'1': '通知',
|
||||||
'2': '紧急',
|
'2': '公告'
|
||||||
'3': '信息',
|
|
||||||
'4': '成功'
|
|
||||||
}
|
}
|
||||||
return textMap[type] || '信息'
|
return textMap[type] || '公告'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化日期
|
// 格式化日期
|
||||||
@@ -414,21 +410,11 @@ defineExpose({
|
|||||||
background: #e6f7ff;
|
background: #e6f7ff;
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.type-urgent {
|
&.type-announcement {
|
||||||
background: #fff1f0;
|
|
||||||
color: #ff4d4f;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.type-info {
|
|
||||||
background: #f6ffed;
|
background: #f6ffed;
|
||||||
color: #52c41a;
|
color: #52c41a;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.type-success {
|
|
||||||
background: #f9f0ff;
|
|
||||||
color: #722ed1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-item-content {
|
.notice-item-content {
|
||||||
|
|||||||
@@ -20,26 +20,17 @@ const tagsViewStore = useTagsViewStore()
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-main {
|
.app-main {
|
||||||
/* 50= navbar 50 */
|
|
||||||
min-height: calc(100vh - 50px);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fixed-header + .app-main {
|
.fixed-header ~ .app-main {
|
||||||
padding-top: 50px;
|
padding-top: 34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hasTagsView {
|
.hasTagsView .app-main {
|
||||||
.app-main {
|
padding-top: 0;
|
||||||
/* 84 = navbar + tags-view = 50 + 34 */
|
|
||||||
min-height: calc(100vh - 84px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fixed-header + .app-main {
|
|
||||||
padding-top: 84px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<div class="right-menu">
|
<div class="left-menu">
|
||||||
<template v-if="appStore.device !== 'mobile'">
|
<div class="hamburger-container">
|
||||||
<header-search id="header-search" class="right-menu-item" />
|
<div class="hamburger" @click="toggleSideBar">
|
||||||
</template>
|
<el-icon :size="20">
|
||||||
<!-- 公告和通知按钮 -->
|
<component :is="sidebar.opened ? 'Fold' : 'Expand'" />
|
||||||
<el-tooltip content="公告/通知" placement="bottom">
|
</el-icon>
|
||||||
<div class="right-menu-item notice-btn" @click="openNoticePanel">
|
|
||||||
<el-badge :value="unreadCount" :hidden="unreadCount === 0" class="notice-badge">
|
|
||||||
<el-icon><Bell /></el-icon>
|
|
||||||
</el-badge>
|
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
</div>
|
||||||
|
<!-- 搜索和公告通知 -->
|
||||||
|
<div class="left-actions">
|
||||||
|
<template v-if="appStore.device !== 'mobile'">
|
||||||
|
<header-search id="header-search" class="left-action-item" />
|
||||||
|
</template>
|
||||||
|
<!-- 公告和通知按钮 -->
|
||||||
|
<el-tooltip content="公告/通知" placement="bottom">
|
||||||
|
<div class="left-action-item notice-btn" @click="openNoticePanel">
|
||||||
|
<el-badge :value="unreadCount" :hidden="unreadCount === 0" class="notice-badge">
|
||||||
|
<el-icon><Bell /></el-icon>
|
||||||
|
</el-badge>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right-menu">
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
<div class="avatar-wrapper">
|
<div class="avatar-wrapper">
|
||||||
<el-dropdown
|
<el-dropdown
|
||||||
@@ -40,9 +52,6 @@
|
|||||||
<router-link to="/user/profile">
|
<router-link to="/user/profile">
|
||||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||||
</router-link>
|
</router-link>
|
||||||
<!-- <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
|
|
||||||
<span>布局设置</span>
|
|
||||||
</el-dropdown-item> -->
|
|
||||||
<el-dropdown-item divided command="logout">
|
<el-dropdown-item divided command="logout">
|
||||||
<span>退出登录</span>
|
<span>退出登录</span>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
@@ -86,8 +95,8 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button type="primary" @click="submit">确 定</el-button>
|
<el-button type="primary" @click="submit">确定</el-button>
|
||||||
<el-button @click="showDialog = false">取 消</el-button>
|
<el-button @click="showDialog = false">取消</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@@ -98,9 +107,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, ref} from 'vue';
|
import {onMounted, ref, computed} from 'vue';
|
||||||
import {ElMessageBox} from 'element-plus';
|
import {ElMessageBox} from 'element-plus';
|
||||||
import {Bell} from '@element-plus/icons-vue';
|
import {Fold, Expand, Bell} from '@element-plus/icons-vue';
|
||||||
import HeaderSearch from '@/components/HeaderSearch';
|
import HeaderSearch from '@/components/HeaderSearch';
|
||||||
import NoticePanel from '@/components/NoticePanel';
|
import NoticePanel from '@/components/NoticePanel';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
@@ -108,16 +117,19 @@ import useUserStore from '@/store/modules/user';
|
|||||||
import useSettingsStore from '@/store/modules/settings';
|
import useSettingsStore from '@/store/modules/settings';
|
||||||
import {getOrg, switchOrg} from '@/api/login';
|
import {getOrg, switchOrg} from '@/api/login';
|
||||||
import {getUnreadCount} from '@/api/system/notice';
|
import {getUnreadCount} from '@/api/system/notice';
|
||||||
|
import {useRouter} from 'vue-router';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const settingsStore = useSettingsStore();
|
const router = useRouter();
|
||||||
const orgOptions = ref([]);
|
const orgOptions = ref([]);
|
||||||
const showDialog = ref(false);
|
const showDialog = ref(false);
|
||||||
const orgId = ref('');
|
const orgId = ref('');
|
||||||
const noticePanelRef = ref(null);
|
const noticePanelRef = ref(null);
|
||||||
const unreadCount = ref(0);
|
const unreadCount = ref(0);
|
||||||
|
|
||||||
|
const sidebar = computed(() => appStore.sidebar);
|
||||||
|
|
||||||
// 加载未读数量
|
// 加载未读数量
|
||||||
function loadUnreadCount() {
|
function loadUnreadCount() {
|
||||||
getUnreadCount().then(res => {
|
getUnreadCount().then(res => {
|
||||||
@@ -132,6 +144,11 @@ function updateUnreadCount() {
|
|||||||
loadUnreadCount();
|
loadUnreadCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 切换侧边栏
|
||||||
|
function toggleSideBar() {
|
||||||
|
appStore.toggleSideBar();
|
||||||
|
}
|
||||||
|
|
||||||
function loadOrgList() {
|
function loadOrgList() {
|
||||||
getOrg().then((res) => {
|
getOrg().then((res) => {
|
||||||
orgOptions.value = res.data;
|
orgOptions.value = res.data;
|
||||||
@@ -220,16 +237,78 @@ function openNoticePanel() {
|
|||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
.navbar {
|
.navbar {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
overflow: visible;
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: transparent;
|
background-color: #fff;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: space-between;
|
||||||
padding-right: 10px;
|
padding: 0 15px;
|
||||||
flex-shrink: 0;
|
|
||||||
min-width: 200px;
|
.left-menu {
|
||||||
z-index: 1002;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.hamburger-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.hamburger {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 12px;
|
||||||
|
height: 50px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
transition: background 0.3s;
|
||||||
|
color: #5a5e66;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.left-action-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 10px;
|
||||||
|
height: 50px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #606266;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-badge {
|
||||||
|
:deep(.el-badge__content) {
|
||||||
|
top: -5px;
|
||||||
|
right: -5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.right-menu {
|
.right-menu {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -241,49 +320,6 @@ function openNoticePanel() {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-menu-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 6px;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #606266;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
&.hover-effect {
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(255,255,255,0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-btn {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0 10px;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.notice-badge {
|
|
||||||
:deep(.el-badge__content) {
|
|
||||||
top: -5px;
|
|
||||||
right: -5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-icon {
|
|
||||||
font-size: 20px;
|
|
||||||
color: #606266;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #409eff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-container {
|
.avatar-container {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
@@ -313,8 +349,8 @@ function openNoticePanel() {
|
|||||||
|
|
||||||
.user-avatar {
|
.user-avatar {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 30px;
|
width: 32px;
|
||||||
height: 30px;
|
height: 32px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@@ -364,8 +400,21 @@ function openNoticePanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
min-width: 150px;
|
padding: 0 10px;
|
||||||
padding-right: 5px;
|
|
||||||
|
.left-menu {
|
||||||
|
.hamburger-container {
|
||||||
|
.hamburger {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-actions {
|
||||||
|
.left-action-item {
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.right-menu {
|
.right-menu {
|
||||||
.avatar-container {
|
.avatar-container {
|
||||||
@@ -388,8 +437,8 @@ function openNoticePanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-avatar {
|
.user-avatar {
|
||||||
width: 24px;
|
width: 28px;
|
||||||
height: 24px;
|
height: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -397,8 +446,21 @@ function openNoticePanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
min-width: 120px;
|
padding: 0 8px;
|
||||||
padding-right: 5px;
|
|
||||||
|
.left-menu {
|
||||||
|
.hamburger-container {
|
||||||
|
.hamburger {
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-actions {
|
||||||
|
.left-action-item {
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.right-menu {
|
.right-menu {
|
||||||
.avatar-container {
|
.avatar-container {
|
||||||
@@ -415,6 +477,11 @@ function openNoticePanel() {
|
|||||||
max-width: 80px;
|
max-width: 80px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,36 +7,25 @@
|
|||||||
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground,
|
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- <el-image
|
<router-link class="sidebar-logo-link" to="/index">
|
||||||
:src="sideTheme === 'theme-dark' ? Logo : Logo"
|
<el-image
|
||||||
class="sidebar-logo"
|
:src="logoImage"
|
||||||
fit="scale-down"
|
class="sidebar-logo"
|
||||||
/> -->
|
fit="contain"
|
||||||
<!-- <transition name="sidebarLogoFade">
|
/>
|
||||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
<div v-if="!collapse" class="logo-text" :style="{ color: textColor }">
|
||||||
<el-image
|
<h1 class="sidebar-title">{{ title }}</h1>
|
||||||
v-if="logo"
|
<p v-if="displayName" class="hospital-name">{{ displayName }}</p>
|
||||||
:src="sideTheme === 'theme-dark' ? logoNew : logoBlack"
|
</div>
|
||||||
class="sidebar-logo"
|
</router-link>
|
||||||
fit="scale-down"
|
|
||||||
/>
|
|
||||||
</router-link>
|
|
||||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
|
||||||
<el-image
|
|
||||||
v-if="logo"
|
|
||||||
:src="sideTheme === 'theme-dark' ? logoNew : logoBlack"
|
|
||||||
class="sidebar-logo"
|
|
||||||
fit="scale-down"
|
|
||||||
/>
|
|
||||||
</router-link>
|
|
||||||
</transition> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import variables from '@/assets/styles/variables.module.scss';
|
import variables from '@/assets/styles/variables.module.scss';
|
||||||
import useSettingsStore from '@/store/modules/settings';
|
import useSettingsStore from '@/store/modules/settings';
|
||||||
import {computed, ref} from 'vue';
|
import useUserStore from '@/store/modules/user';
|
||||||
|
import {computed} from 'vue';
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
collapse: {
|
collapse: {
|
||||||
@@ -45,64 +34,99 @@ defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const title = import.meta.env.VITE_APP_TITLE;
|
const title = import.meta.env.VITE_APP_TITLE || '医院管理系统';
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||||
const logo = ref(true);
|
const displayName = computed(() => userStore.tenantName || userStore.hospitalName || userStore.orgName || '');
|
||||||
|
|
||||||
|
const textColor = computed(() => {
|
||||||
|
return sideTheme.value === 'theme-dark' ? '#fff' : '#303133';
|
||||||
|
});
|
||||||
|
|
||||||
|
const logoImage = computed(() => {
|
||||||
|
return new URL('@/assets/logo/LOGO.jpg', import.meta.url).href;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.sidebarLogoFade-enter-active {
|
|
||||||
transition: opacity 1.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarLogoFade-enter,
|
|
||||||
.sidebarLogoFade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-logo-container {
|
.sidebar-logo-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: auto;
|
width: 100%;
|
||||||
min-width: 120px;
|
|
||||||
max-width: 160px;
|
|
||||||
height: 50px;
|
height: 50px;
|
||||||
line-height: 50px;
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0 8px;
|
display: flex;
|
||||||
margin-left: 5px;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
& .sidebar-logo-link {
|
.sidebar-logo-link {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
padding: 8px 12px;
|
||||||
|
gap: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
& .sidebar-logo {
|
&:hover {
|
||||||
width: auto;
|
opacity: 0.8;
|
||||||
max-width: 120px;
|
|
||||||
height: 28px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& .sidebar-title {
|
.sidebar-logo {
|
||||||
display: inline-block;
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-logo {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #fff;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 50px;
|
font-size: 15px;
|
||||||
font-size: 14px;
|
|
||||||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
||||||
vertical-align: middle;
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hospital-name {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1.2;
|
||||||
|
opacity: 0.85;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.collapse {
|
&.collapse {
|
||||||
.sidebar-logo {
|
.sidebar-logo-link {
|
||||||
margin-right: 0px;
|
padding: 0;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="{ 'has-logo': showLogo }"
|
:class="[
|
||||||
class="sidebar-wrapper"
|
'sidebar-wrapper',
|
||||||
|
{ 'has-logo': showLogo },
|
||||||
|
{ 'is-collapse': isCollapse }
|
||||||
|
]"
|
||||||
:style="{
|
:style="{
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground,
|
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- <logo v-if="showLogo" :collapse="isCollapse" /> -->
|
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||||
<div class="menu-container">
|
<el-scrollbar class="sidebar-scrollbar">
|
||||||
<div class="menu-scrollbar">
|
<el-menu
|
||||||
<el-menu
|
:default-active="activeMenu"
|
||||||
:default-active="activeMenu"
|
:collapse="isCollapse"
|
||||||
:collapse="isCollapse"
|
:background-color="
|
||||||
:background-color="
|
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground
|
||||||
sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground
|
"
|
||||||
"
|
:text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
|
||||||
:text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
|
:unique-opened="true"
|
||||||
:unique-opened="true"
|
:active-text-color="theme"
|
||||||
:active-text-color="theme"
|
:collapse-transition="false"
|
||||||
:collapse-transition="false"
|
mode="vertical"
|
||||||
mode="horizontal"
|
>
|
||||||
>
|
<sidebar-item
|
||||||
<sidebar-item
|
v-for="(route, index) in sidebarRouters"
|
||||||
v-for="(route, index) in sidebarRouters"
|
:key="route.path + index"
|
||||||
:key="route.path + index"
|
:item="route"
|
||||||
:item="route"
|
:base-path="route.path"
|
||||||
:base-path="route.path"
|
/>
|
||||||
/>
|
</el-menu>
|
||||||
</el-menu>
|
</el-scrollbar>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<navbar @setLayout="setLayout" class="navbar-container" />
|
|
||||||
<settings ref="settingRef" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import Logo from './Logo';
|
||||||
import SidebarItem from './SidebarItem';
|
import SidebarItem from './SidebarItem';
|
||||||
import variables from '@/assets/styles/variables.module.scss';
|
import variables from '@/assets/styles/variables.module.scss';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import useSettingsStore from '@/store/modules/settings';
|
import useSettingsStore from '@/store/modules/settings';
|
||||||
import usePermissionStore from '@/store/modules/permission';
|
import usePermissionStore from '@/store/modules/permission';
|
||||||
import {computed, ref} from 'vue';
|
import {computed} from 'vue';
|
||||||
import {useRoute} from 'vue-router';
|
import {useRoute} from 'vue-router';
|
||||||
import {Navbar, Settings} from '@/layout/components';
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
@@ -65,134 +64,122 @@ const activeMenu = computed(() => {
|
|||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
});
|
});
|
||||||
const settingRef = ref(null);
|
|
||||||
function setLayout() {
|
|
||||||
settingRef.value.openSetting();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@import '@/assets/styles/variables.module.scss';
|
||||||
|
|
||||||
.sidebar-wrapper {
|
.sidebar-wrapper {
|
||||||
|
width: $sideBarWidth;
|
||||||
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
height: 50px;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
transition: width 0.28s;
|
||||||
}
|
|
||||||
|
|
||||||
.menu-container {
|
|
||||||
flex: 1;
|
|
||||||
height: 50px;
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-scrollbar {
|
|
||||||
height: 50px;
|
|
||||||
width: 100%;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
height: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar:vertical {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: rgba(255, 255, 255, 0.3);
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-menu--horizontal {
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center !important;
|
|
||||||
border-bottom: none !important;
|
|
||||||
background-color: transparent !important;
|
|
||||||
min-width: auto;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
height: 50px;
|
|
||||||
|
|
||||||
& > .el-menu-item,
|
|
||||||
& > .el-sub-menu {
|
|
||||||
height: 50px;
|
|
||||||
line-height: 50px;
|
|
||||||
color: #fff;
|
|
||||||
padding: 0 15px !important;
|
|
||||||
font-size: 14px;
|
|
||||||
min-width: 120px !important;
|
|
||||||
flex-shrink: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.svg-icon) {
|
|
||||||
margin-right: 8px !important;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-sub-menu__title) {
|
|
||||||
padding-right: 25px !important;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-sub-menu__icon-arrow) {
|
|
||||||
right: 8px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.menu-title) {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-container {
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
height: 50px;
|
|
||||||
z-index: 1002;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式处理 */
|
&.is-collapse {
|
||||||
@media (max-width: 768px) {
|
width: 54px;
|
||||||
.menu-container {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu--horizontal {
|
.mobile & {
|
||||||
& > .el-menu-item,
|
position: fixed;
|
||||||
& > .el-sub-menu {
|
z-index: 1001;
|
||||||
padding: 0 12px !important;
|
height: 100%;
|
||||||
min-width: 80px !important;
|
top: 0;
|
||||||
font-size: 12px;
|
left: 0;
|
||||||
|
transform: translateX(0);
|
||||||
|
transition: transform 0.28s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-collapse.mobile & {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-logo {
|
||||||
|
.sidebar-scrollbar {
|
||||||
|
height: calc(100% - #{$logoHeight});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-scrollbar {
|
||||||
|
height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-scrollbar::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-scrollbar::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-scrollbar::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu {
|
||||||
|
border: none;
|
||||||
|
height: 100%;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-menu-item),
|
||||||
|
:deep(.el-sub-menu__title) {
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-menu-item) {
|
||||||
|
&.is-active {
|
||||||
|
background-color: var(--current-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-sub-menu) {
|
||||||
|
.el-menu-item {
|
||||||
|
min-width: $sideBarWidth !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
> .el-sub-menu__title {
|
||||||
|
color: var(--current-color) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
:deep(.el-menu--collapse) {
|
||||||
.el-menu--horizontal {
|
.el-menu-item,
|
||||||
& > .el-menu-item,
|
.el-sub-menu__title {
|
||||||
& > .el-sub-menu {
|
padding: 0 20px !important;
|
||||||
padding: 0 8px !important;
|
text-align: center;
|
||||||
min-width: 60px !important;
|
|
||||||
font-size: 11px;
|
span {
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
visibility: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-sub-menu {
|
||||||
|
&.is-opened {
|
||||||
|
> .el-sub-menu__title .el-icon-arrow-right {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
|
<div class="app-wrapper">
|
||||||
|
<!-- 遮罩层 -->
|
||||||
<div
|
<div
|
||||||
v-if="device === 'mobile' && sidebar.opened"
|
v-if="device === 'mobile' && sidebar.opened"
|
||||||
class="drawer-bg"
|
class="drawer-bg"
|
||||||
@click="handleClickOutside"
|
@click="handleClickOutside"
|
||||||
/>
|
/>
|
||||||
<div class="top-container">
|
<!-- 左侧侧边栏 -->
|
||||||
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
|
<sidebar v-if="!sidebar.hide" />
|
||||||
<!-- <navbar @setLayout="setLayout" class="navbar-container" /> -->
|
<!-- 右侧主容器 -->
|
||||||
</div>
|
<div class="main-wrapper">
|
||||||
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
|
<!-- 顶部导航栏 -->
|
||||||
<div :class="{ 'fixed-header': fixedHeader }">
|
<navbar @setLayout="setLayout" />
|
||||||
<tags-view v-if="needTagsView" />
|
<!-- 内容区 -->
|
||||||
|
<div :class="{ 'hasTagsView': needTagsView }" class="content-wrapper">
|
||||||
|
<!-- 标签栏 -->
|
||||||
|
<div v-if="needTagsView" :class="{ 'fixed-header': fixedHeader }">
|
||||||
|
<tags-view />
|
||||||
|
</div>
|
||||||
|
<!-- 主内容 -->
|
||||||
|
<app-main />
|
||||||
</div>
|
</div>
|
||||||
<app-main />
|
|
||||||
<settings ref="settingRef" />
|
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 设置组件 -->
|
||||||
|
<settings ref="settingRef" />
|
||||||
<!-- 公告弹窗组件 -->
|
<!-- 公告弹窗组件 -->
|
||||||
<notice-popup ref="noticePopupRef" />
|
<notice-popup ref="noticePopupRef" />
|
||||||
</div>
|
</div>
|
||||||
@@ -24,7 +32,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {useWindowSize} from '@vueuse/core';
|
import {useWindowSize} from '@vueuse/core';
|
||||||
import Sidebar from './components/Sidebar/index.vue';
|
import Sidebar from './components/Sidebar/index.vue';
|
||||||
import {AppMain, Settings, TagsView} from './components';
|
import {AppMain, Settings, TagsView, Navbar} from './components';
|
||||||
import NoticePopup from '@/components/NoticePopup/index.vue';
|
import NoticePopup from '@/components/NoticePopup/index.vue';
|
||||||
|
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
@@ -38,14 +46,7 @@ const device = computed(() => useAppStore().device);
|
|||||||
const needTagsView = computed(() => settingsStore.tagsView);
|
const needTagsView = computed(() => settingsStore.tagsView);
|
||||||
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
||||||
|
|
||||||
const classObj = computed(() => ({
|
const { width } = useWindowSize();
|
||||||
hideSidebar: !sidebar.value.opened,
|
|
||||||
openSidebar: sidebar.value.opened,
|
|
||||||
withoutAnimation: sidebar.value.withoutAnimation,
|
|
||||||
mobile: device.value === 'mobile',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const { width, height } = useWindowSize();
|
|
||||||
const WIDTH = 992; // refer to Bootstrap's responsive design
|
const WIDTH = 992; // refer to Bootstrap's responsive design
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
@@ -83,66 +84,67 @@ defineExpose({
|
|||||||
|
|
||||||
.app-wrapper {
|
.app-wrapper {
|
||||||
@include clearfix;
|
@include clearfix;
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
&.mobile.openSidebar {
|
display: flex;
|
||||||
position: fixed;
|
overflow: hidden;
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer-bg {
|
.drawer-bg {
|
||||||
background: #000;
|
background: #000;
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
top: 0;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
background-color: $base-menu-background;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 1001;
|
z-index: 999;
|
||||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.35);
|
|
||||||
overflow: visible;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-container {
|
.main-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 50px;
|
display: flex;
|
||||||
min-width: 0;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-container {
|
.navbar-container {
|
||||||
min-width: 280px;
|
|
||||||
height: 50px;
|
height: 50px;
|
||||||
|
width: 100%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 0;
|
||||||
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fixed-header {
|
.fixed-header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
left: 0;
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: width 0.28s;
|
padding: 0 15px;
|
||||||
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile .fixed-header {
|
.sidebarHide {
|
||||||
width: 100%;
|
.sidebar-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-container {
|
.mobile {
|
||||||
padding-top: 50px;
|
.main-wrapper {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const useUserStore = defineStore(
|
|||||||
roles: [],
|
roles: [],
|
||||||
permissions: [],
|
permissions: [],
|
||||||
tenantId: '',
|
tenantId: '',
|
||||||
|
tenantName: '', // 租户名称
|
||||||
hospitalName:''
|
hospitalName:''
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
@@ -63,6 +64,7 @@ const useUserStore = defineStore(
|
|||||||
this.fixmedinsCode = res.optionJson.fixmedinsCode
|
this.fixmedinsCode = res.optionJson.fixmedinsCode
|
||||||
this.avatar = avatar
|
this.avatar = avatar
|
||||||
this.hospitalName = res.optionJson.hospitalName
|
this.hospitalName = res.optionJson.hospitalName
|
||||||
|
this.tenantName = res.tenantName || ''
|
||||||
|
|
||||||
resolve(res)
|
resolve(res)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
|||||||
@@ -1,287 +1,154 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="awaitingBtn">
|
<!-- 首页内容为空,只显示欢迎信息 -->
|
||||||
<el-button @click="awaitingMedicineBtn">
|
<div class="welcome-content">
|
||||||
效期预警
|
<div class="welcome-card">
|
||||||
<span>{{ total }}</span>
|
<h1 class="welcome-title">欢迎使用</h1>
|
||||||
</el-button>
|
<p class="welcome-subtitle">医院管理系统</p>
|
||||||
|
<div class="quick-actions">
|
||||||
<!-- <el-select v-model="selectValue" @change="handelChange" @keyup.enter="handelEnter">
|
<div class="action-item" @click="handleExpiryWarning">
|
||||||
<el-option label="测试1" value="1"/>
|
<el-icon :size="32" color="#e6a23c">
|
||||||
<el-option label="测试2" value="2 "/>
|
<Warning />
|
||||||
</el-select> -->
|
</el-icon>
|
||||||
|
<span>效期预警</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="logo">
|
|
||||||
<img src="/src/assets/images/jlau.jpg" />
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="Index">
|
<script setup name="Index">
|
||||||
import {getproductReturnPage} from './medicationmanagement/statisticalManagement/statisticalManagent';
|
|
||||||
import {useStore} from '@/store/store';
|
import {useStore} from '@/store/store';
|
||||||
|
import {Warning} from '@element-plus/icons-vue';
|
||||||
|
import {useRouter} from 'vue-router';
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const version = ref('3.8.7');
|
|
||||||
const total = ref(0);
|
|
||||||
const selectValue = ref('');
|
|
||||||
function awaitingMedicineBtn() {
|
|
||||||
store.setRemainingDays(180);
|
|
||||||
console.log(store.remainingDays);
|
|
||||||
|
|
||||||
|
function handleExpiryWarning() {
|
||||||
|
store.setRemainingDays(180);
|
||||||
router.push({
|
router.push({
|
||||||
path: '/medicationmanagement/statisticalManagement/statisticalManagement',
|
path: '/medicationmanagement/statisticalManagement/statisticalManagement',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function goTarget(url) {
|
|
||||||
window.open(url, '__blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handelEnter() {
|
|
||||||
console.log('enter');
|
|
||||||
}
|
|
||||||
|
|
||||||
// function handelChange(val) {
|
|
||||||
// console.log(val);
|
|
||||||
// }
|
|
||||||
|
|
||||||
function getExpirationWarningCount() {
|
|
||||||
getproductReturnPage({ pageNo: 1, pageSize: 10, remainingDays: 180 }).then((res) => {
|
|
||||||
total.value = res.data.total || 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getExpirationWarningCount();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.dashboard-container {
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-content {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
padding: 40px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-card {
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 60px 80px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||||
|
text-align: center;
|
||||||
|
animation: fadeIn 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-title {
|
||||||
|
font-size: 42px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-subtitle {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #909399;
|
||||||
|
margin: 0 0 50px 0;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 30px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.awaitingBtn {
|
|
||||||
.el-button {
|
|
||||||
border: 1px #166773 solid;
|
|
||||||
span {
|
|
||||||
color: red;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.el-button:hover {
|
|
||||||
border: 1px #166773 solid;
|
|
||||||
color: #166773;
|
|
||||||
span {
|
|
||||||
color: red;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 24px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 24px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-title {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #606266;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-value {
|
|
||||||
font-size: 32px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #303133;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-stats {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 12px;
|
padding: 30px;
|
||||||
color: #67c23a;
|
background: #fff;
|
||||||
}
|
border-radius: 12px;
|
||||||
|
|
||||||
.card-stats.down {
|
|
||||||
color: #f56c6c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-icon {
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-container {
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 24px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
||||||
margin-bottom: 30px;
|
|
||||||
height: 300px;
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
width: 100% !important;
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-content {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-container,
|
|
||||||
.todo-container {
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 24px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-title,
|
|
||||||
.todo-title {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #303133;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-list {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-item {
|
|
||||||
padding: 12px 0;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-list {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px 0;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-count {
|
|
||||||
color: #606266;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-container {
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 24px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
padding: 12px;
|
|
||||||
text-align: left;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #303133;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:last-child td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
border: none;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
outline: none;
|
min-width: 160px;
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
&:hover {
|
||||||
background-color: #1890ff;
|
transform: translateY(-5px);
|
||||||
color: #fff;
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary:hover {
|
span {
|
||||||
background-color: #40a9ff;
|
color: #409eff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.btn-outline {
|
span {
|
||||||
background-color: transparent;
|
margin-top: 12px;
|
||||||
border: 1px solid #dcdfe6;
|
font-size: 16px;
|
||||||
color: #606266;
|
color: #303133;
|
||||||
}
|
font-weight: 500;
|
||||||
|
transition: color 0.3s;
|
||||||
.btn-outline:hover {
|
}
|
||||||
border-color: #c6e2ff;
|
|
||||||
color: #40a9ff;
|
|
||||||
background-color: #ecf5ff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.bottom-content {
|
.welcome-card {
|
||||||
grid-template-columns: 1fr;
|
padding: 40px 30px;
|
||||||
|
margin: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-grid {
|
.welcome-title {
|
||||||
grid-template-columns: 1fr;
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-subtitle {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-item {
|
||||||
|
min-width: 100%;
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
759
openhis-ui-vue3/src/views/surgerymanage/index.vue
Normal file
@@ -0,0 +1,759 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<!-- 查询表单 -->
|
||||||
|
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" class="query-form">
|
||||||
|
<el-form-item label="手术编号" prop="surgeryNo">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.surgeryNo"
|
||||||
|
placeholder="请输入手术编号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="手术名称" prop="surgeryName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.surgeryName"
|
||||||
|
placeholder="请输入手术名称"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="患者姓名" prop="patientName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.patientName"
|
||||||
|
placeholder="请输入患者姓名"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="手术状态" prop="statusEnum">
|
||||||
|
<el-select v-model="queryParams.statusEnum" placeholder="请选择手术状态" clearable style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in surgeryStatusOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="手术类型" prop="surgeryTypeEnum">
|
||||||
|
<el-select v-model="queryParams.surgeryTypeEnum" placeholder="请选择手术类型" clearable style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in surgeryTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="计划时间" prop="plannedTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="queryParams.plannedTime"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 240px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="search-buttons">
|
||||||
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button type="primary" plain icon="Plus" @click="handleAdd"> 新增手术 </el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button type="" plain icon="Refresh" @click="getPageList">刷新</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="surgeryList" row-key="id">
|
||||||
|
<el-table-column label="手术编号" align="center" prop="surgeryNo" width="150" />
|
||||||
|
<el-table-column label="患者姓名" align="center" prop="patientName" width="100" />
|
||||||
|
<el-table-column label="性别" align="center" prop="patientGender" width="60" />
|
||||||
|
<el-table-column label="年龄" align="center" prop="patientAge" width="60" />
|
||||||
|
<el-table-column label="手术名称" align="center" prop="surgeryName" min-width="150" show-overflow-tooltip />
|
||||||
|
<el-table-column label="手术类型" align="center" prop="surgeryTypeEnum_dictText" width="100" />
|
||||||
|
<el-table-column label="手术等级" align="center" prop="surgeryLevel_dictText" width="100" />
|
||||||
|
<el-table-column label="手术状态" align="center" prop="statusEnum_dictText" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getStatusType(scope.row.statusEnum)">
|
||||||
|
{{ scope.row.statusEnum_dictText }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="计划时间" align="center" prop="plannedTime" width="160" />
|
||||||
|
<el-table-column label="主刀医生" align="center" prop="mainSurgeonName" width="100" />
|
||||||
|
<el-table-column label="麻醉医生" align="center" prop="anesthetistName" width="100" />
|
||||||
|
<el-table-column label="手术室" align="center" prop="operatingRoomName" width="120" />
|
||||||
|
<el-table-column label="执行科室" align="center" prop="orgName" width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column label="操作" align="center" width="200" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" @click="handleView(scope.row)">查看</el-button>
|
||||||
|
<el-button link type="primary" @click="handleEdit(scope.row)" v-if="scope.row.statusEnum === 0 || scope.row.statusEnum === 1">编辑</el-button>
|
||||||
|
<el-button link type="primary" @click="handleStart(scope.row)" v-if="scope.row.statusEnum === 1">开始</el-button>
|
||||||
|
<el-button link type="primary" @click="handleComplete(scope.row)" v-if="scope.row.statusEnum === 2">完成</el-button>
|
||||||
|
<el-button link type="danger" @click="handleDelete(scope.row)" v-if="scope.row.statusEnum === 0 || scope.row.statusEnum === 1">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<pagination
|
||||||
|
v-show="total > 0"
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getPageList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增或修改手术对话框 -->
|
||||||
|
<el-dialog :title="title" v-model="open" width="800px" @close="cancel" append-to-body :close-on-click-modal="false">
|
||||||
|
<el-form ref="surgeryRef" :model="form" :rules="rules" label-width="120px">
|
||||||
|
<el-form-item label="id" prop="id" v-show="false">
|
||||||
|
<el-input v-model="form.id" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="患者" prop="patientId">
|
||||||
|
<el-select v-model="form.patientId" placeholder="请选择患者" filterable style="width: 100%" :disabled="form.id">
|
||||||
|
<el-option
|
||||||
|
v-for="item in patientList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name + ' (' + item.busNo + ')'"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="就诊流水号" prop="encounterId">
|
||||||
|
<el-input v-model="form.encounterNo" placeholder="请选择就诊" readonly>
|
||||||
|
<template #append>
|
||||||
|
<el-button icon="Search" @click="selectEncounter" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="手术名称" prop="surgeryName">
|
||||||
|
<el-input v-model="form.surgeryName" placeholder="请输入手术名称" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="手术编码" prop="surgeryCode">
|
||||||
|
<el-input v-model="form.surgeryCode" placeholder="请输入手术编码" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="手术类型" prop="surgeryTypeEnum">
|
||||||
|
<el-select v-model="form.surgeryTypeEnum" placeholder="请选择手术类型" style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in surgeryTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="手术等级" prop="surgeryLevel">
|
||||||
|
<el-select v-model="form.surgeryLevel" placeholder="请选择手术等级" style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in surgeryLevelOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="计划手术时间" prop="plannedTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.plannedTime"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="选择日期时间"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="手术部位" prop="bodySite">
|
||||||
|
<el-input v-model="form.bodySite" placeholder="请输入手术部位" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-divider content-position="left">手术团队</el-divider>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="主刀医生" prop="mainSurgeonId">
|
||||||
|
<el-select v-model="form.mainSurgeonId" placeholder="请选择主刀医生" filterable style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in doctorList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="麻醉医生" prop="anesthetistId">
|
||||||
|
<el-select v-model="form.anesthetistId" placeholder="请选择麻醉医生" filterable style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in doctorList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="助手1" prop="assistant1Id">
|
||||||
|
<el-select v-model="form.assistant1Id" placeholder="请选择助手1" filterable clearable style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in doctorList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="助手2" prop="assistant2Id">
|
||||||
|
<el-select v-model="form.assistant2Id" placeholder="请选择助手2" filterable clearable style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in doctorList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="巡回护士" prop="scrubNurseId">
|
||||||
|
<el-select v-model="form.scrubNurseId" placeholder="请选择巡回护士" filterable clearable style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in nurseList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="麻醉方式" prop="anesthesiaTypeEnum">
|
||||||
|
<el-select v-model="form.anesthesiaTypeEnum" placeholder="请选择麻醉方式" style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in anesthesiaTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="手术室" prop="operatingRoomId">
|
||||||
|
<el-input v-model="form.operatingRoomName" placeholder="请输入手术室">
|
||||||
|
<template #append>
|
||||||
|
<el-button icon="Search" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="执行科室" prop="orgId">
|
||||||
|
<el-tree-select
|
||||||
|
v-model="form.orgId"
|
||||||
|
:data="orgList"
|
||||||
|
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||||
|
placeholder="请选择执行科室"
|
||||||
|
check-strictly
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-divider content-position="left">诊断信息</el-divider>
|
||||||
|
|
||||||
|
<el-form-item label="术前诊断" prop="preoperativeDiagnosis">
|
||||||
|
<el-input v-model="form.preoperativeDiagnosis" type="textarea" placeholder="请输入术前诊断" :rows="3" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="术后诊断" prop="postoperativeDiagnosis">
|
||||||
|
<el-input v-model="form.postoperativeDiagnosis" type="textarea" placeholder="请输入术后诊断" :rows="3" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="手术经过描述" prop="surgeryDescription">
|
||||||
|
<el-input v-model="form.surgeryDescription" type="textarea" placeholder="请输入手术经过描述" :rows="5" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="术后医嘱" prop="postoperativeAdvice">
|
||||||
|
<el-input v-model="form.postoperativeAdvice" type="textarea" placeholder="请输入术后医嘱" :rows="3" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="手术费用" prop="surgeryFee">
|
||||||
|
<el-input-number v-model="form.surgeryFee" :precision="2" :min="0" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="麻醉费用" prop="anesthesiaFee">
|
||||||
|
<el-input-number v-model="form.anesthesiaFee" :precision="2" :min="0" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="总费用" prop="totalFee">
|
||||||
|
<el-input-number v-model="form.totalFee" :precision="2" :min="0" style="width: 100%" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="切口等级" prop="incisionLevel">
|
||||||
|
<el-select v-model="form.incisionLevel" placeholder="请选择切口等级" style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in incisionLevelOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="愈合等级" prop="healingLevel">
|
||||||
|
<el-select v-model="form.healingLevel" placeholder="请选择愈合等级" style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in healingLevelOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="并发症描述" prop="complications">
|
||||||
|
<el-input v-model="form.complications" type="textarea" placeholder="请输入并发症描述" :rows="3" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注信息" :rows="2" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 查看手术详情对话框 -->
|
||||||
|
<el-dialog title="手术详情" v-model="viewOpen" width="900px" append-to-body>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="手术编号">{{ viewData.surgeryNo }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手术状态">
|
||||||
|
<el-tag :type="getStatusType(viewData.statusEnum)">{{ viewData.statusEnum_dictText }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="患者姓名">{{ viewData.patientName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="患者信息">{{ viewData.patientGender }} / {{ viewData.patientAge }}岁</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="就诊流水号">{{ viewData.encounterNo }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手术名称">{{ viewData.surgeryName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手术编码">{{ viewData.surgeryCode }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手术类型">{{ viewData.surgeryTypeEnum_dictText }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手术等级">{{ viewData.surgeryLevel_dictText }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="麻醉方式">{{ viewData.anesthesiaTypeEnum_dictText }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="计划时间">{{ viewData.plannedTime }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="实际开始时间">{{ viewData.actualStartTime }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="实际结束时间">{{ viewData.actualEndTime }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手术部位">{{ viewData.bodySite }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="主刀医生">{{ viewData.mainSurgeonName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="麻醉医生">{{ viewData.anesthetistName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="助手1">{{ viewData.assistant1Name }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="助手2">{{ viewData.assistant2Name }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手术室">{{ viewData.operatingRoomName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="执行科室">{{ viewData.orgName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="术前诊断" :span="2">{{ viewData.preoperativeDiagnosis }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="术后诊断" :span="2">{{ viewData.postoperativeDiagnosis }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手术费用" :span="2">¥{{ viewData.surgeryFee }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="麻醉费用" :span="2">¥{{ viewData.anesthesiaFee }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="总费用" :span="2">¥{{ viewData.totalFee }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="备注" :span="2">{{ viewData.remark }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="SurgeryManage">
|
||||||
|
import { getSurgeryPage, addSurgery, updateSurgery, deleteSurgery, getSurgeryDetail, updateSurgeryStatus } from '@/api/surgerymanage'
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance()
|
||||||
|
const loading = ref(true)
|
||||||
|
const showSearch = ref(true)
|
||||||
|
const surgeryList = ref([])
|
||||||
|
const queryParams = ref({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
surgeryNo: undefined,
|
||||||
|
surgeryName: undefined,
|
||||||
|
patientName: undefined,
|
||||||
|
statusEnum: undefined,
|
||||||
|
surgeryTypeEnum: undefined,
|
||||||
|
plannedTime: undefined
|
||||||
|
})
|
||||||
|
const open = ref(false)
|
||||||
|
const viewOpen = ref(false)
|
||||||
|
const form = ref({
|
||||||
|
id: undefined,
|
||||||
|
patientId: undefined,
|
||||||
|
encounterId: undefined,
|
||||||
|
encounterNo: undefined,
|
||||||
|
surgeryName: undefined,
|
||||||
|
surgeryCode: undefined,
|
||||||
|
surgeryTypeEnum: undefined,
|
||||||
|
surgeryLevel: undefined,
|
||||||
|
plannedTime: undefined,
|
||||||
|
mainSurgeonId: undefined,
|
||||||
|
anesthetistId: undefined,
|
||||||
|
assistant1Id: undefined,
|
||||||
|
assistant2Id: undefined,
|
||||||
|
scrubNurseId: undefined,
|
||||||
|
anesthesiaTypeEnum: undefined,
|
||||||
|
bodySite: undefined,
|
||||||
|
operatingRoomId: undefined,
|
||||||
|
operatingRoomName: undefined,
|
||||||
|
orgId: undefined,
|
||||||
|
preoperativeDiagnosis: undefined,
|
||||||
|
postoperativeDiagnosis: undefined,
|
||||||
|
surgeryDescription: undefined,
|
||||||
|
postoperativeAdvice: undefined,
|
||||||
|
surgeryFee: undefined,
|
||||||
|
anesthesiaFee: undefined,
|
||||||
|
totalFee: undefined,
|
||||||
|
incisionLevel: undefined,
|
||||||
|
healingLevel: undefined,
|
||||||
|
complications: undefined,
|
||||||
|
remark: undefined
|
||||||
|
})
|
||||||
|
const surgeryRef = ref()
|
||||||
|
const viewData = ref({})
|
||||||
|
const total = ref(0)
|
||||||
|
const title = ref('')
|
||||||
|
|
||||||
|
// 字典选项
|
||||||
|
const surgeryStatusOptions = ref([
|
||||||
|
{ value: 0, label: '待排期' },
|
||||||
|
{ value: 1, label: '已排期' },
|
||||||
|
{ value: 2, label: '手术中' },
|
||||||
|
{ value: 3, label: '已完成' },
|
||||||
|
{ value: 4, label: '已取消' },
|
||||||
|
{ value: 5, label: '暂停' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const surgeryTypeOptions = ref([
|
||||||
|
{ value: 1, label: '门诊手术' },
|
||||||
|
{ value: 2, label: '住院手术' },
|
||||||
|
{ value: 3, label: '急诊手术' },
|
||||||
|
{ value: 4, label: '择期手术' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const surgeryLevelOptions = ref([
|
||||||
|
{ value: 1, label: '一级手术' },
|
||||||
|
{ value: 2, label: '二级手术' },
|
||||||
|
{ value: 3, label: '三级手术' },
|
||||||
|
{ value: 4, label: '四级手术' },
|
||||||
|
{ value: 5, label: '特级手术' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const anesthesiaTypeOptions = ref([
|
||||||
|
{ value: 0, label: '无麻醉' },
|
||||||
|
{ value: 1, label: '局部麻醉' },
|
||||||
|
{ value: 2, label: '区域麻醉' },
|
||||||
|
{ value: 3, label: '全身麻醉' },
|
||||||
|
{ value: 4, label: '脊椎麻醉' },
|
||||||
|
{ value: 5, label: '硬膜外麻醉' },
|
||||||
|
{ value: 6, label: '表面麻醉' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const incisionLevelOptions = ref([
|
||||||
|
{ value: 1, label: 'I级切口' },
|
||||||
|
{ value: 2, label: 'II级切口' },
|
||||||
|
{ value: 3, label: 'III级切口' },
|
||||||
|
{ value: 4, label: 'IV级切口' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const healingLevelOptions = ref([
|
||||||
|
{ value: 1, label: '甲级愈合' },
|
||||||
|
{ value: 2, label: '乙级愈合' },
|
||||||
|
{ value: 3, label: '丙级愈合' }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 下拉列表数据
|
||||||
|
const patientList = ref([])
|
||||||
|
const doctorList = ref([])
|
||||||
|
const nurseList = ref([])
|
||||||
|
const orgList = ref([])
|
||||||
|
|
||||||
|
const rules = ref({
|
||||||
|
patientId: [{ required: true, message: '请选择患者', trigger: 'change' }],
|
||||||
|
surgeryName: [{ required: true, message: '请输入手术名称', trigger: 'blur' }],
|
||||||
|
surgeryTypeEnum: [{ required: true, message: '请选择手术类型', trigger: 'change' }],
|
||||||
|
surgeryLevel: [{ required: true, message: '请选择手术等级', trigger: 'change' }],
|
||||||
|
plannedTime: [{ required: true, message: '请选择计划手术时间', trigger: 'change' }],
|
||||||
|
mainSurgeonId: [{ required: true, message: '请选择主刀医生', trigger: 'change' }],
|
||||||
|
anesthesiaTypeEnum: [{ required: true, message: '请选择麻醉方式', trigger: 'change' }],
|
||||||
|
bodySite: [{ required: true, message: '请输入手术部位', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听费用变化自动计算总费用
|
||||||
|
watch([() => form.value.surgeryFee, () => form.value.anesthesiaFee], ([newSurgeryFee, newAnesthesiaFee]) => {
|
||||||
|
const surgeryFee = Number(newSurgeryFee) || 0
|
||||||
|
const anesthesiaFee = Number(newAnesthesiaFee) || 0
|
||||||
|
form.value.totalFee = surgeryFee + anesthesiaFee
|
||||||
|
})
|
||||||
|
|
||||||
|
getPageList()
|
||||||
|
|
||||||
|
function getList() {
|
||||||
|
loading.value = true
|
||||||
|
const params = { ...queryParams.value }
|
||||||
|
// 处理时间范围
|
||||||
|
if (params.plannedTime && params.plannedTime.length === 2) {
|
||||||
|
params.plannedTimeStart = params.plannedTime[0]
|
||||||
|
params.plannedTimeEnd = params.plannedTime[1]
|
||||||
|
delete params.plannedTime
|
||||||
|
}
|
||||||
|
getSurgeryPage(params).then((res) => {
|
||||||
|
surgeryList.value = res.data.records
|
||||||
|
total.value = res.data.total
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取手术列表失败:', error)
|
||||||
|
proxy.$modal.msgError('获取手术列表失败,请稍后重试')
|
||||||
|
surgeryList.value = []
|
||||||
|
total.value = 0
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPageList() {
|
||||||
|
queryParams.value.pageNo = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleQuery() {
|
||||||
|
queryParams.value.pageNo = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
proxy.resetForm('queryRef')
|
||||||
|
queryParams.value = {
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
surgeryNo: undefined,
|
||||||
|
surgeryName: undefined,
|
||||||
|
patientName: undefined,
|
||||||
|
statusEnum: undefined,
|
||||||
|
surgeryTypeEnum: undefined,
|
||||||
|
plannedTime: undefined
|
||||||
|
}
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
title.value = '新增手术'
|
||||||
|
open.value = true
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(row) {
|
||||||
|
title.value = '编辑手术'
|
||||||
|
open.value = true
|
||||||
|
getSurgeryDetail(row.id).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
Object.assign(form.value, res.data)
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取手术信息失败:', error)
|
||||||
|
proxy.$modal.msgError('获取手术信息失败')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleView(row) {
|
||||||
|
viewOpen.value = true
|
||||||
|
getSurgeryDetail(row.id).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
viewData.value = res.data
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取手术信息失败:', error)
|
||||||
|
proxy.$modal.msgError('获取手术信息失败')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
open.value = false
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
form.value = {
|
||||||
|
id: undefined,
|
||||||
|
patientId: undefined,
|
||||||
|
encounterId: undefined,
|
||||||
|
encounterNo: undefined,
|
||||||
|
surgeryName: undefined,
|
||||||
|
surgeryCode: undefined,
|
||||||
|
surgeryTypeEnum: undefined,
|
||||||
|
surgeryLevel: undefined,
|
||||||
|
plannedTime: undefined,
|
||||||
|
mainSurgeonId: undefined,
|
||||||
|
anesthetistId: undefined,
|
||||||
|
assistant1Id: undefined,
|
||||||
|
assistant2Id: undefined,
|
||||||
|
scrubNurseId: undefined,
|
||||||
|
anesthesiaTypeEnum: undefined,
|
||||||
|
bodySite: undefined,
|
||||||
|
operatingRoomId: undefined,
|
||||||
|
operatingRoomName: undefined,
|
||||||
|
orgId: undefined,
|
||||||
|
preoperativeDiagnosis: undefined,
|
||||||
|
postoperativeDiagnosis: undefined,
|
||||||
|
surgeryDescription: undefined,
|
||||||
|
postoperativeAdvice: undefined,
|
||||||
|
surgeryFee: undefined,
|
||||||
|
anesthesiaFee: undefined,
|
||||||
|
totalFee: undefined,
|
||||||
|
incisionLevel: undefined,
|
||||||
|
healingLevel: undefined,
|
||||||
|
complications: undefined,
|
||||||
|
remark: undefined
|
||||||
|
}
|
||||||
|
if (surgeryRef.value) {
|
||||||
|
surgeryRef.value.resetFields()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitForm() {
|
||||||
|
proxy.$refs['surgeryRef'].validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
if (form.value.id == undefined) {
|
||||||
|
addSurgery(form.value).then((res) => {
|
||||||
|
proxy.$modal.msgSuccess('新增成功')
|
||||||
|
open.value = false
|
||||||
|
getPageList()
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('新增手术失败:', error)
|
||||||
|
proxy.$modal.msgError('新增手术失败,请稍后重试')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
updateSurgery(form.value).then((res) => {
|
||||||
|
proxy.$modal.msgSuccess('修改成功')
|
||||||
|
open.value = false
|
||||||
|
getPageList()
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('更新手术失败:', error)
|
||||||
|
proxy.$modal.msgError('更新手术失败,请稍后重试')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(row) {
|
||||||
|
proxy.$modal.confirm('是否确认删除手术"' + row.surgeryName + '"?').then(() => {
|
||||||
|
return deleteSurgery(row.id)
|
||||||
|
}).then(() => {
|
||||||
|
getPageList()
|
||||||
|
proxy.$modal.msgSuccess('删除成功')
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('删除手术失败:', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStart(row) {
|
||||||
|
proxy.$modal.confirm('是否确认开始手术"' + row.surgeryName + '"?').then(() => {
|
||||||
|
return updateSurgeryStatus(row.id, 2)
|
||||||
|
}).then(() => {
|
||||||
|
getPageList()
|
||||||
|
proxy.$modal.msgSuccess('手术已开始')
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('开始手术失败:', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleComplete(row) {
|
||||||
|
proxy.$modal.confirm('是否确认完成手术"' + row.surgeryName + '"?').then(() => {
|
||||||
|
return updateSurgeryStatus(row.id, 3)
|
||||||
|
}).then(() => {
|
||||||
|
getPageList()
|
||||||
|
proxy.$modal.msgSuccess('手术已完成')
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('完成手术失败:', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectEncounter() {
|
||||||
|
// TODO: 实现选择就诊的功能
|
||||||
|
proxy.$modal.msgInfo('请选择就诊记录')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusType(status) {
|
||||||
|
const typeMap = {
|
||||||
|
0: 'info',
|
||||||
|
1: 'warning',
|
||||||
|
2: 'primary',
|
||||||
|
3: 'success',
|
||||||
|
4: 'danger',
|
||||||
|
5: 'info'
|
||||||
|
}
|
||||||
|
return typeMap[status] || 'info'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
351
手术管理模块开发说明.md
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
# 手术管理模块开发说明
|
||||||
|
|
||||||
|
## 模块概述
|
||||||
|
|
||||||
|
手术管理模块是一个完整的医疗手术管理系统,涵盖从手术排期、执行到记录的全流程管理。本模块基于经典的Spring Boot + Vue3前后端分离架构开发。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
### 1. 手术信息管理
|
||||||
|
- 手术基本信息录入(手术名称、编码、类型、等级)
|
||||||
|
- 患者信息关联
|
||||||
|
- 就诊信息关联
|
||||||
|
- 手术部位描述
|
||||||
|
|
||||||
|
### 2. 手术团队管理
|
||||||
|
- 主刀医生选择
|
||||||
|
- 麻醉医生选择
|
||||||
|
- 助手1/助手2选择
|
||||||
|
- 巡回护士选择
|
||||||
|
- 麻醉方式选择
|
||||||
|
|
||||||
|
### 3. 手术状态管理
|
||||||
|
- 待排期
|
||||||
|
- 已排期
|
||||||
|
- 手术中
|
||||||
|
- 已完成
|
||||||
|
- 已取消
|
||||||
|
- 暂停
|
||||||
|
|
||||||
|
### 4. 手术时间管理
|
||||||
|
- 计划手术时间
|
||||||
|
- 实际开始时间
|
||||||
|
- 实际结束时间
|
||||||
|
|
||||||
|
### 5. 诊断信息管理
|
||||||
|
- 术前诊断
|
||||||
|
- 术后诊断
|
||||||
|
- 手术经过描述
|
||||||
|
- 术后医嘱
|
||||||
|
- 并发症描述
|
||||||
|
|
||||||
|
### 6. 手术费用管理
|
||||||
|
- 手术费用
|
||||||
|
- 麻醉费用
|
||||||
|
- 总费用自动计算
|
||||||
|
|
||||||
|
### 7. 手术切口管理
|
||||||
|
- 切口等级(I级、II级、III级、IV级)
|
||||||
|
- 愈合等级(甲级、乙级、丙级)
|
||||||
|
|
||||||
|
## 技术架构
|
||||||
|
|
||||||
|
### 后端技术栈
|
||||||
|
- Spring Boot 2.x
|
||||||
|
- MyBatis Plus
|
||||||
|
- PostgreSQL 12+
|
||||||
|
- JDK 1.8+
|
||||||
|
|
||||||
|
### 前端技术栈
|
||||||
|
- Vue 3.x
|
||||||
|
- Element Plus
|
||||||
|
- Axios
|
||||||
|
- Vite
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
openh-is/
|
||||||
|
├── openhis-server-new/ # 后端项目
|
||||||
|
│ ├── openhis-domain/ # 领域层
|
||||||
|
│ │ └── src/main/java/com/openhis/
|
||||||
|
│ │ ├── clinical/
|
||||||
|
│ │ │ ├── domain/
|
||||||
|
│ │ │ │ └── Surgery.java # 手术实体类
|
||||||
|
│ │ │ ├── mapper/
|
||||||
|
│ │ │ │ └── SurgeryMapper.java # 手术Mapper接口
|
||||||
|
│ │ │ └── service/
|
||||||
|
│ │ │ ├── ISurgeryService.java # 手术Service接口
|
||||||
|
│ │ │ └── impl/
|
||||||
|
│ │ │ └── SurgeryServiceImpl.java # 手术Service实现
|
||||||
|
│ │ └── common/ # 公共模块
|
||||||
|
│ │ └── src/main/java/com/openhis/common/
|
||||||
|
│ │ └── enums/ # 枚举类
|
||||||
|
│ │ ├── SurgeryTypeEnum.java # 手术类型枚举
|
||||||
|
│ │ ├── SurgeryStatusEnum.java # 手术状态枚举
|
||||||
|
│ │ ├── SurgeryLevelEnum.java # 手术等级枚举
|
||||||
|
│ │ ├── AnesthesiaTypeEnum.java # 麻醉方式枚举
|
||||||
|
│ │ ├── IncisionLevelEnum.java # 切口等级枚举
|
||||||
|
│ │ └── HealingLevelEnum.java # 愈合等级枚举
|
||||||
|
│ │
|
||||||
|
│ ├── openhis-application/ # 应用层
|
||||||
|
│ │ └── src/main/java/com/openhis/web/clinicalmanage/
|
||||||
|
│ │ ├── controller/
|
||||||
|
│ │ │ └── SurgeryController.java # 手术控制器
|
||||||
|
│ │ ├── dto/
|
||||||
|
│ │ │ └── SurgeryDto.java # 手术数据传输对象
|
||||||
|
│ │ ├── appservice/
|
||||||
|
│ │ │ ├── ISurgeryAppService.java # 手术应用服务接口
|
||||||
|
│ │ │ └── impl/
|
||||||
|
│ │ │ └── SurgeryAppServiceImpl.java # 手术应用服务实现
|
||||||
|
│ │ └── mapper/
|
||||||
|
│ │ └── SurgeryAppMapper.java # 手术应用Mapper
|
||||||
|
│ │
|
||||||
|
│ └── src/main/resources/mapper/
|
||||||
|
│ ├── clinical/
|
||||||
|
│ │ └── SurgeryMapper.xml # 手术Mapper XML
|
||||||
|
│ └── clinicalmanage/
|
||||||
|
│ └── SurgeryMapper.xml # 手术应用Mapper XML
|
||||||
|
│
|
||||||
|
├── openhis-ui-vue3/ # 前端项目
|
||||||
|
│ └── src/
|
||||||
|
│ ├── api/
|
||||||
|
│ │ └── surgerymanage.js # 手术API接口
|
||||||
|
│ └── views/
|
||||||
|
│ └── surgerymanage/
|
||||||
|
│ └── index.vue # 手术管理页面
|
||||||
|
│
|
||||||
|
└── surgery_manage_init.sql # 数据库初始化脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据库设计
|
||||||
|
|
||||||
|
### 主表:cli_surgery(手术管理表)
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| id | bigint | 主键ID |
|
||||||
|
| surgery_no | varchar(50) | 手术编号(唯一) |
|
||||||
|
| patient_id | bigint | 患者ID |
|
||||||
|
| patient_name | varchar(100) | 患者姓名 |
|
||||||
|
| encounter_id | bigint | 就诊ID |
|
||||||
|
| surgery_name | varchar(200) | 手术名称 |
|
||||||
|
| surgery_code | varchar(100) | 手术编码 |
|
||||||
|
| surgery_type_enum | int2 | 手术类型 |
|
||||||
|
| surgery_level | int2 | 手术等级 |
|
||||||
|
| status_enum | int2 | 手术状态 |
|
||||||
|
| planned_time | timestamp | 计划手术时间 |
|
||||||
|
| actual_start_time | timestamp | 实际开始时间 |
|
||||||
|
| actual_end_time | timestamp | 实际结束时间 |
|
||||||
|
| main_surgeon_id | bigint | 主刀医生ID |
|
||||||
|
| main_surgeon_name | varchar(100) | 主刀医生姓名 |
|
||||||
|
| anesthetist_id | bigint | 麻醉医生ID |
|
||||||
|
| anesthetist_name | varchar(100) | 麻醉医生姓名 |
|
||||||
|
| anesthesia_type_enum | int2 | 麻醉方式 |
|
||||||
|
| body_site | varchar(200) | 手术部位 |
|
||||||
|
| preoperative_diagnosis | text | 术前诊断 |
|
||||||
|
| postoperative_diagnosis | text | 术后诊断 |
|
||||||
|
| surgery_fee | numeric(10,2) | 手术费用 |
|
||||||
|
| anesthesia_fee | numeric(10,2) | 麻醉费用 |
|
||||||
|
| total_fee | numeric(10,2) | 总费用 |
|
||||||
|
|
||||||
|
### 字典表
|
||||||
|
|
||||||
|
手术管理模块包含以下字典类型:
|
||||||
|
- surgery_status(手术状态)
|
||||||
|
- surgery_type(手术类型)
|
||||||
|
- surgery_level(手术等级)
|
||||||
|
- anesthesia_type(麻醉方式)
|
||||||
|
- incision_level(切口等级)
|
||||||
|
- healing_level(愈合等级)
|
||||||
|
|
||||||
|
## 安装部署
|
||||||
|
|
||||||
|
### 1. 数据库初始化
|
||||||
|
|
||||||
|
执行SQL脚本初始化数据库表和字典数据:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
psql -U postgres -d his_database -f surgery_manage_init.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
或者使用psql客户端执行:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
\i /path/to/surgery_manage_init.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 后端配置
|
||||||
|
|
||||||
|
1. 将后端代码复制到对应目录
|
||||||
|
2. 修改数据库连接配置(application.yml)
|
||||||
|
3. 启动Spring Boot应用
|
||||||
|
|
||||||
|
### 3. 前端配置
|
||||||
|
|
||||||
|
1. 将前端代码复制到对应目录
|
||||||
|
2. 配置API接口地址(.env.development)
|
||||||
|
3. 启动前端开发服务器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## API接口说明
|
||||||
|
|
||||||
|
### 1. 分页查询手术列表
|
||||||
|
|
||||||
|
**接口地址:** `GET /clinical-manage/surgery/surgery-page`
|
||||||
|
|
||||||
|
**请求参数:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pageNo": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
"surgeryNo": "SS20251230001",
|
||||||
|
"surgeryName": "阑尾切除术",
|
||||||
|
"patientName": "张三",
|
||||||
|
"statusEnum": 1,
|
||||||
|
"surgeryTypeEnum": 2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 查询手术详情
|
||||||
|
|
||||||
|
**接口地址:** `GET /clinical-manage/surgery/surgery-detail`
|
||||||
|
|
||||||
|
**请求参数:**
|
||||||
|
```
|
||||||
|
id: 手术ID
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 新增手术
|
||||||
|
|
||||||
|
**接口地址:** `POST /clinical-manage/surgery/surgery`
|
||||||
|
|
||||||
|
**请求参数:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"patientId": 1,
|
||||||
|
"surgeryName": "阑尾切除术",
|
||||||
|
"surgeryCode": "ICD-9-CM:47.09",
|
||||||
|
"surgeryTypeEnum": 2,
|
||||||
|
"surgeryLevel": 2,
|
||||||
|
"plannedTime": "2025-12-31 09:00:00",
|
||||||
|
"mainSurgeonId": 10,
|
||||||
|
"anesthetistId": 11,
|
||||||
|
"anesthesiaTypeEnum": 3,
|
||||||
|
"bodySite": "腹部"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 修改手术
|
||||||
|
|
||||||
|
**接口地址:** `PUT /clinical-manage/surgery/surgery`
|
||||||
|
|
||||||
|
**请求参数:** 同新增手术,需包含id
|
||||||
|
|
||||||
|
### 5. 删除手术
|
||||||
|
|
||||||
|
**接口地址:** `DELETE /clinical-manage/surgery/surgery`
|
||||||
|
|
||||||
|
**请求参数:**
|
||||||
|
```
|
||||||
|
id: 手术ID
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 更新手术状态
|
||||||
|
|
||||||
|
**接口地址:** `PUT /clinical-manage/surgery/surgery-status`
|
||||||
|
|
||||||
|
**请求参数:**
|
||||||
|
```
|
||||||
|
id: 手术ID
|
||||||
|
statusEnum: 状态值
|
||||||
|
```
|
||||||
|
|
||||||
|
## 前端页面功能
|
||||||
|
|
||||||
|
### 1. 查询功能
|
||||||
|
- 支持按手术编号、手术名称、患者姓名模糊查询
|
||||||
|
- 支持按手术状态、手术类型精确查询
|
||||||
|
- 支持按计划时间范围查询
|
||||||
|
|
||||||
|
### 2. 新增功能
|
||||||
|
- 完整的手术信息录入表单
|
||||||
|
- 患者下拉选择
|
||||||
|
- 医生/护士下拉选择
|
||||||
|
- 费用自动计算
|
||||||
|
|
||||||
|
### 3. 编辑功能
|
||||||
|
- 仅待排期和已排期状态的手术可编辑
|
||||||
|
- 手术中或已完成的手术不可编辑
|
||||||
|
|
||||||
|
### 4. 状态流转
|
||||||
|
- 已排期 → 手术中
|
||||||
|
- 手术中 → 已完成
|
||||||
|
- 待排期/已排期 → 已取消
|
||||||
|
|
||||||
|
### 5. 删除功能
|
||||||
|
- 仅待排期和已排期状态的手术可删除
|
||||||
|
- 已完成的手术不能删除
|
||||||
|
|
||||||
|
## 扩展开发建议
|
||||||
|
|
||||||
|
### 1. 手术排期管理
|
||||||
|
- 可增加手术排期日历视图
|
||||||
|
- 手术室资源冲突检测
|
||||||
|
- 手术排队优先级管理
|
||||||
|
|
||||||
|
### 2. 手术统计报表
|
||||||
|
- 手术量统计
|
||||||
|
- 手术类型分布
|
||||||
|
- 手术成功率统计
|
||||||
|
- 手术费用统计
|
||||||
|
|
||||||
|
### 3. 手术文档管理
|
||||||
|
- 手术知情同意书
|
||||||
|
- 手术安全核查表
|
||||||
|
- 手术记录单
|
||||||
|
- 麻醉记录单
|
||||||
|
|
||||||
|
### 4. 手术质控管理
|
||||||
|
- 手术质量评估
|
||||||
|
- 并发症统计
|
||||||
|
- 术后恢复跟踪
|
||||||
|
- 手术质量指标管理
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **手术编号生成**:手术编号采用自动生成机制,格式为SS + 10位数字
|
||||||
|
2. **权限控制**:需要配置相应的菜单权限和操作权限
|
||||||
|
3. **数据校验**:新增手术时必须选择患者和主刀医生
|
||||||
|
4. **状态流转**:手术状态的流转需要符合业务逻辑
|
||||||
|
5. **费用计算**:总费用自动计算,不允许手动修改
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q1: 手术编号重复怎么办?
|
||||||
|
A: 手术编号是系统自动生成的唯一编号,不会重复。如果需要自定义编号,需要修改SurgeryServiceImpl中的生成逻辑。
|
||||||
|
|
||||||
|
### Q2: 如何添加新的手术类型?
|
||||||
|
A: 在数据库sys_dict_data表中添加新的surgery_type字典数据即可。
|
||||||
|
|
||||||
|
### Q3: 手术开始后还能修改信息吗?
|
||||||
|
A: 根据业务规则,手术开始后不允许修改基本信息,但可以补充术后诊断等信息。
|
||||||
|
|
||||||
|
### Q4: 如何实现手术室资源管理?
|
||||||
|
A: 可以新增手术室管理模块,建立手术排期与手术室的关联关系,实现资源冲突检测。
|
||||||
|
|
||||||
|
## 版本历史
|
||||||
|
|
||||||
|
- v1.0.0 (2025-12-30)
|
||||||
|
- 初始版本发布
|
||||||
|
- 实现手术基本管理功能
|
||||||
|
- 实现手术状态流转
|
||||||
|
- 实现手术团队管理
|
||||||
|
|
||||||
|
## 联系方式
|
||||||
|
|
||||||
|
如有问题或建议,请联系开发团队。
|
||||||
6
迁移记录-DB变更记录/2025-12-31 add_tenant_id_to_cli_surgery.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
-- 为 cli_surgery 表添加 tenant_id 字段
|
||||||
|
-- 创建时间: 2025-12-31
|
||||||
|
-- 说明: 添加租户ID字段以支持多租户功能
|
||||||
|
|
||||||
|
ALTER TABLE cli_surgery ADD COLUMN tenant_id int4;
|
||||||
|
COMMENT ON COLUMN cli_surgery.tenant_id IS '租户ID';
|
||||||
207
迁移记录-DB变更记录/surgery_manage_init.sql
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
-- 手术管理模块数据库表结构
|
||||||
|
-- 创建时间: 2025-12-30
|
||||||
|
-- 说明: 手术管理模块数据库表结构(PostgreSQL版本)
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for cli_surgery
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS cli_surgery CASCADE;
|
||||||
|
CREATE TABLE cli_surgery (
|
||||||
|
id bigint NOT NULL,
|
||||||
|
surgery_no varchar(50) NOT NULL,
|
||||||
|
patient_id bigint NOT NULL,
|
||||||
|
patient_name varchar(100),
|
||||||
|
encounter_id bigint,
|
||||||
|
surgery_name varchar(200) NOT NULL,
|
||||||
|
surgery_code varchar(100),
|
||||||
|
surgery_type_enum int2,
|
||||||
|
surgery_level int2,
|
||||||
|
status_enum int2 DEFAULT 0,
|
||||||
|
planned_time timestamp,
|
||||||
|
actual_start_time timestamp,
|
||||||
|
actual_end_time timestamp,
|
||||||
|
main_surgeon_id bigint,
|
||||||
|
main_surgeon_name varchar(100),
|
||||||
|
assistant_1_id bigint,
|
||||||
|
assistant_1_name varchar(100),
|
||||||
|
assistant_2_id bigint,
|
||||||
|
assistant_2_name varchar(100),
|
||||||
|
anesthetist_id bigint,
|
||||||
|
anesthetist_name varchar(100),
|
||||||
|
scrub_nurse_id bigint,
|
||||||
|
scrub_nurse_name varchar(100),
|
||||||
|
anesthesia_type_enum int2,
|
||||||
|
body_site varchar(200),
|
||||||
|
incision_level int2,
|
||||||
|
healing_level int2,
|
||||||
|
operating_room_id bigint,
|
||||||
|
operating_room_name varchar(100),
|
||||||
|
org_id bigint,
|
||||||
|
org_name varchar(100),
|
||||||
|
preoperative_diagnosis text,
|
||||||
|
postoperative_diagnosis text,
|
||||||
|
surgery_description text,
|
||||||
|
postoperative_advice text,
|
||||||
|
complications text,
|
||||||
|
surgery_fee numeric(10,2) DEFAULT 0.00,
|
||||||
|
anesthesia_fee numeric(10,2) DEFAULT 0.00,
|
||||||
|
total_fee numeric(10,2) DEFAULT 0.00,
|
||||||
|
remark varchar(500),
|
||||||
|
create_by bigint,
|
||||||
|
create_time timestamp DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
update_by varchar(64),
|
||||||
|
update_time timestamp,
|
||||||
|
delete_flag varchar(1) DEFAULT '0',
|
||||||
|
CONSTRAINT pk_cli_surgery PRIMARY KEY (id),
|
||||||
|
CONSTRAINT uk_cli_surgery_no UNIQUE (surgery_no)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 创建索引
|
||||||
|
CREATE INDEX idx_cli_surgery_patient_id ON cli_surgery(patient_id);
|
||||||
|
CREATE INDEX idx_cli_surgery_encounter_id ON cli_surgery(encounter_id);
|
||||||
|
CREATE INDEX idx_cli_surgery_org_id ON cli_surgery(org_id);
|
||||||
|
CREATE INDEX idx_cli_surgery_status_enum ON cli_surgery(status_enum);
|
||||||
|
CREATE INDEX idx_cli_surgery_planned_time ON cli_surgery(planned_time);
|
||||||
|
|
||||||
|
-- 添加表注释
|
||||||
|
COMMENT ON TABLE cli_surgery IS '手术管理表';
|
||||||
|
COMMENT ON COLUMN cli_surgery.id IS 'ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.surgery_no IS '手术编号';
|
||||||
|
COMMENT ON COLUMN cli_surgery.patient_id IS '患者ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.patient_name IS '患者姓名';
|
||||||
|
COMMENT ON COLUMN cli_surgery.encounter_id IS '就诊ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.surgery_name IS '手术名称';
|
||||||
|
COMMENT ON COLUMN cli_surgery.surgery_code IS '手术编码';
|
||||||
|
COMMENT ON COLUMN cli_surgery.surgery_type_enum IS '手术类型编码 1-门诊手术 2-住院手术 3-急诊手术 4-择期手术';
|
||||||
|
COMMENT ON COLUMN cli_surgery.surgery_level IS '手术等级 1-一级 2-二级 3-三级 4-四级 5-特级';
|
||||||
|
COMMENT ON COLUMN cli_surgery.status_enum IS '手术状态 0-待排期 1-已排期 2-手术中 3-已完成 4-已取消 5-暂停';
|
||||||
|
COMMENT ON COLUMN cli_surgery.planned_time IS '计划手术时间';
|
||||||
|
COMMENT ON COLUMN cli_surgery.actual_start_time IS '实际开始时间';
|
||||||
|
COMMENT ON COLUMN cli_surgery.actual_end_time IS '实际结束时间';
|
||||||
|
COMMENT ON COLUMN cli_surgery.main_surgeon_id IS '主刀医生ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.main_surgeon_name IS '主刀医生姓名';
|
||||||
|
COMMENT ON COLUMN cli_surgery.assistant_1_id IS '助手1 ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.assistant_1_name IS '助手1 姓名';
|
||||||
|
COMMENT ON COLUMN cli_surgery.assistant_2_id IS '助手2 ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.assistant_2_name IS '助手2 姓名';
|
||||||
|
COMMENT ON COLUMN cli_surgery.anesthetist_id IS '麻醉医生ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.anesthetist_name IS '麻醉医生姓名';
|
||||||
|
COMMENT ON COLUMN cli_surgery.scrub_nurse_id IS '巡回护士ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.scrub_nurse_name IS '巡回护士姓名';
|
||||||
|
COMMENT ON COLUMN cli_surgery.anesthesia_type_enum IS '麻醉方式编码 0-无麻醉 1-局部麻醉 2-区域麻醉 3-全身麻醉 4-脊椎麻醉 5-硬膜外麻醉 6-表面麻醉';
|
||||||
|
COMMENT ON COLUMN cli_surgery.body_site IS '手术部位';
|
||||||
|
COMMENT ON COLUMN cli_surgery.incision_level IS '手术切口等级 1-I级切口 2-II级切口 3-III级切口 4-IV级切口';
|
||||||
|
COMMENT ON COLUMN cli_surgery.healing_level IS '手术切口愈合等级 1-甲级愈合 2-乙级愈合 3-丙级愈合';
|
||||||
|
COMMENT ON COLUMN cli_surgery.operating_room_id IS '手术室ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.operating_room_name IS '手术室名称';
|
||||||
|
COMMENT ON COLUMN cli_surgery.org_id IS '执行科室ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.org_name IS '执行科室名称';
|
||||||
|
COMMENT ON COLUMN cli_surgery.preoperative_diagnosis IS '术前诊断';
|
||||||
|
COMMENT ON COLUMN cli_surgery.postoperative_diagnosis IS '术后诊断';
|
||||||
|
COMMENT ON COLUMN cli_surgery.surgery_description IS '手术经过描述';
|
||||||
|
COMMENT ON COLUMN cli_surgery.postoperative_advice IS '术后医嘱';
|
||||||
|
COMMENT ON COLUMN cli_surgery.complications IS '并发症描述';
|
||||||
|
COMMENT ON COLUMN cli_surgery.surgery_fee IS '手术费用';
|
||||||
|
COMMENT ON COLUMN cli_surgery.anesthesia_fee IS '麻醉费用';
|
||||||
|
COMMENT ON COLUMN cli_surgery.total_fee IS '总费用';
|
||||||
|
COMMENT ON COLUMN cli_surgery.remark IS '备注信息';
|
||||||
|
COMMENT ON COLUMN cli_surgery.create_by IS '创建人ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.create_time IS '创建时间';
|
||||||
|
COMMENT ON COLUMN cli_surgery.update_by IS '更新人ID';
|
||||||
|
COMMENT ON COLUMN cli_surgery.update_time IS '更新时间';
|
||||||
|
COMMENT ON COLUMN cli_surgery.delete_flag IS '删除标记 0-正常 1-删除';
|
||||||
|
|
||||||
|
-- 创建自动更新时间的触发器函数
|
||||||
|
CREATE OR REPLACE FUNCTION update_modified_column()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.update_time = CURRENT_TIMESTAMP;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- 创建触发器
|
||||||
|
CREATE TRIGGER update_cli_surgery_modtime
|
||||||
|
BEFORE UPDATE ON cli_surgery
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_modified_column();
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Dictionary data for surgery manage
|
||||||
|
-- ----------------------------
|
||||||
|
-- 手术状态字典
|
||||||
|
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
('手术状态', 'surgery_status', '0', 'admin', CURRENT_TIMESTAMP, '手术状态列表')
|
||||||
|
ON CONFLICT (dict_type) DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
(1, '待排期', '0', 'surgery_status', '0', 'admin', CURRENT_TIMESTAMP, '待排期状态'),
|
||||||
|
(2, '已排期', '1', 'surgery_status', '0', 'admin', CURRENT_TIMESTAMP, '已排期状态'),
|
||||||
|
(3, '手术中', '2', 'surgery_status', '0', 'admin', CURRENT_TIMESTAMP, '手术中状态'),
|
||||||
|
(4, '已完成', '3', 'surgery_status', '0', 'admin', CURRENT_TIMESTAMP, '已完成状态'),
|
||||||
|
(5, '已取消', '4', 'surgery_status', '0', 'admin', CURRENT_TIMESTAMP, '已取消状态'),
|
||||||
|
(6, '暂停', '5', 'surgery_status', '0', 'admin', CURRENT_TIMESTAMP, '暂停状态')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- 手术类型字典
|
||||||
|
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
('手术类型', 'surgery_type', '0', 'admin', CURRENT_TIMESTAMP, '手术类型列表')
|
||||||
|
ON CONFLICT (dict_type) DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
(1, '门诊手术', '1', 'surgery_type', '0', 'admin', CURRENT_TIMESTAMP, '门诊手术'),
|
||||||
|
(2, '住院手术', '2', 'surgery_type', '0', 'admin', CURRENT_TIMESTAMP, '住院手术'),
|
||||||
|
(3, '急诊手术', '3', 'surgery_type', '0', 'admin', CURRENT_TIMESTAMP, '急诊手术'),
|
||||||
|
(4, '择期手术', '4', 'surgery_type', '0', 'admin', CURRENT_TIMESTAMP, '择期手术')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- 手术等级字典
|
||||||
|
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
('手术等级', 'surgery_level', '0', 'admin', CURRENT_TIMESTAMP, '手术等级列表')
|
||||||
|
ON CONFLICT (dict_type) DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
(1, '一级手术', '1', 'surgery_level', '0', 'admin', CURRENT_TIMESTAMP, '一级手术'),
|
||||||
|
(2, '二级手术', '2', 'surgery_level', '0', 'admin', CURRENT_TIMESTAMP, '二级手术'),
|
||||||
|
(3, '三级手术', '3', 'surgery_level', '0', 'admin', CURRENT_TIMESTAMP, '三级手术'),
|
||||||
|
(4, '四级手术', '4', 'surgery_level', '0', 'admin', CURRENT_TIMESTAMP, '四级手术'),
|
||||||
|
(5, '特级手术', '5', 'surgery_level', '0', 'admin', CURRENT_TIMESTAMP, '特级手术')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- 麻醉方式字典
|
||||||
|
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
('麻醉方式', 'anesthesia_type', '0', 'admin', CURRENT_TIMESTAMP, '麻醉方式列表')
|
||||||
|
ON CONFLICT (dict_type) DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
(1, '无麻醉', '0', 'anesthesia_type', '0', 'admin', CURRENT_TIMESTAMP, '无麻醉'),
|
||||||
|
(2, '局部麻醉', '1', 'anesthesia_type', '0', 'admin', CURRENT_TIMESTAMP, '局部麻醉'),
|
||||||
|
(3, '区域麻醉', '2', 'anesthesia_type', '0', 'admin', CURRENT_TIMESTAMP, '区域麻醉'),
|
||||||
|
(4, '全身麻醉', '3', 'anesthesia_type', '0', 'admin', CURRENT_TIMESTAMP, '全身麻醉'),
|
||||||
|
(5, '脊椎麻醉', '4', 'anesthesia_type', '0', 'admin', CURRENT_TIMESTAMP, '脊椎麻醉'),
|
||||||
|
(6, '硬膜外麻醉', '5', 'anesthesia_type', '0', 'admin', CURRENT_TIMESTAMP, '硬膜外麻醉'),
|
||||||
|
(7, '表面麻醉', '6', 'anesthesia_type', '0', 'admin', CURRENT_TIMESTAMP, '表面麻醉')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- 切口等级字典
|
||||||
|
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
('切口等级', 'incision_level', '0', 'admin', CURRENT_TIMESTAMP, '切口等级列表')
|
||||||
|
ON CONFLICT (dict_type) DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
(1, 'I级切口', '1', 'incision_level', '0', 'admin', CURRENT_TIMESTAMP, 'I级切口'),
|
||||||
|
(2, 'II级切口', '2', 'incision_level', '0', 'admin', CURRENT_TIMESTAMP, 'II级切口'),
|
||||||
|
(3, 'III级切口', '3', 'incision_level', '0', 'admin', CURRENT_TIMESTAMP, 'III级切口'),
|
||||||
|
(4, 'IV级切口', '4', 'incision_level', '0', 'admin', CURRENT_TIMESTAMP, 'IV级切口')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- 愈合等级字典
|
||||||
|
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
('愈合等级', 'healing_level', '0', 'admin', CURRENT_TIMESTAMP, '愈合等级列表')
|
||||||
|
ON CONFLICT (dict_type) DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark) VALUES
|
||||||
|
(1, '甲级愈合', '1', 'healing_level', '0', 'admin', CURRENT_TIMESTAMP, '甲级愈合'),
|
||||||
|
(2, '乙级愈合', '2', 'healing_level', '0', 'admin', CURRENT_TIMESTAMP, '乙级愈合'),
|
||||||
|
(3, '丙级愈合', '3', 'healing_level', '0', 'admin', CURRENT_TIMESTAMP, '丙级愈合')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||