修改了编辑框1

This commit is contained in:
2025-08-13 16:41:26 +08:00
parent 2724bbcef1
commit ce5ca666f1
247 changed files with 6163 additions and 87 deletions
+759
View File
@@ -0,0 +1,759 @@
<template>
<div class="uebox">
<div v-if="editorError" class="editor-error">
<p>编辑器加载失败: {{ editorError }}</p>
<button @click="retryLoad">重试加载</button>
</div>
<vue-ueditor-wrap
v-model="content"
:editor-id="props.id"
:config="editorConfig"
:editorDependencies="editorDependencies"
@ready="onEditorReady"
@error="onEditorError"
/>
</div>
</template>
<script setup>
import { VueUeditorWrap } from 'vue-ueditor-wrap'
import { computed, reactive, onMounted, ref } from 'vue'
import { useUserStore } from '/@/store/modules/system/user.js'
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const'
// 解析后端API基地址(按环境变量/全局变量/本地存储,依次回退)
const resolveApiBase = () => {
const env = import.meta.env || {}
const candidates = [
env.VITE_APP_API_URL, // 使用正确的环境变量名
env.VITE_GLOB_API_URL,
env.VITE_API_BASE,
env.VITE_API_URL,
env.VITE_BASE_API,
// 可选的全局变量/本地存储回退
typeof window !== 'undefined' ? window.__API_BASE__ : '',
typeof localStorage !== 'undefined' ? localStorage.getItem('API_BASE_URL') : ''
].filter(Boolean)
const base = candidates.length > 0 ? candidates[0] : ''
return base ? base.replace(/\/+$/,'') : ''
}
const apiBase = resolveApiBase()
const uploadEndpoint = '/support/file/upload'
const resolvedServerUrl = `${apiBase}${uploadEndpoint}`
// 获取用户store实例
const userStore = useUserStore()
const props = defineProps({
// 内容
modelValue: {
type: String,
default: ''
},
// 必须得有,如果要在一个页面内写两个或以上的组件,需要用id去区分
id: {
type: String,
default: 'common_editor'
}
})
const emits = defineEmits(['update:modelValue'])
const content = computed({
get() {
return props.modelValue || ''
},
set(value) {
emits('update:modelValue', value)
return value
}
})
// 编辑器状态
const editorError = ref('')
const editorReady = ref(false)
// UEditor依赖文件配置
const editorDependencies = [
'ueditor.config.js',
'ueditor.all.js',
'lang/zh-cn/zh-cn.js'
]
const editorConfig = reactive({
// 基础路径配置 - 必须正确设置,避免路径拼接问题
UEDITOR_HOME_URL: '/UEditorPlus/',
UEDITOR_CORS_URL: '/UEditorPlus/',
// 不从服务器加载配置,使用本地配置
loadConfigFromServer: false,
// 使用项目中的文件上传接口(按环境解析,直接包含folder参数)
serverUrl: `${resolvedServerUrl}?folder=${FILE_FOLDER_TYPE_ENUM.ARTICLE.value}`,
serverHeaders: computed(() => ({
'Authorization': 'Bearer ' + userStore.getToken
})),
// 编辑器基本配置
initialFrameHeight: 500,
initialFrameWidth: '100%',
autoHeightEnabled: false,
catchRemoteImageEnable: false,
// 编辑器功能配置
enableAutoSave: true,
autoSaveInterval: 60000, // 60秒自动保存
enableContextMenu: true,
// 图片上传配置 - 简化配置
imageActionName: 'uploadimage',
imageFieldName: 'file',
imageMaxSize: 2048000, // 2MB
imageAllowFiles: ['.png', '.jpg', '.jpeg', '.gif', '.bmp'],
imageCompressEnable: false, // 禁用图片压缩,避免webuploader错误
imageCompressBorder: 1600,
imageInsertAlign: 'none',
imageUrlPrefix: '',
// 视频上传配置
videoActionName: 'uploadvideo',
videoFieldName: 'file',
videoMaxSize: 102400000, // 100MB
videoAllowFiles: ['.flv', '.swf', '.mkv', '.avi', '.rm', '.rmvb', '.mpeg', '.mpg', '.ogg', '.ogv', '.mov', '.wmv', '.mp4', '.webm', '.wav', '.mid'],
videoUrlPrefix: '',
// 附件上传配置
fileActionName: 'uploadfile',
fileFieldName: 'file',
fileMaxSize: 51200000, // 50MB
fileAllowFiles: ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.flv', '.swf', '.mkv', '.avi', '.rm', '.rmvb', '.mpeg', '.mpg', '.ogg', '.ogv', '.mov', '.wmv', '.mp4', '.webm', '.wav', '.mid', '.rar', '.zip', '.tar', '.gz', '.7z', '.bz2', '.cab', '.iso', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf', '.txt', '.md', '.xml'],
fileUrlPrefix: '',
// 完整的工具栏配置
toolbars:[
[
"fullscreen", // 全屏
"source", // 源代码
"|",
"undo", // 撤销
"redo", // 重做
"|",
"bold", // 加粗
"italic", // 斜体
"underline", // 下划线
"fontborder", // 字符边框
"strikethrough",// 删除线
"superscript", // 上标
"subscript", // 下标
"removeformat", // 清除格式
"formatmatch", // 格式刷
"autotypeset", // 自动排版
"blockquote", // 引用
"pasteplain", // 纯文本粘贴模式
"|",
"forecolor", // 字体颜色
"backcolor", // 背景色
"insertorderedlist", // 有序列表
"insertunorderedlist", // 无序列表
"selectall", // 全选
"cleardoc", // 清空文档
"|",
"rowspacingtop",// 段前距
"rowspacingbottom", // 段后距
"lineheight", // 行间距
"|",
"customstyle", // 自定义标题
"paragraph", // 段落格式
"fontfamily", // 字体
"fontsize", // 字号
"|",
"directionalityltr", // 从左向右输入
"directionalityrtl", // 从右向左输入
"indent", // 首行缩进
"|",
"justifyleft", // 居左对齐
"justifycenter", // 居中对齐
"justifyright",
"justifyjustify", // 两端对齐
"|",
"touppercase", // 字母大写
"tolowercase", // 字母小写
"|",
"link", // 超链接
"unlink", // 取消链接
"anchor", // 锚点
"|",
"imagenone", // 图片默认
"imageleft", // 图片左浮动
"imageright", // 图片右浮动
"imagecenter", // 图片居中
"|",
"simpleupload", // 单图上传
"insertimage", // 多图上传
"emotion", // 表情
"scrawl", // 涂鸦
"insertvideo", // 视频
"attachment", // 附件
"insertframe", // 插入Iframe
"insertcode", // 插入代码
"pagebreak", // 分页
"template", // 模板
"background", // 背景
"formula", // 公式
"|",
"horizontal", // 分隔线
"date", // 日期
"time", // 时间
"spechars", // 特殊字符
"wordimage", // Word图片转存
"|",
"inserttable", // 插入表格
"deletetable", // 删除表格
"insertparagraphbeforetable", // 表格前插入行
"insertrow", // 前插入行
"deleterow", // 删除行
"insertcol", // 前插入列
"deletecol", // 删除列
"mergecells", // 合并多个单元格
"mergeright", // 右合并单元格
"mergedown", // 下合并单元格
"splittocells", // 完全拆分单元格
"splittorows", // 拆分成行
"splittocols", // 拆分成列
"contentimport", // 内容导入(支持Word、Markdown
"|",
"print", // 打印
"preview", // 预览
"searchreplace", // 查询替换
"help", // 帮助
]
],
// 图片上传配置
imageConfig: {
disableUpload: false,
disableOnline: false,
selectCallback: null
},
// 视频上传配置
videoConfig: {
disableUpload: false,
selectCallback: null
},
// 附件上传配置
attachmentConfig: {
disableUpload: false,
selectCallback: null
},
// 其他配置
debug: false,
autoSaveEnable: true,
autoSaveRestore: false,
maximumWords: 10000,
maxUndoCount: 20,
minFrameHeight: 220,
autoFloatEnabled: false,
topOffset: 0,
toolbarTopOffset: 0,
// 文件处理配置 - 避免webuploader错误
enableContextMenu: true,
catchRemoteImageEnable: false,
autoHeightEnabled: false,
// 错误处理
tipError: function(message, title) {
console.error('UEditor Error:', message);
editorError.value = message;
}
})
// 编辑器就绪事件
const onEditorReady = (editorInstance) => {
console.log('UEditor已就绪:', editorInstance);
editorReady.value = true;
editorError.value = '';
// 确保编辑器可编辑
if (editorInstance && editorInstance.setEnabled) {
try {
editorInstance.setEnabled(true);
} catch (error) {
console.error('启用编辑器失败:', error);
}
}
// 设置初始内容
if (content.value) {
try {
editorInstance.setContent(content.value);
} catch (error) {
console.error('设置初始内容失败:', error);
}
}
// 监听图片上传完成事件
if (editorInstance && editorInstance.addListener) {
try {
// 监听图片上传完成
editorInstance.addListener('afterimagepaste', function(type, data) {
console.log('图片粘贴完成:', type, data);
});
editorInstance.addListener('afterimageinsert', function(type, data) {
console.log('图片插入完成:', type, data);
});
// 尝试监听上传完成事件
editorInstance.addListener('afterimageupload', function(type, data) {
console.log('图片上传完成:', type, data);
});
// 监听内容变化,检查是否有图片插入
editorInstance.addListener('contentchange', function() {
console.log('编辑器内容变化');
const content = editorInstance.getContent();
console.log('当前内容:', content);
// 检查内容中是否包含图片
if (content.includes('<img')) {
console.log('检测到图片标签');
// 检查是否有undefined的图片src,如果有则尝试修复
if (content.includes('src="undefined"')) {
console.log('检测到undefined图片src,尝试修复');
// 延迟执行,等待图片上传完成
setTimeout(() => {
// 这里可以尝试从其他地方获取正确的图片URL
console.log('尝试修复undefined图片src');
}, 2000);
}
}
});
} catch (error) {
console.error('添加事件监听器失败:', error);
}
}
// 尝试重写图片上传成功的处理
if (editorInstance && editorInstance.execCommand) {
try {
// 保存原始的execCommand方法
const originalExecCommand = editorInstance.execCommand;
// 重写execCommand方法,拦截图片插入
editorInstance.execCommand = function(command, ...args) {
console.log('执行命令:', command, args);
if (command === 'inserthtml' && args[0] && args[0].includes('<img')) {
console.log('检测到图片插入命令:', args[0]);
}
// 调用原始方法
return originalExecCommand.call(this, command, ...args);
};
console.log('已重写execCommand方法');
} catch (error) {
console.error('重写execCommand失败:', error);
}
}
}
// 编辑器错误事件
const onEditorError = (error) => {
console.error('UEditor错误:', error);
editorError.value = error.message || '编辑器加载失败';
editorReady.value = false;
}
// 重试加载
const retryLoad = () => {
editorError.value = '';
editorReady.value = false;
// 强制重新渲染组件
location.reload();
}
// 组件挂载后的处理
onMounted(() => {
// 在UEditor加载前设置全局配置,避免路径问题
window.UEDITOR_HOME_URL = '/UEditorPlus/';
window.UEDITOR_CORS_URL = '/UEditorPlus/';
// 设置UEditor的全局上传成功回调
window.UEDITOR_CONFIG = window.UEDITOR_CONFIG || {};
// 图片上传成功后的处理 - 关键配置
window.UEDITOR_CONFIG.onImageUploadSuccess = function(result) {
console.log('=== 图片上传成功回调开始 ===');
console.log('原始返回数据:', result);
console.log('数据类型:', typeof result);
console.log('是否为字符串:', typeof result === 'string');
let parsedResult;
try {
// 如果result是字符串,尝试解析JSON
if (typeof result === 'string') {
parsedResult = JSON.parse(result);
} else {
parsedResult = result;
}
console.log('解析后的数据:', parsedResult);
} catch (e) {
console.error('JSON解析失败:', e);
parsedResult = result;
}
// 确保返回正确的数据格式
if (parsedResult && parsedResult.data && parsedResult.data.fileUrl) {
const returnData = {
url: parsedResult.data.fileUrl,
title: parsedResult.data.fileName || '图片',
alt: parsedResult.data.fileName || '图片',
state: 'SUCCESS'
};
console.log('返回给UEditor的数据:', returnData);
return returnData;
}
// 如果数据格式不对,返回错误状态
console.error('数据格式错误,无法提取fileUrl');
console.log('完整数据:', parsedResult);
return {
state: 'ERROR',
message: '上传失败:数据格式错误'
};
};
// 视频上传成功后的处理
window.UEDITOR_CONFIG.onVideoUploadSuccess = function(result) {
console.log('视频上传成功回调:', result);
if (result && result.data && result.data.fileUrl) {
return {
url: result.data.fileUrl,
title: result.data.fileName,
state: 'SUCCESS'
};
}
return {
state: 'ERROR',
message: '上传失败:数据格式错误'
};
};
// 附件上传成功后的处理
window.UEDITOR_CONFIG.onFileUploadSuccess = function(result) {
console.log('附件上传成功回调:', result);
if (result && result.data && result.data.fileUrl) {
return {
url: result.data.fileUrl,
title: result.data.fileName,
state: 'SUCCESS'
};
}
return {
state: 'ERROR',
message: '上传失败:数据格式错误'
};
};
// 检查依赖文件是否存在
editorDependencies.forEach(dep => {
fetch(dep)
.then(response => {
if (!response.ok) {
console.error(`依赖文件加载失败: ${dep}`);
editorError.value = `依赖文件加载失败: ${dep}`;
}
})
.catch(error => {
console.error(`依赖文件请求失败: ${dep}`, error);
editorError.value = `依赖文件请求失败: ${dep}`;
});
});
// 延迟强制刷新编辑器配置
setTimeout(() => {
if (window.UE && window.UEDITOR_CONFIG) {
// 强制覆盖配置
Object.assign(window.UEDITOR_CONFIG, editorConfig);
// 确保上传成功回调被正确设置
if (window.UEDITOR_CONFIG.onImageUploadSuccess) {
console.log('UEditor图片上传成功回调已设置');
}
if (window.UEDITOR_CONFIG.onVideoUploadSuccess) {
console.log('UEditor视频上传成功回调已设置');
}
if (window.UEDITOR_CONFIG.onFileUploadSuccess) {
console.log('UEditor附件上传成功回调已设置');
}
// 尝试监听UEditor的图片上传完成事件
if (window.UE && window.UE.getEditor) {
try {
const editor = window.UE.getEditor(props.id);
if (editor && editor.addListener) {
editor.addListener('afterimagepaste', function(type, data) {
console.log('图片粘贴事件:', type, data);
});
editor.addListener('afterimageinsert', function(type, data) {
console.log('图片插入事件:', type, data);
});
editor.addListener('afterimageupload', function(type, data) {
console.log('图片上传事件:', type, data);
});
}
} catch (e) {
console.log('编辑器实例获取失败,可能还未初始化:', e);
}
}
// 尝试重写UEditor的图片上传处理逻辑
if (window.UE && window.UE.getEditor) {
try {
const editor = window.UE.getEditor(props.id);
if (editor) {
// 重写图片上传成功后的处理
const originalInsertImage = editor.insertImage;
if (originalInsertImage) {
editor.insertImage = function(url, title, alt) {
console.log('重写的insertImage被调用:', url, title, alt);
// 如果URL是undefined,尝试从其他地方获取
if (!url || url === 'undefined') {
console.error('图片URL无效:', url);
return false;
}
// 调用原始方法
return originalInsertImage.call(this, url, title, alt);
};
console.log('已重写insertImage方法');
}
}
} catch (e) {
console.log('重写insertImage失败:', e);
}
}
// 尝试拦截网络请求,在图片上传成功后强制处理
if (window.fetch) {
const originalFetch = window.fetch;
window.fetch = function(url, options) {
// 如果是图片上传请求
if (url && url.includes('/support/file/upload') && options && options.method === 'POST') {
console.log('拦截到图片上传请求:', url);
return originalFetch(url, options).then(response => {
// 克隆响应以便多次读取
const clonedResponse = response.clone();
// 读取响应内容
clonedResponse.json().then(data => {
console.log('图片上传响应:', data);
// 如果上传成功,尝试强制插入图片
if (data && data.code === 0 && data.data && data.data.fileUrl) {
console.log('图片上传成功,尝试强制插入:', data.data.fileUrl);
// 延迟执行,确保编辑器已就绪
setTimeout(() => {
if (window.UE && window.UE.getEditor) {
try {
const editor = window.UE.getEditor(props.id);
if (editor && editor.execCommand) {
const imgHtml = `<img src="${data.data.fileUrl}" title="${data.data.fileName}" alt="${data.data.fileName}" />`;
console.log('插入图片HTML:', imgHtml);
editor.execCommand('inserthtml', imgHtml);
}
} catch (e) {
console.error('强制插入图片失败:', e);
}
}
}, 1000);
}
}).catch(e => {
console.error('读取响应失败:', e);
});
return response;
});
}
// 其他请求正常处理
return originalFetch(url, options);
};
console.log('已拦截fetch请求');
}
// 拦截XMLHttpRequest,这是UEditor可能使用的另一种请求方式
if (window.XMLHttpRequest) {
const originalXHROpen = window.XMLHttpRequest.prototype.open;
const originalXHRSend = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.open = function(method, url, ...args) {
this._ueditorUrl = url;
this._ueditorMethod = method;
return originalXHROpen.call(this, method, url, ...args);
};
window.XMLHttpRequest.prototype.send = function(data) {
const xhr = this;
const url = this._ueditorUrl;
const method = this._ueditorMethod;
// 如果是图片上传请求
if (url && url.includes('/support/file/upload') && method === 'POST') {
console.log('XMLHttpRequest拦截到图片上传请求:', url);
// 监听响应
xhr.addEventListener('load', function() {
if (xhr.status === 200) {
try {
const response = JSON.parse(xhr.responseText);
console.log('XMLHttpRequest图片上传响应:', response);
// 如果上传成功,尝试强制插入图片
if (response && response.code === 0 && response.data && response.data.fileUrl) {
console.log('XMLHttpRequest图片上传成功,尝试强制插入:', response.data.fileUrl);
// 延迟执行,确保编辑器已就绪
setTimeout(() => {
if (window.UE && window.UE.getEditor) {
try {
const editor = window.UE.getEditor(props.id);
if (editor && editor.execCommand) {
// 先删除所有undefined的图片
const currentContent = editor.getContent();
if (currentContent.includes('src="undefined"')) {
console.log('删除undefined图片');
editor.setContent(currentContent.replace(/<img[^>]*src="undefined"[^>]*>/g, ''));
}
// 插入正确的图片
const imgHtml = `<img src="${response.data.fileUrl}" title="${response.data.fileName}" alt="${response.data.fileName}" />`;
console.log('插入图片HTML:', imgHtml);
editor.execCommand('inserthtml', imgHtml);
}
} catch (e) {
console.error('XMLHttpRequest强制插入图片失败:', e);
}
}
}, 1000);
}
} catch (e) {
console.error('XMLHttpRequest响应解析失败:', e);
}
}
});
}
return originalXHRSend.call(this, data);
};
console.log('已拦截XMLHttpRequest');
}
}
}, 3000);
// 额外的图片上传处理 - 监听全局事件
window.addEventListener('message', function(event) {
if (event.data && event.data.type === 'ueditor-image-upload-success') {
console.log('收到图片上传成功消息:', event.data);
// 这里可以处理图片上传成功后的逻辑
}
});
})
</script>
<style scoped>
.uebox {
width: 100%;
border-radius: 6px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
position: relative;
}
.editor-error {
padding: 20px;
background-color: #fff2f0;
border: 1px solid #ffccc7;
border-radius: 6px;
margin-bottom: 10px;
text-align: center;
}
.editor-error button {
margin-top: 10px;
padding: 8px 16px;
background-color: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.editor-error button:hover {
background-color: #40a9ff;
}
/* 编辑器容器样式优化 */
.uebox :deep(.edui-editor) {
border: none !important;
width: 100% !important;
height: auto !important;
}
.uebox :deep(.edui-editor-toolbarbox) {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
border-bottom: 1px solid #e8e8e8;
width: 100% !important;
display: block !important;
}
.uebox :deep(.edui-editor-iframeholder) {
border: 1px solid #e8e8e8;
border-top: none;
width: 100% !important;
height: 500px !important;
}
.uebox :deep(.edui-editor-iframeholder iframe) {
width: 100% !important;
height: 100% !important;
pointer-events: auto !important;
}
.uebox :deep(.edui-editor-iframeholder .edui-body-container) {
pointer-events: auto !important;
width: 100% !important;
height: 100% !important;
}
/* 响应式设计 */
@media (max-width: 768px) {
.uebox {
margin: 0 -8px;
}
.uebox :deep(.edui-editor-toolbarbox) {
padding: 4px;
}
}
</style>
@@ -149,7 +149,11 @@
</a-form-item>
<a-form-item label="内容" name="articleContent" v-if="!isLinkChecked">
<SmartWangeditor ref="contentRef" :modelValue="form.articleContent" :height="300" />
<div class="editor-container">
<div class="editor-tip">请在此处编辑文章内容支持富文本格式图片上传表格等功能</div>
<UEditor v-model="form.articleContent" :id="'article_content'" />
</div>
</a-form-item>
</a-form>
@@ -218,7 +222,7 @@
import { smartSentry } from '/@/lib/smart-sentry';
import FileUpload from '/@/components/support/file-upload/index.vue';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import SmartWangeditor from '/@/components/framework/wangeditor/index.vue';
import UEditor from '/@/components/business/ueditor.vue';
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
import dayjs from 'dayjs';
@@ -332,7 +336,7 @@
// 组件ref
const formRef = ref();
const contentRef = ref();
const formDefault = {
articleTitle: undefined, //标题
@@ -747,8 +751,12 @@
try {
// 只有在非外部链接模式下才获取内容
if (!isLinkChecked.value) {
form.articleContent = contentRef.value.getHtml();
form.articleContentText = contentRef.value.getText();
// UEditor通过v-model自动同步,不需要手动获取
// 如果需要纯文本,可以从HTML中提取
if (form.articleContent) {
// 简单的HTML标签移除,提取纯文本
form.articleContentText = form.articleContent.replace(/<[^>]*>/g, '');
}
}
await formRef.value.validateFields();
save();
@@ -932,4 +940,27 @@
font-size: 12px;
color: #333;
}
.editor-container {
position: relative;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 10px;
background-color: #fafafa;
min-height: 200px; /* 确保编辑器有最小高度 */
}
.editor-tip {
position: absolute;
top: 10px;
left: 10px;
background-color: #fff;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
color: #8c8c8c;
z-index: 1;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
</style>
@@ -100,14 +100,6 @@
/>
</a-form-item>
<!-- 编辑时显示状态 -->
<a-form-item v-if="form.doctorId" label="状态" name="status">
<a-radio-group v-model:value="form.status">
<a-radio :value="1">正常</a-radio>
<a-radio :value="2">禁用</a-radio>
</a-radio-group>
</a-form-item>
<!-- 编辑时显示头像 -->
<a-form-item v-if="form.doctorId" label="头像" name="avatar">
<div v-if="form.avatar && typeof form.avatar === 'string' && form.avatar.trim()" class="avatar-display">
@@ -172,7 +164,6 @@ const formDefault = {
hospitalId: undefined,
hospitalUuid: undefined,
hospitalName: undefined,
status: 1,
avatar: undefined,
province: undefined,
city: undefined,
@@ -15,9 +15,6 @@
<a-form-item label="医院名称" class="smart-query-form-item">
<a-input style="width: 200px" v-model:value="queryForm.hospitalName" placeholder="医院名称" />
</a-form-item>
<a-form-item label="状态" class="smart-query-form-item">
<a-input style="width: 200px" v-model:value="queryForm.status" placeholder="状态" />
</a-form-item>
<a-form-item class="smart-query-form-item">
<a-button type="primary" @click="onSearch">
<template #icon>
@@ -67,15 +64,6 @@
>
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'status'">
<a-switch
:checked="text === 1"
@change="(checked) => onStatusChange(record, checked)"
:loading="record.statusLoading || false"
size="small"
/>
</template>
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button @click="showForm(record)" type="link">编辑</a-button>
@@ -142,12 +130,6 @@
ellipsis: true,
width: 120,
},
{
title: '状态',
dataIndex: 'status',
ellipsis: true,
width: 100,
},
{
title: '创建时间',
dataIndex: 'createdAt',
@@ -166,7 +148,6 @@
const queryFormState = {
keywords: undefined, //医生姓名
hospitalName: undefined, //医院名称
status: undefined, //状态
pageNum: 1,
pageSize: 10,
};
@@ -287,34 +268,4 @@
SmartLoading.hide();
}
}
// ---------------------------- 状态切换 ----------------------------
// 状态切换
async function onStatusChange(record, checked) {
try {
// 使用Vue.set确保响应式
if (!record.hasOwnProperty('statusLoading')) {
record.statusLoading = false;
}
record.statusLoading = true;
const newStatus = checked ? 1 : 2;
await caseClinicalDoctorApi.updateStatus(record.doctorId, newStatus);
// 更新状态
record.status = newStatus;
message.success('状态更新成功');
// 刷新当前行数据
await nextTick();
} catch (e) {
smartSentry.captureError(e);
// 恢复原状态
record.status = checked ? 2 : 1;
message.error('状态更新失败');
} finally {
record.statusLoading = false;
}
}
</script>
@@ -50,19 +50,15 @@
/>
</a-form-item>
<a-form-item label="内容" name="exchangeContent">
<SmartWangeditor
ref="contentRef"
:modelValue="form.exchangeContent"
:height="300"
placeholder="请输入内容"
<UEditor
v-model="form.exchangeContent"
:id="'exchange_content'"
/>
</a-form-item>
<a-form-item label="总结" name="exchangeSummary">
<SmartWangeditor
ref="summaryRef"
:modelValue="form.exchangeSummary"
:height="200"
placeholder="请输入总结"
<UEditor
v-model="form.exchangeSummary"
:id="'exchange_summary'"
/>
</a-form-item>
@@ -229,7 +225,7 @@
import { caseExchangeApi } from '/@/api/business/case-exchange/case-exchange-api';
import { caseClinicalArticleApi } from '/@/api/business/case-clinical-article/case-clinical-article-api';
import { smartSentry } from '/@/lib/smart-sentry';
import SmartWangeditor from '/@/components/framework/wangeditor/index.vue';
import UEditor from '/@/components/business/ueditor.vue';
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
import dayjs from 'dayjs';
@@ -294,15 +290,7 @@
// 加载投票信息
await loadVoteInfo(exchangeId);
// 等待DOM更新后设置富文本编辑器内容
nextTick(() => {
if (contentRef.value && result.data.exchangeContent) {
contentRef.value.setHtml(result.data.exchangeContent);
}
if (summaryRef.value && result.data.exchangeSummary) {
summaryRef.value.setHtml(result.data.exchangeSummary);
}
});
// UEditor通过v-model自动同步,不需要手动设置
}
} catch (err) {
smartSentry.captureError(err);
@@ -330,8 +318,6 @@
// 组件ref
const formRef = ref();
const contentRef = ref();
const summaryRef = ref(); // 新增总结编辑器ref
const formDefault = {
exchangeTitle: undefined, //标题
@@ -401,8 +387,7 @@
// 点击确定,验证表单
async function onSubmit() {
try {
form.exchangeContent = contentRef.value.getHtml();
form.exchangeSummary = summaryRef.value.getHtml(); // 获取总结编辑器的内容
// UEditor通过v-model自动同步,不需要手动获取
await formRef.value.validateFields();
save();
} catch (err) {
@@ -0,0 +1,86 @@
<template>
<div class="ueditor-test">
<h2>UEditor 简单测试</h2>
<div class="editor-container">
<vue-ueditor-wrap
v-model="content"
:editor-id="'test_editor'"
:config="{
UEDITOR_HOME_URL: '/UEditorPlus/',
initialFrameHeight: 300,
initialFrameWidth: '100%'
}"
/>
</div>
<div class="content-display">
<h3>内容预览</h3>
<div v-html="content"></div>
</div>
<div class="actions">
<button @click="setContent">设置内容</button>
<button @click="clearContent">清空内容</button>
<button @click="logContent">输出内容</button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { VueUeditorWrap } from 'vue-ueditor-wrap';
const content = ref('<p>这是测试内容</p>');
const setContent = () => {
content.value = '<h2>新设置的内容</h2><p>包含<strong>HTML标签</strong></p>';
};
const clearContent = () => {
content.value = '';
};
const logContent = () => {
console.log('当前内容:', content.value);
};
</script>
<style scoped>
.ueditor-test {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.editor-container {
margin: 20px 0;
border: 1px solid #ddd;
border-radius: 4px;
}
.content-display {
margin: 20px 0;
padding: 15px;
background-color: #f5f5f5;
border-radius: 4px;
}
.actions {
margin: 20px 0;
}
.actions button {
margin-right: 10px;
padding: 8px 16px;
background-color: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.actions button:hover {
background-color: #40a9ff;
}
</style>