根因: - Bug #请修复 Bug #591 存在的问题 修复: - ### 变更摘要 - 全链路数据流分析**:录取(弹窗输入)→ 保存(API传入)→ 查询(Mapper返回)→ 修改(Service记录)→ 删除/停止(状态变更)→ 关联(列表展示) - ### 后端变更(4个文件) - 1. `AdviceBatchOpParam.java`** — 停嘱参数添加 `stopTime` 字段 - 新增 `@JsonFormat Date stopTime`,支持前端传入停嘱时间 - 2. `RequestBaseDto.java`** — 查询DTO添加 `stopUserName`、`stopTime` 字段 - 新增 `String stopUserName`(停嘱医生姓名) - 新增 `Date stopTime`(停嘱时间) - 3. `AdviceManageAppServiceImpl.java`** — 停嘱Service增强 - 优先使用前端传入的 `stopTime`,兜底用当前时间 - 通过 `SecurityUtils.getNickName()` 获取当前操作用户昵称,记录到 `updateBy` - 药品和诊疗两个更新入口均已同步修改 - 4. `AdviceManageAppMapper.xml`** — 三个UNION ALL子查询添加字段 - 药品子查询:`T1.effective_dose_end AS stop_time` + `T1.update_by AS stop_user_name` - 耗材子查询:`NULL AS stop_time` + `'' AS stop_user_name` - 诊疗子查询:`T1.occurrence_end_time AS stop_time` + `T1.update_by AS stop_user_name` - ### 前端变更(1个文件) - `order/index.vue`**: - 1. **停嘱时间弹窗** — 点击「停嘱」后弹出 `el-dialog`,内含 `el-date-picker`(datetime类型,默认当前时间),确定后才调用API - 2. **表格列** — 在「皮试」列后面、「诊断」列前面新增两列: - 「停嘱医生」`prop="stopUserName"`,宽度120px - 「停嘱时间」`prop="stopTime"`,宽度170px - 3. **`handleStopAdvice`** — 保留原有校验(未保存/未签发/已停止检查),校验通过后弹出时间选择弹窗而非直接调API - 4. **`confirmStopAdvice`** — 新增确认函数,将 `stopTime` 拼入请求参数后调用 `stopAdvice` API - ### 验证结果 - ✅ 前端 Lint 检查通过(仅1个预存的 `vue/no-dupe-keys` 警告) - ✅ 后端 Maven 编译通过(BUILD SUCCESS)
283 lines
7.5 KiB
Vue
Executable File
283 lines
7.5 KiB
Vue
Executable File
<template>
|
||
<div class="editor-container">
|
||
<div>
|
||
<el-upload
|
||
v-if="type == 'url'"
|
||
:action="uploadUrl"
|
||
:before-upload="handleBeforeUpload"
|
||
:on-success="handleUploadSuccess"
|
||
:on-error="handleUploadError"
|
||
name="file"
|
||
:show-file-list="false"
|
||
:headers="headers"
|
||
class="editor-img-uploader"
|
||
>
|
||
<i
|
||
ref="uploadRef"
|
||
class="editor-img-uploader"
|
||
/>
|
||
</el-upload>
|
||
</div>
|
||
<div class="editor">
|
||
<quill-editor
|
||
ref="quillEditorRef"
|
||
:content="content"
|
||
content-type="html"
|
||
:options="options"
|
||
:style="styles"
|
||
@update:content="content = $event"
|
||
@text-change="(e) => $emit('update:modelValue', content)"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {QuillEditor} from "@vueup/vue-quill";
|
||
import "@vueup/vue-quill/dist/vue-quill.snow.css";
|
||
import {getToken} from "@/utils/auth";
|
||
import {computed, onMounted, ref, watch} from 'vue';
|
||
import modal from '@/plugins/modal';
|
||
|
||
const quillEditorRef = ref();
|
||
const uploadRef = ref();
|
||
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
||
const headers = ref({
|
||
Authorization: "Bearer " + getToken()
|
||
});
|
||
|
||
const props = defineProps({
|
||
/* 编辑器的内容 */
|
||
modelValue: {
|
||
type: String,
|
||
},
|
||
/* 高度 */
|
||
height: {
|
||
type: Number,
|
||
default: null,
|
||
},
|
||
/* 最小高度 */
|
||
minHeight: {
|
||
type: Number,
|
||
default: null,
|
||
},
|
||
/* 只读 */
|
||
readOnly: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
/* 上传文件大小限制(MB) */
|
||
fileSize: {
|
||
type: Number,
|
||
default: 5,
|
||
},
|
||
/* 类型(base64格式、url格式) */
|
||
type: {
|
||
type: String,
|
||
default: "url",
|
||
}
|
||
});
|
||
|
||
const options = ref({
|
||
theme: "snow",
|
||
bounds: document.body,
|
||
debug: "warn",
|
||
modules: {
|
||
// 工具栏配置
|
||
toolbar: [
|
||
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
|
||
["blockquote", "code-block"], // 引用 代码块
|
||
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
|
||
[{ indent: "-1" }, { indent: "+1" }], // 缩进
|
||
[{ size: ["small", false, "large", "huge"] }], // 字体大小
|
||
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
|
||
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
|
||
[{ align: [] }], // 对齐方式
|
||
["clean"], // 清除文本格式
|
||
["link", "image", "video"] // 链接、图片、视频
|
||
],
|
||
},
|
||
placeholder: "请输入内容",
|
||
readOnly: props.readOnly
|
||
});
|
||
|
||
const styles = computed(() => {
|
||
let style = {};
|
||
if (props.minHeight) {
|
||
style.minHeight = `${props.minHeight}px`;
|
||
}
|
||
if (props.height) {
|
||
style.height = `${props.height}px`;
|
||
}
|
||
return style;
|
||
});
|
||
|
||
const content = ref("");
|
||
watch(() => props.modelValue, (v) => {
|
||
if (v !== content.value) {
|
||
content.value = v === undefined ? "<p></p>" : v;
|
||
}
|
||
}, { immediate: true });
|
||
|
||
// 如果设置了上传地址则自定义图片上传事件
|
||
onMounted(() => {
|
||
if (props.type == 'url') {
|
||
let quill = quillEditorRef.value?.getQuill();
|
||
if (quill) {
|
||
let toolbar = quill.getModule("toolbar");
|
||
toolbar.addHandler("image", (value) => {
|
||
if (value && uploadRef.value) {
|
||
uploadRef.value.click();
|
||
} else {
|
||
quill.format("image", false);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
// 上传前校检格式和大小
|
||
function handleBeforeUpload(file) {
|
||
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg+xml"];
|
||
const isJPG = type.includes(file.type);
|
||
//检验文件格式
|
||
if (!isJPG) {
|
||
modal.msgError(`图片格式错误!`);
|
||
return false;
|
||
}
|
||
// 校检文件大小
|
||
if (props.fileSize) {
|
||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||
if (!isLt) {
|
||
modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// 上传成功处理
|
||
function handleUploadSuccess(res, file) {
|
||
// 如果上传成功
|
||
if (res.code == 200) {
|
||
// 获取富文本实例
|
||
let quill = quillEditorRef.value?.getQuill();
|
||
if (quill) {
|
||
// 获取光标位置
|
||
let range = quill.selection?.savedRange || quill.getSelection();
|
||
let length = range?.index || 0;
|
||
// 插入图片,res.url为服务器返回的图片链接地址
|
||
quill.insertEmbed(length, "image", import.meta.env.VITE_APP_BASE_API + res.fileName);
|
||
// 调整光标到最后
|
||
quill.setSelection(length + 1);
|
||
}
|
||
} else {
|
||
modal.msgError("图片插入失败");
|
||
}
|
||
}
|
||
|
||
// 上传失败处理
|
||
function handleUploadError() {
|
||
modal.msgError("图片插入失败");
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.editor-img-uploader {
|
||
display: none;
|
||
}
|
||
|
||
.editor,
|
||
.ql-toolbar {
|
||
white-space: pre-wrap !important;
|
||
line-height: normal !important;
|
||
}
|
||
|
||
.quill-img {
|
||
display: none;
|
||
}
|
||
|
||
.ql-snow .ql-tooltip[data-mode="link"]::before {
|
||
content: "请输入链接地址:";
|
||
}
|
||
|
||
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
||
border-right: 0px;
|
||
content: "保存";
|
||
padding-right: 0px;
|
||
}
|
||
|
||
.ql-snow .ql-tooltip[data-mode="video"]::before {
|
||
content: "请输入视频地址:";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||
content: "14px";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
|
||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
|
||
content: "10px";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
|
||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
|
||
content: "18px";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
|
||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
|
||
content: "32px";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
||
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
||
content: "文本";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||
content: "标题1";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||
content: "标题2";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||
content: "标题3";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||
content: "标题4";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||
content: "标题5";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||
content: "标题6";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||
content: "标准字体";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
|
||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
|
||
content: "衬线字体";
|
||
}
|
||
|
||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
|
||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
|
||
content: "等宽字体";
|
||
}
|
||
</style> |