Files
his/openhis-ui-vue3/src/views/doctorstation/doctorphrase/index.vue

525 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="doctor-phrase-container">
<!-- 搜索栏 -->
<div class="search-bar">
<el-select v-model="searchScope" placeholder="范围" style="width: 120px;">
<el-option label="个人" :value="1"></el-option>
<el-option label="科室" :value="2"></el-option>
<el-option label="全院" :value="3"></el-option>
</el-select>
<el-input v-model="searchKeyword" placeholder="请输入名称" style="width: 240px; margin-left: 10px;"
@keyup.enter="handleSearch"></el-input>
<el-button style="margin-left: 10px;" @click="handleSearch"><el-icon>
<Search />
</el-icon> 查询</el-button>
<el-button style="margin-left: 10px;" @click="handleReset"><el-icon>
<Refresh />
</el-icon> 重置</el-button>
<el-button type="primary" style="margin-left: 10px;" @click="showAddDialog"><el-icon>
<Plus />
</el-icon> 增加</el-button>
</div>
<!-- 表格 -->
<el-table :data="tableData" style="width: 100%; margin-top: 20px;">
<el-table-column prop="sortNo" label="排序号" width="200">
</el-table-column>
<el-table-column prop="phraseName" label="名称" width="250">
</el-table-column>
<el-table-column prop="phraseContent" label="内容">
</el-table-column>
<el-table-column label="范围" width="250">
<template #default="scope">
{{ getScopeName(scope.row.phraseType) }}
</template>
</el-table-column>
<!-- 业务分类列使用枚举转换中文名称 -->
<el-table-column label="业务分类" width="200">
<template #default="scope">
{{ getBusinessTypeName(scope.row.phraseCategory) }}
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" @click="showEditDialog(scope.row)">
编辑
</el-button>
<el-button type="danger" size="small" style="margin-left: 5px;" @click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage"
:page-sizes="[15, 30, 50]" :page-size="pageSize" layout="total, prev, pager, next, jumper, sizes"
:total="total"></el-pagination>
</div>
<!-- 新增模态框 -->
<el-dialog v-model="addDialogVisible" title="新增医生常用语" width="600px" center>
<el-form ref="addFormRef" :model="addForm" :rules="addRules" label-width="100px" class="add-form">
<el-form-item label="名称" prop="phraseName">
<el-input v-model="addForm.phraseName" placeholder="请输入常用语名称(不超过50字)" maxlength="50" show-word-limit
style="width: 100%;"></el-input>
</el-form-item>
<el-form-item label="内容" prop="phraseContent">
<el-input v-model="addForm.phraseContent" placeholder="请输入常用语内容" type="textarea" :rows="4"
style="width: 100%;"></el-input>
</el-form-item>
<el-form-item label="排序号">
<el-input-number v-model="addForm.sortNo" :min="1" :step="1" style="width: 100%;"></el-input-number>
</el-form-item>
<el-form-item label="业务分类" prop="phraseCategory">
<el-select v-model="addForm.phraseCategory" placeholder="请选择业务分类" style="width: 100%;">
<el-option v-for="item in businessTypeOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="范围" prop="phraseType">
<el-select v-model="addForm.phraseType" placeholder="请选择范围" style="width: 100%;">
<el-option label="个人" :value="1"></el-option>
<el-option label="科室" :value="2"></el-option>
<el-option label="全院" :value="3"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleAdd">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 编辑模态框 -->
<el-dialog v-model="editDialogVisible" title="编辑医生常用语" width="600px" center>
<el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="100px" class="add-form">
<el-form-item label="名称" prop="phraseName">
<el-input v-model="editForm.phraseName" placeholder="请输入常用语名称(不超过50字)" maxlength="50" show-word-limit
style="width: 100%;"></el-input>
</el-form-item>
<el-form-item label="内容" prop="phraseContent">
<el-input v-model="editForm.phraseContent" placeholder="请输入常用语内容" type="textarea" :rows="4"
style="width: 100%;"></el-input>
</el-form-item>
<el-form-item label="排序号">
<el-input-number v-model="editForm.sortNo" :min="1" :step="1" style="width: 100%;"></el-input-number>
</el-form-item>
<el-form-item label="业务分类" prop="phraseCategory">
<el-select v-model="editForm.phraseCategory" placeholder="请选择业务分类" style="width: 100%;">
<el-option v-for="item in businessTypeOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="范围" prop="phraseType">
<el-select v-model="editForm.phraseType" placeholder="请选择范围" style="width: 100%;">
<el-option label="个人" :value="1"></el-option>
<el-option label="科室" :value="2"></el-option>
<el-option label="全院" :value="3"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="editDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleEditSave">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { Check, Close, Plus, Refresh, Search } from '@element-plus/icons-vue'
import {
addDoctorPhrase,
deleteDoctorPhrase,
getDoctorPhraseList,
searchDoctorPhraseList,
updateDoctorPhrase
} from './api'
import { ElMessage, ElMessageBox } from 'element-plus'
// 如果数据字典未对齐后端枚举暂时注释useDict改用固定枚举后续可替换为dict
// import { useDict } from '@/utils/dict'
// 核心配置定义和后端枚举对齐的业务分类选项替换原businessclassification
const businessTypeOptions = ref([
{ label: '主诉', value: 'MAIN_COMPLAINT' },
{ label: '现病史', value: 'PRESENT_HISTORY' },
{ label: '术前', value: 'PRE_OPERATION' },
{ label: '术后', value: 'POST_OPERATION' },
{ label: '既往史', value: 'PAST_HISTORY' }
])
// 搜索条件
const searchScope = ref(null) // null表示未选择数字类型1=个人2=科室3=全院
const searchKeyword = ref('')
// 表格数据
const tableData = ref([])
// 分页参数配置
const currentPage = ref(1)
const pageSize = ref(15)
const total = ref(0)
// 新增模态框相关配置
const addDialogVisible = ref(false)
const addFormRef = ref()
// 新增表单默认值:业务分类默认空,强制用户选择
const addForm = ref({
phraseName: '',
phraseContent: '',
sortNo: 1,
phraseType: 1,
phraseCategory: ''
})
// 完善新增表单验证规则补充业务分类必填、名称50字限制
const addRules = {
phraseName: [
{ required: true, message: '请输入常用语名称', trigger: 'blur' },
{ max: 50, message: '常用语名称不能超过50字', trigger: 'blur' }
],
phraseContent: [
{ required: true, message: '请输入常用语内容', trigger: 'blur' }
],
phraseType: [
{ required: true, message: '请选择范围', trigger: 'change' }
],
phraseCategory: [
{ required: true, message: '请选择业务分类', trigger: 'change' }
]
}
// 编辑模态框相关配置
const editDialogVisible = ref(false)
const editFormRef = ref()
// 编辑表单默认值配置
const editForm = ref({
id: '',
phraseName: '',
phraseContent: '',
sortNo: 1,
phraseType: 1,
phraseCategory: ''
})
// 完善编辑表单验证规则(和新增一致,保证校验统一)
const editRules = {
phraseName: [
{ required: true, message: '请输入常用语名称', trigger: 'blur' },
{ max: 50, message: '常用语名称不能超过50字', trigger: 'blur' }
],
phraseContent: [
{ required: true, message: '请输入常用语内容', trigger: 'blur' }
],
phraseType: [
{ required: true, message: '请选择范围', trigger: 'change' }
],
phraseCategory: [
{ required: true, message: '请选择业务分类', trigger: 'change' }
]
}
// 重构业务分类名称转换方法(适配后端编码→中文展示)
const getBusinessTypeName = (code) => {
if (!code) return '未知类型'
const item = businessTypeOptions.value.find(item => item.value === code)
return item ? item.label : '未知类型'
}
// 获取范围名称转换方法
const getScopeName = (scope) => {
const scopeMap = {
1: '个人',
2: '科室',
3: '全院'
}
return scopeMap[scope] || '未知范围'
}
// 名称唯一性校验函数(新增/编辑通用编辑时排除自身ID
const validatePhraseName = (phraseName, excludeId = null) => {
if (!phraseName || !phraseName.trim()) {
return { valid: false, message: '请输入常用语名称' }
}
// 检查字数限制严格控制50字
if (phraseName.trim().length > 50) {
return { valid: false, message: '常用语名称不能超过50字' }
}
// 检查名称是否已存在,编辑时排除当前记录
const existingPhrase = allData.value.find(item => {
if (excludeId && item.id === excludeId) {
return false
}
return item.phraseName.trim() === phraseName.trim()
})
if (existingPhrase) {
return { valid: false, message: '常用语名称已存在,请输入不同的名称' }
}
return { valid: true, message: '' }
}
// 所有数据(用于客户端分页处理)
const allData = ref([])
// 获取医生常用语列表数据
const fetchDoctorPhraseList = async () => {
try {
const response = await getDoctorPhraseList()
// 处理后端返回的数据结构data.data
if (response.code === 200 && response.data && response.data.data) {
// 按照sortNo由小到大排序保证列表顺序正确
allData.value = response.data.data.sort((a, b) => a.sortNo - b.sortNo)
total.value = allData.value.length
// 执行客户端分页逻辑
applyPagination()
} else {
ElMessage.error('获取数据失败: ' + (response.msg || '未知错误'))
allData.value = []
total.value = 0
}
} catch (error) {
console.error('获取列表失败:', error) // 增加控制台日志便于调试
ElMessage.error('获取数据失败: 网络请求错误')
allData.value = []
total.value = 0
}
}
// 重置功能方法
const handleReset = () => {
// 重置搜索条件
searchScope.value = null
searchKeyword.value = ''
// 重置分页到第一页
currentPage.value = 1
// 重新加载所有数据
fetchDoctorPhraseList()
}
// 客户端分页处理核心方法
const applyPagination = () => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
tableData.value = allData.value.slice(start, end)
}
// 分页条数改变事件
const handleSizeChange = (val) => {
pageSize.value = val
applyPagination()
}
// 分页页码改变事件
const handleCurrentChange = (val) => {
currentPage.value = val
applyPagination()
}
// 搜索功能核心方法
const handleSearch = async () => {
try {
// searchScope可能是null未选择、1=个人2=科室3=全院
const phraseType = searchScope.value === null ? undefined : searchScope.value
// 调用搜索接口phraseName, phraseType
const response = await searchDoctorPhraseList(searchKeyword.value, phraseType)
if (response.code === 200 && response.data && response.data.data) {
// 按照sortNo由小到大排序
allData.value = response.data.data.sort((a, b) => a.sortNo - b.sortNo)
total.value = allData.value.length
currentPage.value = 1 // 搜索后重置到第一页
applyPagination() // 应用分页
} else {
ElMessage.error('搜索失败: ' + (response.msg || '未知错误'))
allData.value = []
total.value = 0
}
} catch (error) {
console.error('搜索失败:', error)
ElMessage.error('搜索失败: 网络请求错误')
allData.value = []
total.value = 0
}
}
// 打开新增模态框方法
const showAddDialog = () => {
// 重置表单数据
addForm.value = {
phraseName: '',
phraseContent: '',
sortNo: 1,
phraseType: 1,
phraseCategory: ''
}
// 重置表单验证状态
if (addFormRef.value) {
addFormRef.value.clearValidate()
}
// 打开模态框
addDialogVisible.value = true
}
// 提交新增表单方法
const handleAdd = async () => {
try {
// 先执行表单验证
const validateResult = await addFormRef.value.validate()
if (!validateResult) return
// 名称唯一性校验
const nameValidation = validatePhraseName(addForm.value.phraseName)
if (!nameValidation.valid) {
ElMessage.error(nameValidation.message)
return
}
// 添加数据库要求的必填默认字段
const formData = {
...addForm.value,
phraseCode: 'PHR000', // 建议后端自动生成,前端可传空
enableFlag: 1,
staffId: 1001, // 建议从登录态获取,不要硬编码
deptCode: 'DEPT001', // 建议从登录态获取
creatorId: 1001 // 建议从登录态获取
}
// 调用新增接口
const response = await addDoctorPhrase(formData)
// 处理新增结果
if (response.code === 200) {
ElMessage.success('新增成功')
addDialogVisible.value = false
// 重新拉取数据比手动unshift更可靠避免数据不一致
fetchDoctorPhraseList()
} else {
ElMessage.error('新增失败: ' + (response.msg || '未知错误'))
}
} catch (error) {
console.error('新增失败:', error)
// 表单验证失败或其他错误
ElMessage.error('新增失败: 请填写完整信息或网络异常')
}
}
// 删除功能方法
const handleDelete = async (row) => {
try {
// 弹出确认对话框
await ElMessageBox.confirm(
'确定要删除该常用语吗?删除后无法恢复!',
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
// 调用删除接口
const response = await deleteDoctorPhrase(row.id)
if (response.code === 200) {
ElMessage.success('删除成功')
// 重新拉取数据
fetchDoctorPhraseList()
} else {
ElMessage.error('删除失败: ' + (response.msg || '未知错误'))
}
} catch (error) {
// 用户取消删除时不提示错误
if (error !== 'cancel') {
console.error('删除失败:', error)
ElMessage.error('删除操作失败: 网络异常或权限不足')
}
}
}
// 打开编辑弹窗核心方法 - 点击表格编辑按钮触发
const showEditDialog = (row) => {
// 深拷贝当前行数据到编辑表单,避免原数据被修改
editForm.value = JSON.parse(JSON.stringify(row))
// 确保数字类型正确,防止表单赋值异常
editForm.value.sortNo = Number(editForm.value.sortNo) || 1
editForm.value.phraseType = Number(editForm.value.phraseType) || 1
// 重置表单验证状态
if (editFormRef.value) {
editFormRef.value.clearValidate()
}
// 打开编辑弹窗
editDialogVisible.value = true
}
// 编辑表单提交保存方法
const handleEditSave = async () => {
try {
// 先执行表单验证
const validateResult = await editFormRef.value.validate()
if (!validateResult) return
// 名称唯一性校验排除当前编辑的这条记录ID
const nameValidation = validatePhraseName(editForm.value.phraseName, editForm.value.id)
if (!nameValidation.valid) {
ElMessage.error(nameValidation.message)
return
}
// 准备更新数据修复时间格式为ISO字符串适配后端LocalDateTime
const updateData = {
...editForm.value,
enableFlag: 1,
updateTime: new Date().toISOString() // 前端临时赋值,后端最终以自己的为准
}
// 调用更新接口
const response = await updateDoctorPhrase(updateData)
if (response.code === 200) {
ElMessage.success('更新成功')
editDialogVisible.value = false
// 重新拉取数据,保证列表数据最新
fetchDoctorPhraseList()
} else {
ElMessage.error('更新失败: ' + (response.msg || '未知错误'))
}
} catch (error) {
console.error('更新失败:', error)
ElMessage.error('更新操作失败: 网络请求错误')
}
}
// 组件挂载时初始化加载数据
onMounted(() => {
fetchDoctorPhraseList()
})
</script>
<style scoped>
.doctor-phrase-container {
padding: 20px;
}
.search-bar {
display: flex;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
/* 适配小屏幕,防止按钮换行溢出 */
gap: 10px;
/* 替换margin-left更优雅的间距 */
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
/* 新增表单样式适配 */
.add-form {
padding: 10px 0;
}
</style>