Merge branch 'develop' of https://gitea.gentronhealth.com/Yajentine/his into develop

This commit is contained in:
py
2025-11-19 15:54:20 +08:00
21 changed files with 1603 additions and 262 deletions

View File

@@ -58,6 +58,7 @@ public class GenController extends BaseController {
@GetMapping("/list")
public TableDataInfo genList(GenTable genTable) {
startPage();
List<GenTable> list = genTableService.selectGenTableList(genTable);
return getDataTable(list);
}

View File

@@ -0,0 +1,53 @@
package com.openhis.web.basicmanage.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.utils.SecurityUtils;
import com.openhis.administration.domain.Invoice;
import com.openhis.administration.service.IInvoiceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* 发票管理控制器
*
* @author system
* @date 2025-02-20
*/
@RestController
@RequestMapping("/basicmanage/invoice")
public class InvoiceController {
@Autowired
private IInvoiceService invoiceService;
/**
* 分页查询发票列表(带用户角色权限过滤)
*
* @param pageNo 页码
* @param pageSize 每页条数
* @param request 请求对象
* @return 发票列表
*/
@GetMapping("/page")
public R<?> selectInvoicePage(
@RequestParam(defaultValue = "1") Integer pageNo,
@RequestParam(defaultValue = "10") Integer pageSize,
HttpServletRequest request) {
// 获取当前用户ID
Long userId = SecurityUtils.getUserId();
// 判断当前用户是否为管理员
boolean isAdmin = SecurityUtils.isAdmin(userId);
// 分页查询发票列表
Page<Invoice> page = new Page<>(pageNo, pageSize);
return R.ok(invoiceService.selectInvoicePage(page, isAdmin, userId));
}
}

View File

@@ -0,0 +1,88 @@
package com.openhis.web.basicmanage.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.utils.SecurityUtils;
import com.openhis.administration.domain.InvoiceSegment;
import com.openhis.administration.service.IInvoiceSegmentService;
import com.openhis.web.basicmanage.domain.InvoiceSegmentDeleteRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
/**
* 发票段管理控制器
*
* @author system
* @date 2025-11-18
*/
@RestController
@RequestMapping("/basicmanage/invoice-segment")
public class InvoiceSegmentController {
@Autowired
private IInvoiceSegmentService invoiceSegmentService;
/**
* 分页查询发票段列表(带用户角色权限过滤)
*
* @param pageNo 页码
* @param pageSize 每页条数
* @param request 请求对象
* @return 发票段列表
*/
@GetMapping("/page")
public R<?> selectInvoiceSegmentPage(
@RequestParam(defaultValue = "1") Integer pageNo,
@RequestParam(defaultValue = "100") Integer pageSize,
HttpServletRequest request) {
// 获取当前用户ID
Long userId = SecurityUtils.getUserId();
// 判断当前用户是否为管理员
boolean isAdmin = SecurityUtils.isAdmin(userId);
// 分页查询发票段列表
Page<InvoiceSegment> page = new Page<>(pageNo, pageSize);
return R.ok(invoiceSegmentService.selectInvoiceSegmentPage(page, isAdmin, userId));
}
/**
* 新增发票段
*
* @param invoiceSegment 发票段信息
* @return 操作结果
*/
@PostMapping("/add")
public R<?> addInvoiceSegment(@RequestBody InvoiceSegment invoiceSegment) {
// 设置创建人信息
invoiceSegment.setCreateBy(SecurityUtils.getUsername());
int result = invoiceSegmentService.insertInvoiceSegment(invoiceSegment);
return result > 0 ? R.ok() : R.fail();
}
/**
* 修改发票段
*
* @param invoiceSegment 发票段信息
* @return 操作结果
*/
@PostMapping("/update")
public R<?> updateInvoiceSegment(@RequestBody InvoiceSegment invoiceSegment) {
// 设置更新人信息
invoiceSegment.setUpdateBy(SecurityUtils.getUsername());
int result = invoiceSegmentService.updateInvoiceSegment(invoiceSegment);
return result > 0 ? R.ok() : R.fail();
}
/**
* 删除发票段
*/
@PostMapping("/delete")
public R<?> delete(@RequestBody InvoiceSegmentDeleteRequest request) {
int rows = invoiceSegmentService.deleteInvoiceSegmentByIds(request.getIds());
return rows > 0 ? R.ok("删除成功") : R.fail("删除失败");
}
}

View File

@@ -0,0 +1,23 @@
package com.openhis.web.basicmanage.domain;
import java.io.Serializable;
/**
* 发票段删除请求类
*
* @author system
* @date 2024-06-19
*/
public class InvoiceSegmentDeleteRequest implements Serializable {
private static final long serialVersionUID = 1L;
private Long[] ids;
public Long[] getIds() {
return ids;
}
public void setIds(Long[] ids) {
this.ids = ids;
}
}

View File

@@ -5,43 +5,32 @@ package com.openhis.web.ybmanage.vo;
import java.math.BigDecimal;
import javax.validation.constraints.NotNull;
import lombok.Data;
/**
* 【3201】后台计算结果 DB映射实体
* Settlement3202 Result DB mapping entity
*
* @author SunJQ
* @date 2025-04-15
*/
@Data
public class Settlement3202VO {
/** 医疗费用总额 */
/** Medical Fee Sum */
private BigDecimal medFeeSumAmt;
/** 基金支付总额 */
/** Fund Pay Sum */
private BigDecimal fundPaySumAmt;
/** 个人账户支付总额 */
/** Account Pay */
private BigDecimal acctPay;
/** 个人账户支付总额 */
/** Account Gj Pay */
private BigDecimal acctGjPay;
/** 现金支付总额 */
/** Self Pay Cash */
private BigDecimal selfPayCash;
/** 微信支付总额 */
/** Self Pay WeChat */
private BigDecimal selfPayVx;
/** 阿里支付总额 */
/** Self Pay Alipay */
private BigDecimal selfPayAli;
/** 银行卡支付总额 */
/** Self Pay Bank Card */
private BigDecimal selfPayUnion;
/** 定点医药机构结算笔数 */
/** Settlement Count */
private Integer fixMedInsSetlCnt;
}

View File

@@ -32,6 +32,11 @@
<groupId>com.openhis</groupId>
<artifactId>openhis-common</artifactId>
</dependency>
<!-- 核心共通模块 -->
<dependency>
<groupId>com.core</groupId>
<artifactId>core-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -0,0 +1,59 @@
package com.openhis.administration.domain;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 发票段管理Entity实体
*
* @author system
* @date 2025-11-18
*/
@Data
@TableName("adm_invoice_segment")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class InvoiceSegment extends HisBaseEntity {
/** ID */
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/** 段ID */
private Long segmentId;
/** 开始号码 */
private String beginNumber;
/** 结束号码 */
private String endNumber;
/** 员工ID */
private Long employeeId;
/** 员工姓名 */
private String employeeName;
/** 开票员ID */
private Long invoicingStaffId;
/** 开票员姓名 */
private String invoicingStaffName;
/** 创建日期 */
private Date createDate;
/** 状态 */
private String status;
/** 备注 */
private String remark;
}

View File

@@ -0,0 +1,17 @@
package com.openhis.administration.mapper;
import org.springframework.stereotype.Repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.openhis.administration.domain.InvoiceSegment;
/**
* 发票段管理Mapper接口
*
* @author system
* @date 2025-11-18
*/
@Repository
public interface InvoiceSegmentMapper extends BaseMapper<InvoiceSegment> {
}

View File

@@ -0,0 +1,47 @@
package com.openhis.administration.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.openhis.administration.domain.InvoiceSegment;
/**
* 发票段管理Service接口
*
* @author system
* @date 2025-11-18
*/
public interface IInvoiceSegmentService {
/**
* 分页查询发票段列表
*
* @param page 分页对象
* @param isAdmin 是否管理员
* @param userId 用户ID
* @return 分页结果
*/
Page<InvoiceSegment> selectInvoiceSegmentPage(Page<InvoiceSegment> page, boolean isAdmin, Long userId);
/**
* 新增发票段
*
* @param invoiceSegment 发票段信息
* @return 结果
*/
int insertInvoiceSegment(InvoiceSegment invoiceSegment);
/**
* 修改发票段
*
* @param invoiceSegment 发票段信息
* @return 结果
*/
int updateInvoiceSegment(InvoiceSegment invoiceSegment);
/**
* 删除发票段
*
* @param ids 发票段ID列表
* @return 结果
*/
int deleteInvoiceSegmentByIds(Long[] ids);
}

View File

@@ -1,5 +1,7 @@
package com.openhis.administration.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.openhis.administration.domain.Invoice;
import com.openhis.administration.domain.Supplier;
@@ -18,4 +20,14 @@ public interface IInvoiceService extends IService<Invoice> {
* @return
*/
Long addInvoice(Invoice invoice);
/**
* 分页查询发票列表(带用户角色权限过滤)
*
* @param page 分页参数
* @param isAdmin 是否管理员
* @param userId 当前用户ID
* @return 发票列表
*/
IPage<Invoice> selectInvoicePage(Page<Invoice> page, boolean isAdmin, Long userId);
}

View File

@@ -0,0 +1,196 @@
package com.openhis.administration.service.impl;
import java.util.List;
import java.util.Objects;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.openhis.administration.domain.InvoiceSegment;
import com.openhis.administration.mapper.InvoiceSegmentMapper;
import com.openhis.administration.service.IInvoiceSegmentService;
/**
* 发票段管理Service实现
*
* @author system
* @date 2025-11-18
*/
@Service
public class InvoiceSegmentServiceImpl implements IInvoiceSegmentService {
@Autowired
private InvoiceSegmentMapper invoiceSegmentMapper;
/**
* 分页查询发票段列表
*/
@Override
public Page<InvoiceSegment> selectInvoiceSegmentPage(Page<InvoiceSegment> page, boolean isAdmin, Long userId) {
LambdaQueryWrapper<InvoiceSegment> queryWrapper = new LambdaQueryWrapper<>();
// 移除数据过滤限制,允许查看所有发票段数据
// 按创建时间倒序排列
queryWrapper.orderByDesc(InvoiceSegment::getCreateDate);
return invoiceSegmentMapper.selectPage(page, queryWrapper);
}
/**
* 新增发票段
*/
@Override
public int insertInvoiceSegment(InvoiceSegment invoiceSegment) {
return invoiceSegmentMapper.insert(invoiceSegment);
}
/**
* 修改发票段
*/
@Override
public int updateInvoiceSegment(InvoiceSegment invoiceSegment) {
System.out.println("===== 开始更新发票段 ====");
System.out.println("传入的invoiceSegment对象: id=" + invoiceSegment.getId() + ", segmentId=" + invoiceSegment.getSegmentId());
// 确保必填字段存在
if (invoiceSegment.getBeginNumber() == null || invoiceSegment.getEndNumber() == null) {
System.out.println("错误: beginNumber或endNumber为空无法更新");
return 0;
}
// 先尝试直接通过id更新
int rows = invoiceSegmentMapper.updateById(invoiceSegment);
System.out.println("直接通过id更新结果: 影响行数=" + rows);
// 如果直接更新失败,尝试多种查询策略
if (rows == 0) {
// 策略1: 使用传入的id作为segmentId查询处理前端传入的keyId作为segment_id的情况
if (invoiceSegment.getId() != null) {
System.out.println("策略1: 尝试将id=" + invoiceSegment.getId() + "作为segment_id查询");
LambdaQueryWrapper<InvoiceSegment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(InvoiceSegment::getSegmentId, invoiceSegment.getId());
queryWrapper.eq(InvoiceSegment::getDeleteFlag, "0");
InvoiceSegment existingSegment = invoiceSegmentMapper.selectOne(queryWrapper);
if (existingSegment != null) {
System.out.println("策略1成功: 找到匹配的记录原始id=" + existingSegment.getId() + ", segmentId=" + existingSegment.getSegmentId());
invoiceSegment.setId(existingSegment.getId());
invoiceSegment.setSegmentId(existingSegment.getSegmentId()); // 确保segmentId正确
rows = invoiceSegmentMapper.updateById(invoiceSegment);
System.out.println("更新结果: 影响行数=" + rows);
}
}
// 策略2: 使用传入的segmentId字段查询
if (rows == 0 && invoiceSegment.getSegmentId() != null) {
System.out.println("策略2: 尝试使用segmentId=" + invoiceSegment.getSegmentId() + "查询");
LambdaQueryWrapper<InvoiceSegment> segmentIdWrapper = new LambdaQueryWrapper<>();
segmentIdWrapper.eq(InvoiceSegment::getSegmentId, invoiceSegment.getSegmentId());
segmentIdWrapper.eq(InvoiceSegment::getDeleteFlag, "0");
InvoiceSegment existingSegment = invoiceSegmentMapper.selectOne(segmentIdWrapper);
if (existingSegment != null) {
System.out.println("策略2成功: 找到匹配的记录原始id=" + existingSegment.getId() + ", segmentId=" + existingSegment.getSegmentId());
invoiceSegment.setId(existingSegment.getId());
rows = invoiceSegmentMapper.updateById(invoiceSegment);
System.out.println("更新结果: 影响行数=" + rows);
}
}
// 策略3: 基于业务键查询beginNumber和endNumber通常是唯一的业务组合
if (rows == 0) {
System.out.println("策略3: 尝试根据业务键查询beginNumber=" + invoiceSegment.getBeginNumber() + ", endNumber=" + invoiceSegment.getEndNumber());
LambdaQueryWrapper<InvoiceSegment> businessWrapper = new LambdaQueryWrapper<>();
businessWrapper.eq(InvoiceSegment::getBeginNumber, invoiceSegment.getBeginNumber());
businessWrapper.eq(InvoiceSegment::getEndNumber, invoiceSegment.getEndNumber());
businessWrapper.eq(InvoiceSegment::getDeleteFlag, "0");
InvoiceSegment existingSegment = invoiceSegmentMapper.selectOne(businessWrapper);
if (existingSegment != null) {
System.out.println("策略3成功: 找到匹配的记录原始id=" + existingSegment.getId() + ", segmentId=" + existingSegment.getSegmentId());
invoiceSegment.setId(existingSegment.getId());
invoiceSegment.setSegmentId(existingSegment.getSegmentId()); // 确保segmentId正确
rows = invoiceSegmentMapper.updateById(invoiceSegment);
System.out.println("更新结果: 影响行数=" + rows);
}
}
// 策略4: 如果是特定场景下的已知ID问题添加特殊处理逻辑
if (rows == 0) {
System.out.println("策略4: 检查是否需要特殊处理");
// 检查是否是之前日志中提到的ID问题
if ("1990329963367977000".equals(invoiceSegment.getSegmentId() + "")) {
System.out.println("检测到特殊ID模式尝试替代查询");
// 这里可以添加更具体的替代查询逻辑
}
}
// 增强调试信息:显示当前表中所有可能相关的记录
if (rows == 0) {
System.out.println("所有查询策略都失败了,显示表中相关记录:");
// 查询条件可以根据实际情况调整
LambdaQueryWrapper<InvoiceSegment> debugWrapper = new LambdaQueryWrapper<>();
debugWrapper.eq(InvoiceSegment::getDeleteFlag, "0");
// 可以添加时间范围或其他条件来限制结果数量
debugWrapper.last("LIMIT 10");
List<InvoiceSegment> segments = invoiceSegmentMapper.selectList(debugWrapper);
for (InvoiceSegment seg : segments) {
System.out.println("记录: id=" + seg.getId() + ", segmentId=" + seg.getSegmentId() + ", beginNumber=" + seg.getBeginNumber() + ", endNumber=" + seg.getEndNumber());
}
System.out.println("提示: 请检查前端传递的ID是否与数据库中的记录匹配");
}
}
System.out.println("===== 更新发票段结束 ==== 最终结果: " + (rows > 0 ? "成功" : "失败"));
return rows;
}
/**
* 删除发票段
*/
@Override
public int deleteInvoiceSegmentByIds(Long[] ids) {
System.out.println("删除发票段IDs: " + java.util.Arrays.toString(ids));
List<Long> idList = java.util.Arrays.asList(ids);
System.out.println("删除ID列表: " + idList);
// 检查记录是否存在且未被删除 - 先尝试通过id查找
LambdaQueryWrapper<InvoiceSegment> checkWrapper = new LambdaQueryWrapper<>();
checkWrapper.in(InvoiceSegment::getId, idList);
checkWrapper.eq(InvoiceSegment::getDeleteFlag, "0"); // 检查未被删除的记录
List<InvoiceSegment> existingRecords = invoiceSegmentMapper.selectList(checkWrapper);
System.out.println("通过id检查到的未删除记录数: " + existingRecords.size());
// 如果通过id没有找到记录尝试通过segment_id查找
if (existingRecords.isEmpty()) {
System.out.println("通过id未找到记录尝试通过segment_id查找");
LambdaQueryWrapper<InvoiceSegment> segmentIdWrapper = new LambdaQueryWrapper<>();
segmentIdWrapper.in(InvoiceSegment::getSegmentId, idList);
segmentIdWrapper.eq(InvoiceSegment::getDeleteFlag, "0");
existingRecords = invoiceSegmentMapper.selectList(segmentIdWrapper);
System.out.println("通过segment_id检查到的未删除记录数: " + existingRecords.size());
// 如果通过segment_id找到了记录使用这些记录的id进行删除
if (!existingRecords.isEmpty()) {
List<Long> actualIds = existingRecords.stream()
.map(InvoiceSegment::getId)
.collect(java.util.stream.Collectors.toList());
System.out.println("使用实际id列表进行删除: " + actualIds);
int rows = invoiceSegmentMapper.deleteBatchIds(actualIds);
System.out.println("删除影响行数: " + rows);
return rows;
}
}
// 直接通过id删除
int rows = invoiceSegmentMapper.deleteBatchIds(idList);
System.out.println("删除影响行数: " + rows);
return rows;
}
}

View File

@@ -2,17 +2,18 @@ package com.openhis.administration.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.core.common.utils.SecurityUtils;
import com.openhis.administration.domain.Invoice;
import com.openhis.administration.domain.Supplier;
import com.openhis.administration.mapper.InvoiceMapper;
import com.openhis.administration.service.IInvoiceService;
import com.openhis.common.enums.SupplyStatus;
import com.openhis.workflow.domain.SupplyRequest;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.openhis.administration.domain.Invoice;
import com.openhis.administration.mapper.InvoiceMapper;
import com.openhis.administration.service.IInvoiceService;
import java.util.Date;
import java.util.List;
@@ -26,11 +27,12 @@ import java.util.List;
public class InvoiceServiceImpl extends ServiceImpl<InvoiceMapper, Invoice> implements IInvoiceService {
/**
* 新增发票
*
*
* @param invoice 发票实体
* @return
*/
public Long addInvoice(Invoice invoice){
@Override
public Long addInvoice(Invoice invoice){
// 根据编码判断发票是否存在
List<Invoice> invoices =
baseMapper.selectList(new LambdaQueryWrapper<Invoice>().eq(Invoice::getBusNo, invoice.getBusNo()));
@@ -46,4 +48,26 @@ public class InvoiceServiceImpl extends ServiceImpl<InvoiceMapper, Invoice> impl
return invoice.getId();
}
/**
* 分页查询发票列表(带用户角色权限过滤)
*
* @param page 分页参数
* @param isAdmin 是否管理员
* @param userId 当前用户ID
* @return 发票列表
*/
@Override
public IPage<Invoice> selectInvoicePage(Page<Invoice> page, boolean isAdmin, Long userId) {
LambdaQueryWrapper<Invoice> queryWrapper = new LambdaQueryWrapper<>();
// 如果不是管理员,只查询当前用户创建的发票
if (!isAdmin) {
queryWrapper.eq(Invoice::getInvoicingStaffId, userId);
}
// 按创建时间降序排序
queryWrapper.orderByDesc(Invoice::getCreateTime);
return baseMapper.selectPage(page, queryWrapper);
}
}

View File

@@ -32,16 +32,7 @@
</ul>
</div>
<!-- 未设置票据号码员工提示 -->
<div v-if="showNoInvoiceWarning && employeesWithoutInvoice.length > 0" class="alert alert-warning warning-messages">
<h4 class="warning-title">未设置票据号码提醒</h4>
<p>以下员工尚未设置票据号码</p>
<ul class="warning-list">
<li v-for="(employee, index) in employeesWithoutInvoice" :key="index" class="warning-item">
{{ employee.name }} (员工工号: {{ employee.employeeId }})
</li>
</ul>
</div>
<!-- 数据表格 -->
<div class="table-container">
@@ -61,19 +52,28 @@
<tbody>
<tr
v-for="(item, index) in filteredData"
:key="item.id"
:class="{ 'editing-row': item.isActive }"
:key="item.keyId"
:class="{'editing-row': item.isActive }"
>
<td class="sequence-number">{{ index + 1 }}</td>
<td class="employee-info">
<div class="input-container">
<input
<select
v-if="item.isActive"
v-model="item.operator"
class="form-control"
placeholder="请输入操作员名称"
maxlength="20"
/>
class="form-control"
>
<option value="">请选择操作员</option>
<option
v-for="user in userList"
:key="user.employeeId"
:value="user.name"
:data-employee-id="user.employeeId"
@change="updateEmployeeId(item, user.employeeId)"
>
{{ user.name }}
</option>
</select>
<span v-else class="employee-name">{{ item.operator || '-' }}</span>
</div>
</td>
@@ -140,7 +140,7 @@
<td class="action-buttons">
<button
v-if="!item.isActive"
@click="toggleEdit(item.id)"
@click="toggleEdit(item.keyId)"
class="btn btn-primary btn-sm mr-1"
title="编辑"
>
@@ -148,14 +148,14 @@
</button>
<button
v-if="item.isActive"
@click="toggleEdit(item.id)"
@click="toggleEdit(item.keyId)"
class="btn btn-secondary btn-sm mr-1"
title="取消"
>
<i class="icon-cancel"></i> 取消
</button>
<button
@click="deleteRow(item.id)"
@click="deleteRow(item)"
class="btn btn-danger btn-sm"
title="删除"
>
@@ -189,71 +189,49 @@
</template>
<script>
import { listUser } from '@/api/system/user';
import request from '@/utils/request'; // 导入请求工具
export default {
name: 'InvoiceManagement',
data() {
return {
// 用户信息和权限
currentUser: {
name: '骆丹颖',
name: 'admin',
employeeId: '1702',
role: 'admin' // operator: 普通操作员, admin: 管理员
},
// 用户列表用于操作员下拉选择将在created中从后端获取
userList: [],
saveButtonText: '保存',
// 未设置票据号码的员工信息
employeesWithoutInvoice: [],
// 显示未设置票据号码提示
showNoInvoiceWarning: false,
// 发票数据模型,增加更多必要字段
invoiceData: [
{
id: 1,
operator: '骆丹颖',
employeeId: '1702',
date: '2024-01-15',
startNum: 'A20240001',
endNum: 'A20240050',
status: '已使用',
currentNum: 'A20240050', // 当前使用到的号码
isActive: false // 是否处于编辑状态
},
{
id: 2,
operator: '张明',
employeeId: '1801',
date: '2024-01-16',
startNum: 'A20240051',
endNum: 'A20240100',
status: '已使用',
currentNum: 'A20240100',
isActive: false
},
{
id: 3,
operator: '李华',
employeeId: '1903',
date: '2024-01-17',
startNum: 'A20240101',
endNum: 'A20240150',
status: '使用中',
currentNum: 'A20240125',
isActive: false
}
],
invoiceData: [], // 初始为空数组,无默认数据
// 表单验证相关状态
validationErrors: [],
validationErrors: [],
// 过滤后显示的数据
filteredData: []
}
},
computed: {
// 计算属性:判断是否为管理员
isAdmin() {
return this.currentUser.role === 'admin';
}
},
created() {
// 初始化时根据用户权限过滤数据
console.log('组件初始化,开始加载数据');
// 从后端获取用户列表后再加载发票数据,确保用户信息可用
this.getUserList().then(() => {
console.log('用户列表加载完成,开始加载发票数据');
this.loadInvoiceData();
}).catch(() => {
console.warn('用户列表加载失败,仍尝试加载发票数据');
this.loadInvoiceData();
});
// 初始化过滤数据,确保页面首次渲染时有数据显示
this.filterDataByPermission();
},
@@ -267,17 +245,183 @@ export default {
}
},
methods: {
// 获取用户列表
getUserList() {
return listUser().then((res) => {
// 从响应中提取用户列表,并转换为需要的格式
this.userList = res.data.records.map(user => ({
name: user.nickName, // 使用用户昵称作为显示名称
employeeId: user.userId, // 使用用户ID作为员工工号
role: user.practitionerRolesDtoList?.some(role => role.roleKey === 'admin') ? 'admin' : 'operator'
}));
console.log('获取到的用户列表:', this.userList);
return Promise.resolve();
}).catch(error => {
console.error('获取用户列表失败:', error);
return Promise.reject(error);
});
},
// 从后端加载发票数据
loadInvoiceData() {
// 使用request工具从后端API获取发票段数据
request({
url: '/basicmanage/invoice-segment/page', // 更新为发票段的API路径
method: 'get',
params: {
pageNo: 1,
pageSize: 10000 // 进一步增大分页大小,确保能获取数据库中的所有记录
}
}).then(res => {
console.log('获取到的发票段数据响应:', res);
// 添加更多调试信息
console.log('响应总记录数:', res.data.total || '未知');
console.log('实际获取记录数:', res.data.records ? res.data.records.length : 0);
// 检查响应数据格式
if (!res || !res.data) {
console.error('响应数据格式不正确:', res);
if (this.$message) {
this.$message.error('获取发票数据失败:响应格式不正确');
} else {
alert('获取发票数据失败:响应格式不正确');
}
return;
}
// 处理响应数据将后端adm_invoice_segment数据映射到前端所需的数据结构
if (res.data.records && res.data.records.length > 0) {
console.log(`成功获取到 ${res.data.records.length} 条发票段记录`);
this.invoiceData = res.data.records.map((record, index) => {
const mappedRecord = {
id: record.id, // 保留原始id
segmentId: record.segmentId, // 保留原始segmentId
// 为前端操作使用的标识符
keyId: record.id || record.segmentId, // 用于前端组件的key
// 更健壮的操作员名称获取逻辑
// 更健壮的操作员名称获取逻辑,确保类型转换一致
operator: (record.invoicingStaffName || record.employeeName) ?
record.invoicingStaffName || record.employeeName :
((record.invoicingStaffId || record.employeeId) ?
this.getUserNameById(record.invoicingStaffId || record.employeeId) : '-'),
employeeId: record.invoicingStaffId || record.employeeId || '',
date: record.billBusDate || record.createDate ?
new Date(record.billBusDate || record.createDate).toISOString().split('T')[0] : '',
startNum: record.startNum || record.beginNumber || '',
endNum: record.endNum || record.endNumber || '',
// 从remark字段中提取currentNum值支持多种格式
currentNum: record.currentNum || record.currentNumber ||
(record.remark && record.remark.includes('当前使用号码:') ?
record.remark.split('当前使用号码:')[1].split(';')[0].trim() : ''),
status: record.statusEnum ? record.statusEnum.value : record.status || '未使用',
remark: record.remark || '', // 保留原始remark字段
isActive: false // 确保初始状态不是编辑状态
};
console.log(`记录 ${index + 1} 映射后的数据:`, mappedRecord);
return mappedRecord;
});
console.log('最终映射后的发票数据:', this.invoiceData);
// 确保数据加载后立即过滤
this.filterDataByPermission();
} else {
console.log('未获取到发票段记录或记录为空');
this.invoiceData = [];
this.filterDataByPermission(); // 即使没有数据也调用过滤函数
}
}).catch(error => {
console.error('获取发票数据失败:', error);
console.error('错误详情:', error.response || error);
// 显示更详细的错误信息
let errorMessage = '加载发票数据失败';
if (error.response) {
errorMessage += `: ${error.response.status} ${error.response.statusText}`;
} else if (error.message) {
errorMessage += `: ${error.message}`;
}
if (this.$message) {
this.$message.error(errorMessage);
} else {
alert(errorMessage);
}
});
},
// 更新发票数据到后端
updateInvoiceData(record) {
// 准备发送到后端的数据格式适配adm_invoice_segment表结构
const dataToSend = {
id: record.id, // 保持一致的ID字段
segmentId: record.segmentId || record.id, // 优先使用record.segmentId确保segment_id不为空
invoicingStaffId: record.employeeId,
employeeId: record.employeeId, // 同时提供employeeId字段
billBusDate: record.date ? new Date(record.date) : null,
createDate: record.date ? new Date(record.date) : null,
startNum: record.startNum,
beginNumber: record.startNum, // 同时提供beginNumber字段
endNum: record.endNum,
endNumber: record.endNum, // 同时提供endNumber字段
currentNum: record.currentNum,
currentNumber: record.currentNum, // 同时提供currentNumber字段
statusEnum: { value: record.status },
status: record.status, // 同时提供status字段
tenantId: 0, // 添加tenantId字段
deleteFlag: '0' // 添加deleteFlag字段
};
// 确保remark字段包含当前使用号码信息
if (record.currentNum) {
dataToSend.remark = `当前使用号码:${record.currentNum}`;
}
console.log('准备更新发票段数据:', {
url: '/basicmanage/invoice-segment/update',
data: dataToSend
});
// 调用后端API保存数据
request({
url: '/basicmanage/invoice-segment/update',
method: 'post',
data: dataToSend
}).then(response => {
console.log('发票段数据更新成功:', response);
return response;
}).catch(error => {
console.error('发票段数据更新失败:', error);
console.error('错误详情:', error.response || error);
throw error;
}).then(res => {
console.log('更新发票数据成功:', res);
}).catch(error => {
console.error('更新发票数据失败:', error);
if (this.$message) {
this.$message.error('更新数据失败,请稍后重试');
}
});
},
// 根据用户权限过滤数据
filterDataByPermission() {
console.log('开始过滤数据,当前用户角色:', this.currentUser.role);
console.log('过滤前数据总量:', this.invoiceData.length);
if (this.isAdmin) {
// 管理员可以看到所有数据
console.log('管理员模式,显示所有数据');
this.filteredData = [...this.invoiceData];
} else {
// 普通操作员只能看到自己的数据
// 普通操作员只能看到自己的数据,确保类型一致
console.log('操作员模式,过滤条件:', this.currentUser.employeeId);
const currentEmployeeId = String(this.currentUser.employeeId);
this.filteredData = this.invoiceData.filter(item =>
item.employeeId === this.currentUser.employeeId
String(item.employeeId) === currentEmployeeId
);
}
console.log('过滤后显示的数据量:', this.filteredData.length);
},
// 检查是否有权限修改指定记录
@@ -295,9 +439,42 @@ export default {
}
},
// 更新员工ID
updateEmployeeId(item, employeeId) {
item.employeeId = employeeId;
},
// 根据员工ID获取用户名称
getUserNameById(employeeId) {
if (!employeeId) return '';
const user = this.userList.find(u => String(u.employeeId) === String(employeeId));
return user ? user.name : '';
},
// 检查是否有权限修改指定记录
canModifyRecord(record) {
// 管理员可以修改所有记录,普通用户只能修改自己的
return this.isAdmin || record.employeeId === this.currentUser.employeeId;
},
// 检查权限并执行操作
checkPermissionAndExecute(record, operation) {
if (this.canModifyRecord(record)) {
operation();
} else {
alert('您没有权限修改此记录!');
}
},
// 更新员工ID
updateEmployeeId(item, employeeId) {
item.employeeId = employeeId;
},
addNewRow() {
// 新增行时自动填充当前用户信息
const newId = Math.max(...this.invoiceData.map(item => item.id), 0) + 1;
// 使用负数作为临时ID避免与后端数据库ID冲突
const newId = -Math.max(...this.invoiceData.map(item => Math.abs(item.id)), 0) - 1;
this.invoiceData.push({
id: newId,
operator: this.currentUser.name, // 自动使用当前用户名称
@@ -307,12 +484,12 @@ export default {
endNum: '',
currentNum: '',
status: '未使用',
isActive: true // 新增行默认处于编辑状态
isActive: true, // 新增行默认处于编辑状态
isNewRecord: true // 添加标记表示这是新记录
});
},
deleteRow(id) {
const record = this.invoiceData.find(item => item.id === id);
deleteRow(record) {
if (!record) {
alert('未找到要删除的记录');
return;
@@ -333,28 +510,204 @@ export default {
}
if (confirm('确定要删除这条记录吗?删除后无法恢复。')) {
const index = this.invoiceData.findIndex(item => item.id === id);
if (index > -1) {
this.invoiceData.splice(index, 1);
// 删除后刷新过滤数据
this.filterDataByPermission();
}
// 添加详细的请求数据日志
console.log('删除记录详情:', record);
// 构建删除数据优先使用id如果没有则使用segmentId
const idsToDelete = [];
if (record.id) idsToDelete.push(record.id);
if (record.segmentId && record.segmentId !== record.id) idsToDelete.push(record.segmentId);
// 如果没有有效的ID尝试使用前端的keyId
const deleteData = { ids: idsToDelete.length > 0 ? idsToDelete : [record.keyId] };
console.log('准备发送删除请求:', { url: '/basicmanage/invoice-segment/delete', method: 'post', data: deleteData });
// 更新日志以反映新的ID处理方式
console.log('删除数据:', deleteData);
console.log('删除ID类型检查:', deleteData.ids.map(id => ({value: id, type: typeof id})));
// 调用删除API
console.log('开始删除操作使用的ID:', deleteData.ids);
console.log('请求数据:', deleteData);
request({
url: '/basicmanage/invoice-segment/delete',
method: 'post',
data: deleteData
}).then(res => {
console.log('删除请求响应:', res);
console.log('响应类型:', typeof res);
console.log('响应结构:', JSON.stringify(res, null, 2));
// 处理响应 - 支持多种响应格式
let isSuccess = false;
let message = '';
// 检查是否为成功响应
if (res.code === 200 || res.data?.code === 200) {
isSuccess = true;
message = res.msg || res.data?.msg || '删除成功';
}
// 检查是否有特定的成功标志
else if (res.data?.success || res.success) {
isSuccess = true;
message = res.msg || res.data?.msg || '删除成功';
}
// 检查是否直接包含成功消息
else if (res.msg?.includes('成功') || res.data?.msg?.includes('成功')) {
isSuccess = true;
message = res.msg || res.data?.msg || '删除成功';
}
// 其他情况视为失败
else {
message = res.msg || res.data?.msg || '删除失败';
console.error('响应中未包含成功标志:', { code: res.code || res.data?.code, msg: message });
}
// 检查是否在响应中包含影响行数信息
let affectedRows = null;
if (res.data?.affectedRows !== undefined) {
affectedRows = res.data.affectedRows;
} else if (res.affectedRows !== undefined) {
affectedRows = res.affectedRows;
}
console.log('删除操作影响行数:', affectedRows);
// 根据结果处理
if (isSuccess) {
// 特别处理影响行数为0的情况
if (affectedRows === 0) {
console.warn('删除操作成功但未影响任何记录,可能记录不存在或已被删除');
// 从前端数据中移除该记录(即使后端没有实际删除)
const index = this.invoiceData.findIndex(item => item.keyId === record.keyId);
if (index > -1) {
this.invoiceData.splice(index, 1);
this.filterDataByPermission();
}
// 显示友好提示
const notFoundMessage = '记录已不存在或已被删除,已从当前列表中移除';
if (this.$message) {
this.$message.warning(notFoundMessage);
} else {
alert(notFoundMessage);
}
} else {
// 正常删除成功的情况
const index = this.invoiceData.findIndex(item => item.keyId === record.keyId);
if (index > -1) {
this.invoiceData.splice(index, 1);
// 删除后刷新过滤数据
this.filterDataByPermission();
}
if (this.$message) {
this.$message.success(message);
} else {
alert(message);
}
}
} else {
// 处理失败响应
console.error('删除失败:', message, res);
if (this.$message) {
this.$message.error(message);
} else {
alert(message);
}
}
}).catch(error => {
// 详细的错误处理
console.error('删除请求异常捕获:', error);
console.error('错误类型:', typeof error);
console.error('完整错误对象:', error);
// 尝试获取更多错误信息
if (error.response) {
console.error('错误响应数据:', JSON.stringify(error.response, null, 2));
} else if (error.config) {
console.error('请求配置:', JSON.stringify(error.config, null, 2));
}
// 智能错误消息提取
let errorMessage = '删除失败';
// 1. 首先尝试从error对象的message属性获取来自request.js的详细错误
if (error.message) {
errorMessage = error.message;
console.log('从error.message获取到错误:', errorMessage);
}
// 2. 然后检查是否有response对象从中提取更详细的错误信息
else if (error.response) {
errorMessage = `${error.response.status} ${error.response.statusText}`;
console.log('从error.response获取到状态:', errorMessage);
if (error.response.data) {
if (error.response.data.msg) {
errorMessage = error.response.data.msg;
} else if (error.response.data.message) {
errorMessage = error.response.data.message;
} else if (typeof error.response.data === 'string') {
errorMessage = error.response.data;
}
console.log('从error.response.data获取到消息:', errorMessage);
}
}
// 3. 最后如果是字符串错误,直接使用
else if (typeof error === 'string') {
errorMessage = error;
console.log('直接使用字符串错误:', errorMessage);
}
// 显示错误信息
console.error('最终确定的错误消息:', errorMessage);
if (this.$message) {
this.$message.error(errorMessage);
} else {
alert(errorMessage);
}
});
}
},
// 切换编辑状态
toggleEdit(id) {
const record = this.invoiceData.find(item => item.id === id);
toggleEdit(keyId) {
const record = this.invoiceData.find(item => item.keyId === keyId);
if (!record) return;
this.checkPermissionAndExecute(record, () => {
record.isActive = !record.isActive;
// 如果取消编辑,清除错误信息
if (!record.isActive) {
this.validationErrors = [];
// 判断是否是取消编辑操作
if (record.isActive) {
// 检查是否是未保存的新增行通过isNewRecord标记判断
const isUnsavedNewRow = record.isNewRecord;
if (isUnsavedNewRow) {
// 对于未保存的新增行,直接删除
const index = this.invoiceData.findIndex(item => item.keyId === keyId);
if (index > -1) {
this.invoiceData.splice(index, 1);
// 删除后刷新过滤数据
this.filterDataByPermission();
}
} else {
// 对于已保存的行,正常切换编辑状态
record.isActive = false;
// 清除错误信息
this.validationErrors = [];
}
} else {
// 进入编辑状态
record.isActive = true;
}
});
},
/* 测试用例:
1. 点击新增按钮 -> 添加一行新记录带有isNewRecord标记
2. 直接点击取消按钮 -> 应该删除该行,不保留任何痕迹
3. 点击新增按钮 -> 添加一行新记录
4. 输入一些数据后再点击取消按钮 -> 应该删除该行,不保留数据
5. 点击新增按钮 -> 添加一行新记录
6. 点击保存按钮 -> 保存成功后移除isNewRecord标记
7. 再次点击编辑,然后点击取消 -> 应该只是退出编辑状态,不删除记录
*/
// 发票号码使用后自动加1功能
incrementInvoiceNumber(employeeId) {
@@ -462,18 +815,21 @@ export default {
},
// 从字符串末尾向前提取前缀,直到遇到第一个字母
// 规则:前缀定义为从号码最末位开始往前推直到出现字母为止的前面字符,若无字母则无前缀
extractPrefixFromEnd(str) {
if (!str) return '';
// 从末尾向前遍历字符串
for (let i = str.length - 1; i >= 0; i--) {
// 如果遇到字母,返回从开始到该字母之前的部分
// 例如:对于"ABC123",从末尾向前找到第一个字母'C',前缀就是'AB'
// 对于"123ABC456",从末尾向前找到第一个字母'C',前缀就是"123AB"
if (/[A-Za-z]/.test(str[i])) {
return str.substring(0, i);
}
}
// 如果没有找到字母,返回整个字符串
return str;
// 如果没有找到字母,则无前缀
return '';
},
// 验证发票号码格式
@@ -532,8 +888,9 @@ export default {
// 检查号码不重复(针对所有操作员)
checkNumberUniqueness(record) {
// 查找所有其他记录(不限操作员)
// 使用keyId作为比较依据因为它是前端唯一标识符
const otherRecords = this.invoiceData.filter(
item => item.id !== record.id
item => item.keyId !== record.keyId
);
for (const item of otherRecords) {
@@ -574,7 +931,8 @@ export default {
}
// 查找所有其他记录
const otherRecords = this.invoiceData.filter(item => item.id !== record.id);
// 使用keyId作为比较依据因为它是前端唯一标识符
const otherRecords = this.invoiceData.filter(item => item.keyId !== record.keyId);
// 提取数字部分进行比较的辅助函数
const extractNumber = function(str) {
@@ -673,29 +1031,34 @@ export default {
},
// 验证单个记录
validateRecord(record) {
// 测试用例说明:
// 1. 测试起始和终止号码前缀一致性:例如"AB123"和"AB456"通过,"AB123"和"AC456"失败
// 2. 测试当前号码与起始号码前缀一致性:例如"AB123"作为起始,"AB125"作为当前号码通过,"AC125"失败
// 3. 测试无字母情况:纯数字"123456"作为起始、终止和当前号码都通过验证
validateRecord(record, rowIndex) {
const errors = [];
const rowInfo = rowIndex ? `${rowIndex}` : '';
// 验证操作员不为空
if (!record.operator || !record.employeeId) {
errors.push(`记录ID ${record.id}: 未设置票据号码的员工`);
errors.push(`${rowInfo}: 未设置票据号码的员工`);
}
// 验证起始号码
const startNumValidation = this.validateInvoiceNumber(record.startNum);
if (!startNumValidation.valid) {
errors.push(`记录ID ${record.id}: 起始号码 ${startNumValidation.message}`);
errors.push(`${rowInfo}: 起始号码 ${startNumValidation.message}`);
}
// 验证终止号码
const endNumValidation = this.validateInvoiceNumber(record.endNum);
if (!endNumValidation.valid) {
errors.push(`记录ID ${record.id}: 终止号码 ${endNumValidation.message}`);
errors.push(`${rowInfo}: 终止号码 ${endNumValidation.message}`);
}
// 验证起始和终止号码长度一致
if (record.startNum && record.endNum && !this.validateNumberLengths(record.startNum, record.endNum)) {
errors.push(`记录ID ${record.id}: 起始发票号码与终止发票号码长度必须完全一致`);
errors.push(`${rowInfo}: 起始发票号码与终止发票号码长度必须完全一致`);
}
// 验证起始和终止号码前缀一致性
@@ -704,27 +1067,37 @@ export default {
const endPrefix = this.extractPrefixFromEnd(record.endNum);
if (startPrefix !== endPrefix) {
errors.push(`记录ID ${record.id}: 起始和终止号码前缀必须一致`);
errors.push(`${rowInfo}: 起始和终止号码前缀必须一致(前缀定义:从号码最末位开始往前推直到出现字母为止的前面字符)`);
}
}
// 验证当前号码前缀与起始号码前缀一致性
if (record.currentNum && record.startNum) {
const currentPrefix = this.extractPrefixFromEnd(record.currentNum);
const startPrefix = this.extractPrefixFromEnd(record.startNum);
if (currentPrefix !== startPrefix) {
errors.push(`${rowInfo}: 当前使用号码前缀与起始号码前缀必须一致(前缀定义:从号码最末位开始往前推直到出现字母为止的前面字符)`);
}
}
// 验证当前号码在范围内
if (record.currentNum && record.startNum && record.endNum &&
!this.validateCurrentNumberInRange(record.currentNum, record.startNum, record.endNum)) {
errors.push(`记录ID ${record.id}: 当前使用号码不在有效范围内`);
errors.push(`${rowInfo}: 当前使用号码不在有效范围内`);
}
// 验证号码唯一性
const uniquenessCheck = this.checkNumberUniqueness(record);
if (!uniquenessCheck.valid) {
errors.push(`记录ID ${record.id}: ${uniquenessCheck.message}`);
errors.push(`${rowInfo}: ${uniquenessCheck.message}`);
}
// 检查号码范围是否与其他记录重叠
if (record.startNum && record.endNum) {
const overlapCheck = this.checkRangeOverlap(record);
if (!overlapCheck.valid) {
errors.push(`记录ID ${record.id}: ${overlapCheck.message}`);
errors.push(`${rowInfo}: ${overlapCheck.message}`);
}
}
@@ -735,9 +1108,9 @@ export default {
runAllValidations() {
this.validationErrors = [];
// 验证每个记录
this.invoiceData.forEach(record => {
const recordErrors = this.validateRecord(record);
// 验证每个记录,添加行号信息
this.invoiceData.forEach((record, index) => {
const recordErrors = this.validateRecord(record, index + 1); // 行号从1开始
if (recordErrors.length > 0) {
this.validationErrors.push(...recordErrors);
}
@@ -763,53 +1136,133 @@ export default {
this.saveButtonText = '保存中...';
// 模拟保存操作
setTimeout(() => {
// 保存成功后将所有记录设为非编辑状态
this.invoiceData.forEach(item => {
item.isActive = false;
// 准备要保存的记录列表
const recordsToSave = this.invoiceData.filter(item => item.isActive || item.isNewRecord);
console.log(`准备保存 ${recordsToSave.length} 条记录`);
// 创建保存Promise数组
const savePromises = recordsToSave.map((record, index) => {
// 准备发送到后端的数据格式适配adm_invoice_segment表结构
const dataToSend = {
// 对于更新操作同时传递id和segmentId确保后端能正确识别
...(!record.isNewRecord && { id: record.keyId }),
// 确保segmentId不为null优先使用keyId然后是segmentId新记录时使用生成的临时ID
segmentId: record.keyId || record.segmentId || Date.now(),
// 员工和开票员信息
employeeId: record.employeeId,
employeeName: record.operator || this.getUserNameById(record.employeeId),
invoicingStaffId: record.employeeId,
invoicingStaffName: record.operator || this.getUserNameById(record.employeeId),
// 日期信息
billBusDate: record.date ? new Date(record.date) : null,
createDate: record.date ? new Date(record.date) : null,
// 号码信息,只使用数据库表对应的字段名
beginNumber: record.startNum,
endNumber: record.endNum,
currentNumber: record.currentNum,
// 状态信息
status: record.status || '未使用',
// 备注信息
remark: record.currentNum ?
(record.remark && !record.remark.includes('当前使用号码:') ?
`${record.remark}; 当前使用号码:${record.currentNum}` :
`当前使用号码:${record.currentNum}`
) : (record.remark || ''),
// 添加租户ID和删除标志
tenantId: 1, // 假设默认租户ID为1
deleteFlag: '0'
};
// 添加调试信息,打印完整的记录对象
console.log(`准备${record.isNewRecord ? '新增' : '更新'}的原始记录对象:`, {
keyId: record.keyId,
id: record.id,
segmentId: record.segmentId,
...record
});
this.saveButtonText = '保存';
alert('数据保存成功!');
// 根据是否是新记录选择不同的API
const url = record.isNewRecord ? '/basicmanage/invoice-segment/add' : '/basicmanage/invoice-segment/update';
const operation = record.isNewRecord ? '新增' : '更新';
// 刷新未设置票据号码员工的警告提示
this.refreshEmployeeWarnings();
}, 800);
console.log(`${operation}记录 ${index + 1}:`, {
url: url,
data: dataToSend
});
return request({
url: url,
method: 'post',
data: dataToSend
}).then(response => {
console.log(`${operation}记录 ${index + 1} 成功:`, response);
return response;
}).catch(err => {
console.error(`${operation}记录 ${index + 1} 失败:`, err);
throw err; // 重新抛出错误以便上层catch捕获
});
});
// 批量保存数据
Promise.all(savePromises)
.then(results => {
console.log('批量保存发票数据成功:', results);
// 保存成功后将所有记录设为非编辑状态并移除isNewRecord标记
this.invoiceData.forEach(item => {
item.isActive = false;
// 移除新增记录标记,确保后续编辑再取消时不会被删除
if (item.isNewRecord) {
delete item.isNewRecord;
}
});
this.saveButtonText = '保存';
// 显示成功消息
const successMessage = `成功保存 ${results.length} 条发票段记录!`;
console.log(successMessage);
if (this.$message) {
this.$message.success(successMessage);
} else {
alert(successMessage);
}
// 重新加载数据以确保数据一致性
console.log('保存成功后重新加载数据...');
this.loadInvoiceData();
})
.catch(error => {
console.error('保存发票数据失败:', error);
console.error('错误详情:', error.response || error);
this.saveButtonText = '保存';
// 显示错误消息
let errorMessage = '保存数据失败';
if (error.response) {
errorMessage += `: ${error.response.status} ${error.response.statusText}`;
} else if (error.message) {
errorMessage += `: ${error.message}`;
}
console.error(errorMessage);
if (this.$message) {
this.$message.error(errorMessage);
} else {
alert(errorMessage);
}
});
},
// 检查未设置票据号码的员工
checkEmployeesWithoutInvoice() {
// 模拟从API获取所有员工列表
// 在实际应用中应该从后端API获取所有员工信息
const allEmployees = [
{ name: '骆丹颖', employeeId: '1702' }
];
// 提取已设置票据号码的员工工号
const employeesWithInvoice = this.invoiceData.map(item => item.employeeId);
// 找出未设置票据号码的员工
this.employeesWithoutInvoice = allEmployees.filter(employee =>
!employeesWithInvoice.includes(employee.employeeId)
);
// 只有当当前用户是管理员时才显示警告
this.showNoInvoiceWarning = this.currentUser.role === 'admin' && this.employeesWithoutInvoice.length > 0;
},
// 刷新未设置票据号码的员工列表
refreshEmployeeWarnings() {
this.checkEmployeesWithoutInvoice();
}
},
mounted() {
// 页面加载后初始化数据
this.filterDataByPermission();
// 检查未设置票据号码的员工
this.checkEmployeesWithoutInvoice();
}
};
</script>

View File

@@ -44,7 +44,15 @@
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="操作员" prop="operatorName" min-width="120" />
<el-table-column label="员工工号" prop="staffNo" min-width="120" />
<el-table-column label="员工工号" prop="staffNo" min-width="120">
<template #default="{ row }">
<el-input
v-if="row._editing"
v-model.trim="row.staffNo"
/>
<span v-else>{{ row.staffNo }}</span>
</template>
</el-table-column>
<el-table-column label="领用日期" prop="receiveDate" min-width="140">
<template #default="{ row }">
<el-date-picker
@@ -113,7 +121,7 @@
<el-input v-model="editForm.operatorName" disabled />
</el-form-item>
<el-form-item label="员工工号">
<el-input v-model="editForm.staffNo" disabled />
<el-input v-model.trim="editForm.staffNo" />
</el-form-item>
<el-form-item label="领用日期">
<el-date-picker v-model="editForm.receiveDate" type="date" value-format="YYYY-MM-DD" style="width: 100%" />

View File

@@ -496,11 +496,12 @@ function getDeptTree() {
}
/** 查询地点下拉树结构 */
function getLocationTree() {
locationTreeSelect({ formList: '11,16' }).then((response) => {
console.log(response, 'response查询部门下拉树结构');
locationOptions.value = response.data.records;
});
const getLocationTree = () => {
locationTreeSelect({ formList: '11,16,17' }).then((res) => {
if (res.data && res.data.records) {
locationOptions.value = res.data.records
}
})
}
// 显示弹框
function edit() {

View File

@@ -854,8 +854,10 @@ function edit() {
statusRestrictedOptions.value = props.statusRestrictedOptions;
partAttributeEnumOptions.value = props.partAttributeEnumOptions;
tempOrderSplitPropertyOptions.value = props.tempOrderSplitPropertyOptions;
antibioticForm.value.antibioticCode = form.value.antibioticCode;
antibioticForm.value.restrictedEnum = form.value.restrictedEnum;
// 当后端返回null或'3'时设置为undefined使select显示为空
antibioticForm.value.antibioticCode = form.value.antibioticCode === null || form.value.antibioticCode === '3' ? undefined : form.value.antibioticCode;
// 当后端返回null、'4'或4时设置为undefined使select显示为空
antibioticForm.value.restrictedEnum = form.value.restrictedEnum === null || form.value.restrictedEnum === '4' || form.value.restrictedEnum === 4 ? undefined : form.value.restrictedEnum;
antibioticForm.value.dose = form.value.dose;
antibioticForm.value.maxUnit = form.value.maxUnit;
antibioticForm.value.minRateCode = form.value.maxRateCode;
@@ -970,8 +972,9 @@ function submitForm() {
proxy.$refs['medicationRef'].validate((valid) => {
if (valid) {
if (form.value.activeFlag == true) {
form.value.antibioticCode = antibioticForm.value.antibioticCode;
form.value.restrictedEnum = antibioticForm.value.restrictedEnum;
// 当用户清空选择时,抗生素分类设置为'3',权限级别设置为'4'
form.value.antibioticCode = antibioticForm.value.antibioticCode === undefined ? '3' : antibioticForm.value.antibioticCode;
form.value.restrictedEnum = antibioticForm.value.restrictedEnum === undefined ? '4' : antibioticForm.value.restrictedEnum;
// form.value.dose = antibioticForm.value.dose;
// form.value.maxUnit = antibioticForm.value.maxUnit;
form.value.minRateCode = antibioticForm.value.minRateCode;

View File

@@ -61,7 +61,7 @@
<script setup>
import { nextTick } from 'vue';
import { getAdviceBaseInfo } from './api';
import { getAdviceBaseInfo, getDeviceList } from './api';
import { throttle } from 'lodash-es';
const props = defineProps({
@@ -97,8 +97,6 @@ const throttledGetList = throttle(
watch(
() => props.adviceQueryParams,
(newValue) => {
console.log('adviceBaseList 接收到参数变化:', newValue);
// 直接更新查询参数
queryParams.value.searchKey = newValue.searchKey || '';
@@ -118,7 +116,6 @@ watch(
queryParams.value.categoryCode = undefined;
}
console.log('发送请求参数:', queryParams.value);
getList();
},
{ deep: true, immediate: true }
@@ -134,51 +131,115 @@ function getPriceFromInventory(row) {
function getList() {
queryParams.value.organizationId = props.patientInfo.orgId;
console.log('发送API请求:', queryParams.value);
// 判断是否是耗材类型adviceType = 2 表示耗材)
const isConsumables = queryParams.value.adviceTypes === '2' || queryParams.value.adviceTypes === 2;
getAdviceBaseInfo(queryParams.value).then((res) => {
if (res.data.records && res.data.records.length > 0) {
let filteredRecords = res.data.records.filter((item) => {
// 后端已经根据adviceTypes参数进行了筛选这里只需要进行前端补充筛选
if (isConsumables) {
// 调用耗材目录接口
const deviceQueryParams = {
pageNo: queryParams.value.pageNum || 1,
pageSize: queryParams.value.pageSize || 100,
searchKey: queryParams.value.searchKey || '',
statusEnum: 2, // 只查询启用状态的耗材
};
getDeviceList(deviceQueryParams).then((res) => {
if (res.data && res.data.records && res.data.records.length > 0) {
// 将耗材目录数据转换为医嘱基础信息格式
const convertedRecords = res.data.records.map((item) => {
return {
adviceName: item.name || item.busNo, // 器材名称
adviceType: 2, // 耗材类型后端接口中耗材的adviceType是2但前端显示为4
unitCode: item.unitCode || '', // 包装单位
unitCode_dictText: item.unitCode_dictText || '',
minUnitCode: item.minUnitCode || item.unitCode || '',
minUnitCode_dictText: item.minUnitCode_dictText || item.unitCode_dictText || '',
volume: item.size || item.totalVolume || '', // 规格
partPercent: item.partPercent || 1, // 拆零比
priceList: item.price ? [{ price: item.price }] : (item.retailPrice ? [{ price: item.retailPrice }] : []),
inventoryList: [], // 耗材可能没有库存列表,需要根据实际情况处理
adviceDefinitionId: item.id,
chargeItemDefinitionId: item.id,
positionId: item.locationId,
positionName: item.locationId_dictText || '',
// 其他可能需要的字段
dose: 0,
doseUnitCode: item.minUnitCode || item.unitCode || '',
doseUnitCode_dictText: item.minUnitCode_dictText || item.unitCode_dictText || '',
injectFlag: 0,
injectFlag_enumText: '否',
skinTestFlag: 0,
skinTestFlag_enumText: '否',
categoryCode: item.categoryCode || '',
// 耗材特有字段
deviceId: item.id, // 耗材ID
deviceName: item.name, // 耗材名称
// 保留原始数据以便后续使用
...item,
};
});
// 过滤库存(药品和耗材需要检查库存,诊疗不需要检查库存)
if (item.adviceType == 1 || item.adviceType == 2) {
if (handleQuantity(item) == 0) {
return false;
adviceBaseList.value = convertedRecords;
total.value = res.data.total || convertedRecords.length;
nextTick(() => {
currentIndex.value = 0;
if (adviceBaseList.value.length > 0) {
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
}
}
// 如果设置了categoryCode筛选条件进行筛选用于区分西药和中成药
// categoryCode = '1' 表示中成药categoryCode = '2' 表示西药
// 注意:只有药品类型(adviceType=1)才需要根据categoryCode筛选
if (queryParams.value.categoryCode && item.adviceType == 1) {
// 只筛选药品类型
if (item.categoryCode != queryParams.value.categoryCode) {
return false;
}
}
return true;
});
adviceBaseList.value = filteredRecords;
console.log('过滤后数据显示:', adviceBaseList.value.length, '条');
total.value = res.data.total;
nextTick(() => {
currentIndex.value = 0;
if (adviceBaseList.value.length > 0) {
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
}
});
} else {
});
} else {
adviceBaseList.value = [];
total.value = 0;
}
}).catch(error => {
console.error('获取耗材目录数据失败:', error);
adviceBaseList.value = [];
}
}).catch(error => {
console.error('获取数据失败:', error);
adviceBaseList.value = [];
});
total.value = 0;
});
} else {
// 调用医嘱基础信息接口(药品、诊疗等)
getAdviceBaseInfo(queryParams.value).then((res) => {
if (res.data.records && res.data.records.length > 0) {
let filteredRecords = res.data.records.filter((item) => {
// 后端已经根据adviceTypes参数进行了筛选这里只需要进行前端补充筛选
// 过滤库存(药品和耗材需要检查库存,诊疗不需要检查库存)
if (item.adviceType == 1 || item.adviceType == 2) {
if (handleQuantity(item) == 0) {
return false;
}
}
// 如果设置了categoryCode筛选条件进行筛选用于区分西药和中成药
// categoryCode = '1' 表示中成药categoryCode = '2' 表示西药
// 注意:只有药品类型(adviceType=1)才需要根据categoryCode筛选
if (queryParams.value.categoryCode && item.adviceType == 1) {
// 只筛选药品类型
if (item.categoryCode != queryParams.value.categoryCode) {
return false;
}
}
return true;
});
adviceBaseList.value = filteredRecords;
total.value = res.data.total;
nextTick(() => {
currentIndex.value = 0;
if (adviceBaseList.value.length > 0) {
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
}
});
} else {
adviceBaseList.value = [];
}
}).catch(error => {
console.error('获取数据失败:', error);
adviceBaseList.value = [];
});
}
}
// 处理键盘事件
const handleKeyDown = (event) => {

View File

@@ -223,6 +223,17 @@ export function getAdviceBaseInfo(queryParams) {
params: queryParams
})
}
/**
* 获取耗材目录列表
*/
export function getDeviceList(queryParams) {
return request({
url: '/data-dictionary/device/information-page',
method: 'get',
params: queryParams
})
}
/**
* 保存处方(单条)
*/
@@ -250,7 +261,8 @@ export function singOut(data) {
return request({
url: '/doctor-station/advice/sign-off',
method: 'post',
data: data
data: data,
skipErrorMsg: true
})
}
/**

View File

@@ -131,7 +131,15 @@
/>
</el-form-item>
<span class="medicine-info"> 诊断{{ diagnosisName }} </span>
<span class="medicine-info"> 皮试{{ scope.row.skinTestFlag_enumText }} </span>
<el-form-item label="皮试:" prop="skinTestFlag" style="margin: 0; margin-right: 20px">
<el-checkbox
v-model="scope.row.skinTestFlag"
:true-label="1"
:false-label="0"
>
</el-checkbox>
</el-form-item>
<span class="medicine-info"> 注射药品{{ scope.row.injectFlag_enumText }} </span>
<span class="total-amount">
总金额{{ scope.row.totalPrice ? scope.row.totalPrice + ' 元' : '0.00 元' }}
@@ -271,6 +279,15 @@
/>
</el-select>
</el-form-item>
<el-form-item label="备注:" prop="remarks" style="margin: 0; margin-right: 20px">
<el-input
v-model="scope.row.remarks"
placeholder="请输入备注"
maxlength="100"
show-word-limit
style="width: 200px"
/>
</el-form-item>
</div>
</div>
<div
@@ -415,7 +432,15 @@
/>
</el-form-item>
<span class="medicine-info"> 诊断:{{ diagnosisName }} </span>
<span class="medicine-info"> 皮试:{{ scope.row.skinTestFlag_enumText }} </span>
<el-form-item label="皮试:" prop="skinTestFlag" style="margin: 0; margin-right: 20px">
<el-checkbox
v-model="scope.row.skinTestFlag"
:true-label="1"
:false-label="0"
>
</el-checkbox>
</el-form-item>
<span class="medicine-info"> 注射药品:{{ scope.row.injectFlag_enumText }} </span>
<span class="total-amount">
总金额:{{ scope.row.totalPrice ? scope.row.totalPrice + ' 元' : '0.00 元' }}
@@ -555,6 +580,15 @@
/>
</el-select>
</el-form-item>
<el-form-item label="备注:" prop="remarks" style="margin: 0; margin-right: 20px">
<el-input
v-model="scope.row.remarks"
placeholder="请输入备注"
maxlength="100"
show-word-limit
style="width: 200px"
/>
</el-form-item>
</div>
</div>
<div
@@ -681,6 +715,15 @@
placeholder="请选择执行科室"
/>
</el-form-item>
<el-form-item label="备注:" prop="remarks" style="margin: 0; margin-right: 20px">
<el-input
v-model="scope.row.remarks"
placeholder="请输入备注"
maxlength="100"
show-word-limit
style="width: 200px"
/>
</el-form-item>
<span class="total-amount">
总金额:{{ scope.row.totalPrice ? scope.row.totalPrice + ' 元' : '0.00 元' }}
</span>
@@ -858,6 +901,13 @@
</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remarks" width="150">
<template #default="scope">
<span v-if="!scope.row.isEdit">
{{ scope.row.remarks || '-' }}
</span>
</template>
</el-table-column>
<el-table-column label="诊断" align="center" prop="diagnosisName" width="150">
<template #default="scope">
<span v-if="!scope.row.isEdit">
@@ -996,13 +1046,12 @@ const adviceTypeList = ref([
label: '中成药',
value: 2,
},
{
label: '耗材',
label: '诊疗',
value: 3,
},
{
label: '诊疗',
label: '耗材',
value: 4,
},
{
@@ -1149,13 +1198,48 @@ function getListInfo(addNewRow) {
getPrescriptionList(props.patientInfo.encounterId).then((res) => {
prescriptionList.value = res.data.map((item) => {
const parsedContent = JSON.parse(item.contentJson);
// 确保 skinTestFlag 是数字类型1 或 0
const skinTestFlag = parsedContent?.skinTestFlag !== undefined && parsedContent?.skinTestFlag !== null
? (typeof parsedContent.skinTestFlag === 'number' ? parsedContent.skinTestFlag : (parsedContent.skinTestFlag ? 1 : 0))
: (item.skinTestFlag !== undefined && item.skinTestFlag !== null
? (typeof item.skinTestFlag === 'number' ? item.skinTestFlag : (item.skinTestFlag ? 1 : 0))
: 0);
// 从后端获取数据时将后端的耗材类型2转换为前端显示的耗材类型4
// 后端接口中1=药品, 2=耗材, 3=诊疗
// 前端显示中1=西药, 2=中成药, 3=诊疗, 4=耗材
let displayAdviceType = parsedContent.adviceType !== undefined ? Number(parsedContent.adviceType) : (item.adviceType !== undefined ? Number(item.adviceType) : undefined);
// 从 contentJson 中读取 deviceId 和 deviceName优先如果没有则从 item 中读取
const deviceId = parsedContent.deviceId || item.deviceId;
const deviceName = parsedContent.deviceName || item.deviceName;
const categoryCode = parsedContent.categoryCode || item.categoryCode;
if (displayAdviceType === 2) {
// 如果 adviceType=2可能是耗材或中成药药品
// 优先判断是否是耗材:有 deviceId 或 deviceName最可靠的判断方式
// 注意categoryCode='2' 可能是西药,也可能是耗材,所以不能仅凭 categoryCode 判断
if (deviceId || deviceName) {
// 如果有 deviceId 或 deviceName一定是耗材转换为前端显示的4
displayAdviceType = 4;
} else if (categoryCode && categoryCode !== '1' && categoryCode !== '2') {
// 如果 categoryCode 不是 '1'(中成药)或 '2'西药说明是耗材转换为前端显示的4
displayAdviceType = 4;
} else if (categoryCode === '1') {
// 如果是中成药adviceType=2 且 categoryCode='1'转换为前端显示的2
displayAdviceType = 2;
}
}
return {
...parsedContent,
...item,
// 确保adviceType是数字类型以便正确显示文本
adviceType: parsedContent.adviceType !== undefined ? Number(parsedContent.adviceType) : (item.adviceType !== undefined ? Number(item.adviceType) : undefined),
adviceType: displayAdviceType,
// 确保 deviceId 和 deviceName 被正确保留,以便后续判断
deviceId: deviceId,
deviceName: deviceName,
categoryCode: categoryCode,
doseQuantity: parsedContent?.doseQuantity,
doseUnitCode_dictText: parsedContent?.doseUnitCode_dictText,
skinTestFlag: skinTestFlag, // 确保皮试字段是数字类型
skinTestFlag_enumText: skinTestFlag == 1 ? '是' : '否', // 更新显示文本
};
});
groupMarkers.value = getGroupMarkers(prescriptionList.value); // 更新标记
@@ -1296,6 +1380,22 @@ function handleChange(value) {
adviceQueryParams.value.searchKey = value;
}
/**
* 处理皮试字段变化
*/
function handleSkinTestChange(row, index) {
// 确保 skinTestFlag 是数字类型1 或 0
if (row.skinTestFlag !== undefined && row.skinTestFlag !== null) {
row.skinTestFlag = typeof row.skinTestFlag === 'number'
? row.skinTestFlag
: (row.skinTestFlag ? 1 : 0);
} else {
row.skinTestFlag = 0;
}
// 更新显示文本
row.skinTestFlag_enumText = row.skinTestFlag == 1 ? '是' : '否';
}
/**
* 选择药品回调
*/
@@ -1350,8 +1450,45 @@ function selectAdviceBase(key, row) {
// 如果医嘱类型是"全部"5或未选择根据药品的categoryCode自动判断
// drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=全部
let finalAdviceType = currentAdviceType;
if ((currentAdviceType === undefined || currentAdviceType === null || currentAdviceType === '' || Number(currentAdviceType) === 5) && Number(row.adviceType) === 1) {
// 药品类型需要根据categoryCode判断是西药还是中成药
// 判断是否是耗材从耗材目录接口返回的数据adviceType=2表示耗材
const isConsumablesFromDevice = row.adviceType == 2 && (row.deviceId || row.deviceName);
// 优先判断如果用户已选择耗材类型4直接使用无论后端返回什么数据
if (currentAdviceType !== undefined && currentAdviceType !== null && currentAdviceType !== '' && Number(currentAdviceType) === 4) {
// 用户已选择耗材类型,直接使用,确保不会被后续逻辑覆盖
finalAdviceType = 4;
} else if (isConsumablesFromDevice) {
// 如果是耗材从耗材目录接口来的数据设置为4前端显示的耗材类型
finalAdviceType = 4;
} else if (Number(row.adviceType) === 2) {
// 如果 row.adviceType == 2可能是中成药药品或耗材
// 重要:优先判断是否是耗材
// 1. 有 deviceId 或 deviceName从耗材目录来的数据
// 2. 用户已选择耗材类型4
// 3. categoryCode 不是 '1'(中成药)或 '2'(西药),说明是耗材
// 4. 从 getAdviceBaseInfo 接口返回的耗材数据adviceType=2 且 categoryCode 通常是 '7' 或其他非药品值
if (row.deviceId || row.deviceName ||
(currentAdviceType !== undefined && currentAdviceType !== null && currentAdviceType !== '' && Number(currentAdviceType) === 4) ||
(row.categoryCode && row.categoryCode !== '1' && row.categoryCode !== '2')) {
finalAdviceType = 4; // 耗材
} else {
// 没有 deviceId 或 deviceName且 categoryCode 是 '1' 或 '2',可能是中成药(药品)
// 根据 categoryCode 判断
if (row.categoryCode == '1') {
finalAdviceType = 2; // 中成药
} else if (row.categoryCode == '2') {
finalAdviceType = 1; // 西药虽然adviceType=2通常不是西药但为了安全起见
} else {
// 如果没有categoryCode或categoryCode不是'1'或'2',默认认为是耗材
finalAdviceType = 4;
}
}
} else if (currentAdviceType !== undefined && currentAdviceType !== null && currentAdviceType !== '' && Number(currentAdviceType) !== 5) {
// 用户已选择其他医嘱类型(且不是"全部"),保留用户的选择,确保是数字类型
finalAdviceType = Number(currentAdviceType);
} else if (Number(row.adviceType) === 1) {
// 药品类型adviceType=1需要根据categoryCode判断是西药还是中成药
// categoryCode = '1' 表示中成药categoryCode = '2' 表示西药
if (row.categoryCode == '1') {
finalAdviceType = 2; // 中成药
@@ -1361,28 +1498,41 @@ function selectAdviceBase(key, row) {
// 如果没有categoryCode使用项目默认的类型
finalAdviceType = Number(row.adviceType);
}
} else if (currentAdviceType !== undefined && currentAdviceType !== null && currentAdviceType !== '' && Number(currentAdviceType) !== 5) {
// 用户已选择医嘱类型(且不是"全部"),保留用户的选择,确保是数字类型
finalAdviceType = Number(currentAdviceType);
} else {
// 使用项目默认的类型,确保是数字类型
finalAdviceType = Number(row.adviceType);
}
// 确保 skinTestFlag 是数字类型1 或 0如果未定义则默认为 0
let skinTestFlag = row.skinTestFlag !== undefined && row.skinTestFlag !== null
? (typeof row.skinTestFlag === 'number' ? row.skinTestFlag : (row.skinTestFlag ? 1 : 0))
: 0;
// 先复制 row 对象,但确保 adviceType 使用 finalAdviceType
const rowData = JSON.parse(JSON.stringify(row));
rowData.adviceType = finalAdviceType; // 确保使用最终判断的医嘱类型
// 确保保留原有的 adviceType如果用户已选择
const originalAdviceType = prescriptionList.value[rowIndex.value]?.adviceType;
const preservedAdviceType = (originalAdviceType === 4) ? 4 : finalAdviceType;
prescriptionList.value[rowIndex.value] = {
...prescriptionList.value[rowIndex.value],
...JSON.parse(JSON.stringify(row)),
adviceType: finalAdviceType, // 确保是数字类型
...rowData,
adviceType: preservedAdviceType, // 优先保留用户已选择的耗材类型4
skinTestFlag: skinTestFlag, // 确保皮试字段是数字类型
};
// 确保字典已加载后更新显示
nextTick(() => {
// 如果字典已加载,强制更新视图以确保显示正确的文本
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) {
// 触发响应式更新
// 触发响应式更新,但保留 adviceType
const currentRow = prescriptionList.value[rowIndex.value];
const preservedAdviceType = currentRow.adviceType; // 保存当前的 adviceType
prescriptionList.value[rowIndex.value] = {
...currentRow
...currentRow,
adviceType: preservedAdviceType // 确保 adviceType 不被覆盖
};
}
});
@@ -1403,7 +1553,11 @@ function selectAdviceBase(key, row) {
).chargeItemDefinitionId;
// 库存列表 + 价格列表拼成批次号的下拉框
if (row.adviceType != 3) {
// 判断是否是耗材(从耗材目录接口返回的数据)
const isConsumables = (row.adviceType == 2 && (row.deviceId || row.deviceName)) || finalAdviceType == 4;
if (row.adviceType != 3 && !isConsumables) {
// 药品类型(西药、中成药)需要检查库存
if (row.inventoryList && row.inventoryList.length == 0) {
expandOrder.value = [];
proxy.$modal.msgWarning('该项目无库存');
@@ -1439,25 +1593,88 @@ function selectAdviceBase(key, row) {
.toFixed(2);
prescriptionList.value[rowIndex.value].positionName = stock.locationName;
}
} else if (isConsumables) {
// 耗材类型:设置价格和位置信息
const price = row.price || row.retailPrice || (row.priceList && row.priceList.length > 0 ? row.priceList[0].price : 0);
prescriptionList.value[rowIndex.value].unitPrice = price;
prescriptionList.value[rowIndex.value].unitTempPrice = price;
prescriptionList.value[rowIndex.value].minUnitPrice = price;
prescriptionList.value[rowIndex.value].positionId = row.locationId;
prescriptionList.value[rowIndex.value].positionName = row.locationId_dictText || row.positionName || '';
// 耗材可能没有库存列表,设置为空数组
prescriptionList.value[rowIndex.value].stockList = [];
prescriptionList.value[rowIndex.value].inventoryList = [];
} else {
// 执行科室默认逻辑:优先使用诊疗项目维护的所属科室,如果没有则使用开单科室
const defaultOrgId = row.positionId || props.patientInfo.orgId;
prescriptionList.value[rowIndex.value].orgId = defaultOrgId;
prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price;
// 诊疗类型:执行科室默认逻辑:优先使用诊疗项目维护的所属科室,如果没有则使用开单科室
const defaultOrgId = row.positionId || props.patientInfo.orgId;
prescriptionList.value[rowIndex.value].orgId = defaultOrgId;
prescriptionList.value[rowIndex.value].unitPrice = row.priceList && row.priceList.length > 0 ? row.priceList[0].price : 0;
}
// 检查是否是皮试药品,如果是则弹出确认提示
// 只对药品类型(西药和中成药)进行皮试提示
// 使用原始 row 中的 skinTestFlag 来判断,因为这是药品基础信息中的皮试标志
const isSkinTestDrug = row.skinTestFlag !== undefined && row.skinTestFlag !== null
? (typeof row.skinTestFlag === 'number' ? row.skinTestFlag == 1 : (row.skinTestFlag ? true : false))
: false;
if ((row.adviceType == 1 || row.adviceType == 2) && isSkinTestDrug) {
ElMessageBox.confirm(
`药品:${row.adviceName}需要做皮试,是否做皮试?`,
'提示',
{
confirmButtonText: '是(Y)',
cancelButtonText: '否(N)',
type: 'info',
closeOnClickModal: false,
closeOnPressEscape: false,
distinguishCancelAndClose: true,
}
)
.then(() => {
// 用户点击"是",自动勾选皮试字段
prescriptionList.value[rowIndex.value].skinTestFlag = 1;
prescriptionList.value[rowIndex.value].skinTestFlag_enumText = '是';
// 展开订单并继续后续流程
expandOrderAndFocus(key, row);
})
.catch((action) => {
// 用户点击"否"或关闭,不勾选皮试字段
prescriptionList.value[rowIndex.value].skinTestFlag = 0;
prescriptionList.value[rowIndex.value].skinTestFlag_enumText = '否';
// 展开订单并继续后续流程
expandOrderAndFocus(key, row);
});
} else {
// 不是皮试药品,直接展开订单
expandOrderAndFocus(key, row);
}
}
expandOrder.value = [key];
/**
* 展开订单并聚焦输入框
*/
function expandOrderAndFocus(key, row) {
expandOrder.value = [key];
nextTick(() => {
// 判断是否是耗材adviceType == 4 或从耗材目录来的数据)
const isConsumables = row.adviceType == 4 || (row.adviceType == 2 && (row.deviceId || row.deviceName));
if (row.adviceType == 1 || row.adviceType == 2) {
// 药品类型(西药、中成药)
if (row.injectFlag == 1) {
inputRefs.value['executeNum']?.focus();
} else {
inputRefs.value['doseQuantity']?.focus();
}
} else if (isConsumables) {
// 耗材类型:类似诊疗,聚焦执行次数输入框
inputRefs.value['quantity']?.focus();
} else {
// 诊疗类型
inputRefs.value['quantity']?.focus();
}
});
}
function getOrgList() {
@@ -1578,9 +1795,14 @@ function handleSave() {
// 此处签发处方和单行保存处方传参相同后台已经将传参存为JSON字符串此处直接转换为JSON即可
loading.value = true;
let list = saveList.map((item) => {
const parsedContent = JSON.parse(item.contentJson);
// 保存时将前端的耗材类型4转换为后端需要的类型2
// 后端接口中1=药品, 2=耗材, 3=诊疗
// 前端显示中1=西药, 2=中成药, 3=诊疗, 4=耗材
const saveAdviceType = parsedContent.adviceType == 4 ? 2 : parsedContent.adviceType;
return {
...JSON.parse(item.contentJson),
adviceType: item.adviceType,
...parsedContent,
adviceType: saveAdviceType,
requestId: item.requestId,
dbOpType: '1',
groupId: item.groupId,
@@ -1698,21 +1920,40 @@ function handleSaveSign(row, index) {
row.conditionDefinitionId = conditionDefinitionId.value;
row.encounterDiagnosisId = encounterDiagnosisId.value;
row.diagnosisName = diagnosisName.value;
// 确保 skinTestFlag 是数字类型1 或 0
if (row.skinTestFlag !== undefined && row.skinTestFlag !== null) {
row.skinTestFlag = typeof row.skinTestFlag === 'number'
? row.skinTestFlag
: (row.skinTestFlag ? 1 : 0);
} else {
row.skinTestFlag = 0;
}
row.skinTestFlag_enumText = row.skinTestFlag == 1 ? '是' : '否';
if (row.injectFlag == 1) {
row.sortNumber = row.sortNumber ? row.sortNumber : prescriptionList.value.length;
}
// row.dose = row.doseQuantity;
// row.doseUnitCode = unitCodeList.value.find((item) => item.type == 'dose').value;
// row.doseUnitCode = JSON.parse(JSON.stringify(row.minUnitCode)); // 页面显示与赋值不符,此处先简单处理,后续修改
row.contentJson = JSON.stringify(row);
// 保存时将前端的耗材类型4转换为后端需要的类型2
// 后端接口中1=药品, 2=耗材, 3=诊疗
// 前端显示中1=西药, 2=中成药, 3=诊疗, 4=耗材
// 创建保存用的数据副本将前端的耗材类型4转换为后端需要的类型2
const saveData = { ...row };
const saveAdviceType = saveData.adviceType == 4 ? 2 : saveData.adviceType;
saveData.adviceType = saveAdviceType;
// 确保 deviceId 和 deviceName 被保存到 contentJson 中,以便重新加载时能正确识别耗材
saveData.contentJson = JSON.stringify(saveData);
if (row.requestId) {
row.dbOpType = '2';
savePrescription({ adviceSaveList: [row] }).then((res) => {
saveData.dbOpType = '2';
savePrescription({ adviceSaveList: [saveData] }).then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('保存成功');
getListInfo(true);
nextId.value == 1;
}
}).catch((error) => {
console.error('保存出错:', error);
});
} else {
if (prescriptionList.value[0].adviceName) {
@@ -1783,11 +2024,18 @@ function setValue(row) {
type: 'minUnit',
});
}
// 确保 skinTestFlag 是数字类型1 或 0如果未定义则默认为 0
const skinTestFlag = row.skinTestFlag !== undefined && row.skinTestFlag !== null
? (typeof row.skinTestFlag === 'number' ? row.skinTestFlag : (row.skinTestFlag ? 1 : 0))
: 0;
prescriptionList.value[rowIndex.value] = {
...prescriptionList.value[rowIndex.value],
...JSON.parse(JSON.stringify(row)),
// 确保adviceType为数字类型避免类型不匹配导致的显示问题
adviceType: Number(row.adviceType),
skinTestFlag: skinTestFlag, // 确保皮试字段是数字类型
skinTestFlag_enumText: skinTestFlag == 1 ? '是' : '否', // 更新显示文本
};
prescriptionList.value[rowIndex.value].orgId = undefined;
prescriptionList.value[rowIndex.value].dose = undefined;

View File

@@ -198,9 +198,41 @@ const handleSave = async () => {
await formRef.value.validate();
}
// 模拟保存操作
// 实际项目中应该调用API保存数据
console.log('保存配置:', formData);
// 将表单数据转换为系统配置格式
const configData = [
{ configKey: 'medicalRecordFee', configValue: formData.medicalRecordFee, configName: '病历本费用' },
{ configKey: 'patientCardFee', configValue: formData.patientCardFee, configName: '就诊卡费' },
{ configKey: 'medicalRecordFlag', configValue: formData.medicalRecordFlag ? '1' : '0', configName: '病历费入账标志' },
{ configKey: 'isNightShift', configValue: formData.isNightShift ? '1' : '0', configName: '是否启用晚班' },
{ configKey: 'patientCardFlag', configValue: formData.patientCardFlag ? '1' : '0', configName: '就诊卡记账标志' },
{ configKey: 'morningStartTime', configValue: formData.morningStartTime, configName: '上午接诊起始时间' },
{ configKey: 'autoGenerateOutpatientNo', configValue: formData.autoGenerateOutpatientNo ? '1' : '0', configName: '自动产生门诊号' },
{ configKey: 'allowModifyOutpatientNo', configValue: formData.allowModifyOutpatientNo ? '1' : '0', configName: '建档时是否允许修改门诊号' },
{ configKey: 'afternoonStartTime', configValue: formData.afternoonStartTime, configName: '下午起始时间' },
{ configKey: 'eveningStartTime', configValue: formData.eveningStartTime, configName: '晚上起始时间' },
{ configKey: 'registrationValidity', configValue: formData.registrationValidity, configName: '挂号有效期' },
{ configKey: 'registrationDocumentMode', configValue: formData.registrationDocumentMode, configName: '挂号单据模式' },
{ configKey: 'exemptFlag', configValue: formData.exemptFlag ? '1' : '0', configName: '减免标志' },
{ configKey: 'consultationFlag', configValue: formData.consultationFlag ? '1' : '0', configName: '义诊标志' },
{ configKey: 'enableHolidayFeeFloat', configValue: formData.enableHolidayFeeFloat ? '1' : '0', configName: '启用法定节假日挂号费浮动' },
{ configKey: 'guardianAge', configValue: formData.guardianAge, configName: '监护人规定年龄' },
{ configKey: 'enableDoubleScreen', configValue: formData.enableDoubleScreen ? '1' : '0', configName: '门诊挂号启用双屏' },
{ configKey: 'optionalRegistrationType', configValue: formData.optionalRegistrationType ? '1' : '0', configName: '挂号类型可选择' },
{ configKey: 'isPrint', configValue: formData.isPrint ? '1' : '0', configName: '是否打印挂号单' },
];
// 调用系统配置API保存每个参数
for (const config of configData) {
// 先查询是否存在该配置
const existingConfig = await getConfigKey(config.configKey);
if (existingConfig.data) {
// 如果存在则更新
await updateConfig({ ...config, configId: existingConfig.data.configId });
} else {
// 如果不存在则新增
await addConfig(config);
}
}
ElMessage.success('保存成功');
} catch (error) {
@@ -217,11 +249,11 @@ const handleClose = () => {
<style scoped>
.app-container {
padding: 20px;
padding: 5px;
}
.config-header {
margin-bottom: 20px;
margin-bottom: 5px;
}
.config-header .el-button {
@@ -229,11 +261,11 @@ const handleClose = () => {
}
.config-tabs {
margin-top: 10px;
margin-top: 0;
}
.config-form {
padding: 20px;
padding: 10px;
background-color: #f5f7fa;
border-radius: 4px;
}
@@ -241,7 +273,7 @@ const handleClose = () => {
.form-row {
display: flex;
align-items: center;
margin-bottom: 15px;
margin-bottom: 5px;
flex-wrap: wrap;
}
@@ -257,14 +289,14 @@ const handleClose = () => {
}
.config-form .el-form-item--medium .el-form-item__content {
line-height: 32px;
line-height: 28px;
}
.tab-content {
padding: 20px;
padding: 10px;
color: #606266;
text-align: center;
line-height: 100px;
line-height: 60px;
background-color: #f5f7fa;
border-radius: 4px;
}

View File

@@ -763,18 +763,27 @@ function handleExport() {
);
}
/** 用户状态修改 */
function handleStatusChange(row) {
/* 测试用例:
1. 启用状态切换:点击启用/停用开关,确认后只更新用户状态字段
2. 角色保持不变:切换状态后,用户的角色权限应保持原样
3. 不同用户类型:对管理员、普通用户、医生等不同角色用户测试状态切换
4. 批量操作:测试批量修改状态是否也只改变状态不改变角色
*/
function handleStatusChange(row) {
let text = row.status === '0' ? '启用' : '停用';
proxy.$modal
.confirm('确认要"' + text + '""' + row.userName + '"用户吗?')
.then(function () {
// 只更新用户状态,不修改用户角色
return changeUserStatus(row.userId, row.status);
})
.then(() => {
proxy.$modal.msgSuccess(text + '成功');
getList(); // 刷新列表,确保显示最新的用户信息
proxy.$modal.msgSuccess(text + '并设置为' + roleText + '成功');
})
.catch(function () {
row.status = row.status === '0' ? '1' : '0';
proxy.$modal.msgError('操作失败,请重试');
});
}
/** 更多操作 */