From 38f2a79e158eb34c9a8cd23626e58d3f1906d8c4 Mon Sep 17 00:00:00 2001 From: wangchengming <15110151257@163.com> Date: Tue, 19 Aug 2025 20:04:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/TEditor/index.vue | 410 +++++++++++++++++- src/views/labelManage/adminApproval/index.vue | 17 +- src/views/labelManage/escApproval/index.vue | 18 +- .../escTask/components/LableApply.vue | 6 +- .../escTask/components/QualificationApply.vue | 2 +- .../components/QualificationApplyForm.vue | 22 +- .../components/QualificationDetailForm.vue | 21 + .../components/QualificationReApplyForm.vue | 22 +- .../components/LableApply.vue | 7 +- .../components/QualificationApply.vue | 2 +- .../components/QualificationApplyForm.vue | 22 +- .../components/QualificationDetailForm.vue | 21 + .../components/QualificationReApplyForm.vue | 22 +- 13 files changed, 555 insertions(+), 37 deletions(-) diff --git a/src/components/TEditor/index.vue b/src/components/TEditor/index.vue index 810c5a5..c3b507a 100644 --- a/src/components/TEditor/index.vue +++ b/src/components/TEditor/index.vue @@ -42,6 +42,7 @@ import "tinymce/plugins/importcss"; //引入自定义样式的css文件 import "tinymce/plugins/accordion"; // 可折叠数据手风琴模式 import "tinymce/plugins/anchor"; //锚点 import "tinymce/plugins/fullscreen"; +// import "tinymce/plugins/fomat"; // 格式刷 import { uploadFile } from "@/api/qualification/myQualifications" const emits = defineEmits(["update:modelValue"]); @@ -78,7 +79,7 @@ const props = defineProps({ }, toolbar: { type: [String, Array, Boolean], - default: "newdocument undo redo | formatselect visualaid|cut selectall| bold italic underline strikethrough |codeformat blockformats| superscript subscript | forecolor backcolor | alignleft aligncenter alignright alignjustify | outdent indent | removeformat bullist numlist lists | image table media link |fullscreen insertdatetime visualchars visualblocks searchreplace pagebreak anchor charmap", + default: "newdocument undo redo | fontfamily fontsize | formatpainter cut selectall | bold italic underline strikethrough |codeformat blockformats| superscript subscript | forecolor backcolor | alignleft aligncenter alignright alignjustify | outdent indent | removeformat bullist numlist lists | image table media link |fullscreen insertdatetime visualchars visualblocks searchreplace pagebreak anchor charmap", // default: "undo redo | accordion accordionremove | blocks fontfamily fontsize| bold italic underline strikethrough ltr rtl | align numlist bullist | link image | table | lineheight outdent indent| forecolor backcolor removeformat | charmap emoticons | anchor codesample", }, readonly: { @@ -96,10 +97,338 @@ const props = defineProps({ }); const loading = ref(false); const tinymceId = ref( - "vue-tinymce-" + +new Date() + ((Math.random() * 1000).toFixed(0) + "") + "vue-tinymce-" + +new Date() + ((Math.random() * 1000).toFixed(0) + "") ); const baseUrl = import.meta.env.VITE_APP_BASE_API + +// 使用闭包来保持状态 +const formatPainter = (function () { + let formatPainterActive = false; + let cachedFormats = null; + let cachedStyles = null; + let cachedAttributes = null; + let cachedClasses = null; + let lastApplyTime = 0; + let nodeChangeTimeout = null; + let editorInstance = null; + + const EXCLUDED_FORMATS = ['removeformat', 'remove', 'delete', 'cut', 'paste']; + + return { + init: function (editor) { + console.log('格式刷初始化'); + editorInstance = editor; + + // 添加自定义格式刷按钮 + editor.ui.registry.addButton('formatpainter', { + icon: 'paste', + tooltip: '格式刷 - 点击激活,选择文本应用格式', + onAction: function () { + console.log('格式刷按钮点击'); + if (formatPainterActive) { + this.deactivate(editor); + } else { + this.activate(editor); + } + }.bind(this) + }); + }, + + isSafeFormat: function (formatName) { + return typeof formatName === 'string' && + formatName.trim().length > 0 && + !EXCLUDED_FORMATS.includes(formatName); + }, + + // 激活格式刷 + activate: function (editor) { + const selection = editor.selection; + + if (!selection.isCollapsed()) { + this.captureCurrentFormats(editor); + formatPainterActive = true; + + // this.updateButtonState(true); + + editor.notificationManager.open({ + text: '格式刷已激活 - 选择目标文本应用格式', + type: 'info', + timeout: 2000 + }); + + // 绑定事件处理函数 + this.applyFormatOnMouseUpBound = this.applyFormatOnMouseUp.bind(this); + this.applyFormatOnKeyUpBound = this.applyFormatOnKeyUp.bind(this); + + editor.on('mouseup', this.applyFormatOnMouseUpBound); + editor.on('keyup', this.applyFormatOnKeyUpBound); + } else { + editor.notificationManager.open({ + text: '请先选择带有格式的文本', + type: 'warning' + }); + return; + } + }, + + // 停用格式刷 + deactivate: function (editor) { + formatPainterActive = false; + + // this.updateButtonState(false); + + // 移除事件监听器 + if (this.applyFormatOnMouseUpBound) { + editor.off('mouseup', this.applyFormatOnMouseUpBound); + } + if (this.applyFormatOnKeyUpBound) { + editor.off('keyup', this.applyFormatOnKeyUpBound); + } + + // 清除超时 + if (nodeChangeTimeout) { + clearTimeout(nodeChangeTimeout); + nodeChangeTimeout = null; + } + }, + + // 捕获当前格式 - 调试版本 + captureCurrentFormats: function (editor) { + cachedFormats = []; + cachedStyles = {}; + cachedAttributes = {}; + cachedClasses = []; + + try { + const formatter = editor.formatter; + const selectedNode = editor.selection.getNode(); + + console.log('选中的节点:', selectedNode); + console.log('节点标签名:', selectedNode?.tagName); + console.log('节点类名:', selectedNode?.className); + console.log('节点样式:', selectedNode?.style?.cssText); + + // 常见的安全格式列表 + const safeCommonFormats = [ + 'bold', 'italic', 'underline', 'strikethrough', + 'superscript', 'subscript', 'forecolor', 'backcolor', + 'fontname', 'fontsize', 'alignleft', 'aligncenter', + 'alignright', 'alignjustify', 'bullist', 'numlist', + 'outdent', 'indent', 'blockquote', 'code', + 'link', 'image' + ]; + + let matchedFormats = 0; + + safeCommonFormats.forEach(formatName => { + if (this.isSafeFormat(formatName)) { + try { + const isMatched = formatter.match(formatName); + if (isMatched) { + console.log(`格式匹配: ${formatName}`); + matchedFormats++; + cachedFormats.push({ + name: formatName, + attributes: {}, + styles: {}, + classes: [] + }); + } + } catch (e) { + console.warn(`检查格式 ${formatName} 时出错:`, e); + } + } + }); + + console.log(`总共匹配了 ${matchedFormats} 种格式`); + + // 获取内联样式 + if (selectedNode && selectedNode.style) { + console.log('开始获取内联样式...'); + let styleCount = 0; + + for (let i = 0; i < selectedNode.style.length; i++) { + const prop = selectedNode.style[i]; + if (prop && selectedNode.style[prop]) { + cachedStyles[prop] = selectedNode.style[prop]; + styleCount++; + } + } + console.log(`获取了 ${styleCount} 个内联样式`); + } + + // 获取类名 + if (selectedNode && selectedNode.className) { + console.log('开始获取类名...'); + cachedClasses = selectedNode.className.split(' ') + .filter(cls => cls && cls.trim()) + .map(cls => cls.trim()); + console.log(`获取了 ${cachedClasses.length} 个类名:`, cachedClasses); + } + + // 强制输出调试信息 + console.log('=== 捕获的格式信息 ==='); + console.log('格式:', cachedFormats.map(f => f.name)); + console.log('样式:', Object.keys(cachedStyles)); + console.log('属性:', Object.keys(cachedAttributes)); + console.log('类名:', cachedClasses); + console.log('=== 格式信息输出完成 ==='); + + // 额外的调试信息 + console.log('cachedFormats 类型:', Array.isArray(cachedFormats) ? '数组' : typeof cachedFormats); + console.log('cachedFormats 长度:', cachedFormats.length); + console.log('cachedStyles 类型:', typeof cachedStyles); + console.log('cachedStyles 键数量:', Object.keys(cachedStyles).length); + + } catch (error) { + console.error('捕获格式时出错:', error); + console.error('错误堆栈:', error.stack); + } + }, + + // 鼠标释放时应用格式 + applyFormatOnMouseUp: function () { + if (formatPainterActive) { + if (nodeChangeTimeout) { + clearTimeout(nodeChangeTimeout); + } + nodeChangeTimeout = setTimeout(() => { + this.applyFormatIfNeeded(editorInstance); + }, 300); + } + }, + + // 键盘释放时应用格式 + applyFormatOnKeyUp: function () { + if (formatPainterActive) { + if (nodeChangeTimeout) { + clearTimeout(nodeChangeTimeout); + } + nodeChangeTimeout = setTimeout(() => { + this.applyFormatIfNeeded(editorInstance); + }, 200); + } + }, + + // 检查并应用格式 + applyFormatIfNeeded: function (editor) { + const now = Date.now(); + if (now - lastApplyTime < 500) { + console.log('时间间隔太短,跳过'); + return; + } + + if (formatPainterActive && !editor.selection.isCollapsed()) { + lastApplyTime = now; + + try { + setTimeout(() => { + this.applyFormats(editor); + this.deactivate(editor); + }, 100); + + } catch (error) { + console.error('应用格式时出错:', error); + this.deactivate(editor); + } + } else { + console.log('不满足应用条件:', { + active: formatPainterActive, + collapsed: editor.selection.isCollapsed() + }); + } + }, + + // 应用格式 + applyFormats: function (editor) { + if ((!cachedFormats || cachedFormats.length === 0) && + (!cachedStyles || Object.keys(cachedStyles).length === 0)) { + return; + } + + try { + const bookmark = editor.selection.getBookmark(); + const selectedNode = editor.selection.getNode(); + + console.log('应用格式数量:', cachedFormats?.length || 0); + console.log('应用样式数量:', Object.keys(cachedStyles || {}).length); + + // 应用文本格式 + if (cachedFormats && cachedFormats.length > 0) { + cachedFormats.forEach(format => { + if (this.isSafeFormat(format.name)) { + try { + editor.formatter.apply(format.name); + } catch (e) { + console.warn('应用格式失败:', format.name, e); + } + } + }); + } + + // 应用内联样式 + if (cachedStyles && selectedNode && selectedNode.style) { + Object.keys(cachedStyles).forEach(prop => { + try { + selectedNode.style[prop] = cachedStyles[prop]; + } catch (e) { + console.warn('应用样式失败:', prop, e); + } + }); + } + + editor.selection.moveToBookmark(bookmark); + + editor.notificationManager.open({ + text: '格式已成功应用', + type: 'success', + timeout: 1500 + }); + + console.log('格式应用完成'); + + } catch (error) { + console.error('应用格式时出错:', error); + } + }, + + // // 更新按钮状态 + // updateButtonState: function (isActive) { + // console.log('更新按钮状态:', isActive ? '激活' : '停用'); + // setTimeout(() => { + // try { + // const buttons = document.querySelectorAll('.tox-tbtn'); + // console.log('找到按钮数量:', buttons.length); + + // buttons.forEach((button, index) => { + // const icon = button.querySelector('.tox-icon'); + // const ariaLabel = button.getAttribute('aria-label') || ''; + + // if ((icon && icon.innerHTML.includes('paste')) || + // ariaLabel.includes('格式刷')) { + // console.log(`找到格式刷按钮 (${index}):`, ariaLabel); + + // if (isActive) { + // button.classList.add('format-painter-active'); + // button.style.boxShadow = '0 0 0 2px #2196f3'; + // console.log('按钮设置为激活状态'); + // } else { + // button.classList.remove('format-painter-active'); + // button.style.boxShadow = ''; + // console.log('按钮设置为停用状态'); + // } + // } + // }); + // } catch (error) { + // console.warn('更新按钮状态失败:', error); + // } + // }, 50); + // } + }; +})(); + + //定义一个对象 init初始化 const init = reactive({ selector: "#" + tinymceId.value, //富文本编辑器的id, @@ -133,15 +462,43 @@ const init = reactive({ editimage_toolbar: "rotateleft rotateright | flipv fliph | editimage imageoptions", // 文字样式 font_family_formats: - "Arial=arial,helvetica,sans-serif; 宋体=SimSun; 微软雅黑=Microsoft Yahei; Impact=impact,chicago;", //字体 - font_size_formats: "11px 12px 14px 16px 18px 24px 36px 48px 64px 72px", //文字大小 + // 中文字体 + '微软雅黑=Microsoft YaHei,微软雅黑,Microsoft JhengHei,sans-serif;' + + '宋体=SimSun,宋体,serif;' + + '黑体=SimHei,黑体,sans-serif;' + + '楷体=KaiTi,楷体,serif;' + + '仿宋=FangSong,仿宋,serif;' + + '新宋体=NSimSun,新宋体,serif;' + + '华文细黑=STXihei,华文细黑,sans-serif;' + + '华文楷体=STKaiti,华文楷体,serif;' + + '华文宋体=STSong,华文宋体,serif;' + + '华文仿宋=STFangsong,华文仿宋,serif;' + + // 英文字体 + 'Andale Mono=andale mono,times;' + + 'Arial=arial,helvetica,sans-serif;' + + 'Arial Black=arial black,avant garde;' + + 'Book Antiqua=book antiqua,palatino;' + + 'Comic Sans MS=comic sans ms,sans-serif;' + + 'Courier New=courier new,courier;' + + 'Georgia=georgia,palatino;' + + 'Helvetica=helvetica;' + + 'Impact=impact,chicago;' + + 'Symbol=symbol;' + + 'Tahoma=tahoma,arial,helvetica,sans-serif;' + + 'Terminal=terminal,monaco;' + + 'Times New Roman=times new roman,times;' + + 'Trebuchet MS=trebuchet ms,geneva;' + + 'Verdana=verdana,geneva;' + + 'Webdings=webdings;' + + 'Wingdings=wingdings,zapf dingbats;', //字体样式 + font_size_formats: "8px 10px 12px 14px 16px 18px 24px 36px 48px 64px 72px", //文字大小 image_caption: true, editimage_cors_hosts: ["picsum.photos"], noneditable_class: "mceNonEditable", toolbar_mode: "wrap", // 工具栏模式 floating / sliding / scrolling / wrap // 默认样式 content_style: - "body { font-family:Helvetica,Arial,sans-serif; font-size:16px }p {margin:3px; line-height:24px;}", + 'body { font-family: Microsoft YaHei, Helvetica Neue, Arial, sans-serif; font-size: 16px; }', image_advtab: true, importcss_append: true, paste_webkit_styles: "all", @@ -157,8 +514,40 @@ const init = reactive({ // autoresize_overflow_padding: 16, min_height: props.minHeight, content_css: "/tinymce/skins/content/default/content.css", //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入 - // setup: function (editor) { - // }, + setup: function (editor) { + console.log('编辑器setup开始'); + + // 初始化格式刷 + formatPainter.init(editor); + + // 添加键盘快捷键支持 + editor.addShortcut('ctrl+shift+c', '复制格式', function () { + console.log('快捷键 Ctrl+Shift+C 触发'); + if (!editor.selection.isCollapsed()) { + formatPainter.captureCurrentFormats(editor); + editor.notificationManager.open({ + text: '格式已复制', + type: 'success', + timeout: 1500 + }); + } + }); + + editor.addShortcut('ctrl+shift+v', '应用格式', function () { + console.log('快捷键 Ctrl+Shift+V 触发'); + if (!editor.selection.isCollapsed()) { + formatPainter.applyFormats(editor); + } + }); + + // 编辑器失去焦点时自动停用格式刷 + editor.on('blur', function () { + console.log('编辑器失去焦点'); + formatPainter.deactivate(editor); + }); + + console.log('编辑器setup完成'); + }, // 图片本地上传方法 点击上传后执行的事件 images_upload_handler: async (blobInfo, progress) => { return new Promise(async (resolve, reject) => { @@ -217,7 +606,7 @@ const init = reactive({ // 设置初始化内容(初始化结束后执行) init_instance_callback: function (editor) { console.log('初始化内容', props.value) - if (props.value) { + if (props.value) { editor.setContent(props.value); // 设置默认文本内容 emits('update:modelValue', editor.getContent()) //更新 } @@ -341,4 +730,9 @@ defineExpose({ .tinymce.ui.FloatPanel { z-index: 99; } + +.format-painter-active { + background-color: #e3f2fd !important; + border: 2px solid #2196f3 !important; +} \ No newline at end of file diff --git a/src/views/labelManage/adminApproval/index.vue b/src/views/labelManage/adminApproval/index.vue index 9b33c5d..1c4321b 100644 --- a/src/views/labelManage/adminApproval/index.vue +++ b/src/views/labelManage/adminApproval/index.vue @@ -88,7 +88,7 @@ 驳回 - + @@ -110,7 +113,7 @@ import { useRouter } from 'vue-router' import Breadcrumb from '@/components/Breadcrumb' import importIcon from '@/assets/images/ImportIcon.png' import { customerDeptTreeSelect, listUser } from "@/api/system/user" -import { getUserLabelPageList } from "@/api/labelManage/labelManage" +import { getUserLabelPageList, deleteBusUserLabel } from "@/api/labelManage/labelManage" const { proxy } = getCurrentInstance() const { bus_label } = proxy.useDict("bus_label") @@ -215,6 +218,16 @@ const resetQuery = () => { handleQuery() } +// 删除 +const handleDetele = (record) => { + const rowId = record.id + proxy.$modal.confirm('是否确认删除该项数据项?').then(function () { + return deleteBusUserLabel(rowId) + }).then(() => { + getBusUserLabelPageList(); + proxy.$modal.msgSuccess("删除成功") + }).catch(() => { }) +} const handleView = (record) => { router.push('/labelManage/adminApprovalDetail/' + record.id) } diff --git a/src/views/labelManage/escApproval/index.vue b/src/views/labelManage/escApproval/index.vue index 44456bc..b276321 100644 --- a/src/views/labelManage/escApproval/index.vue +++ b/src/views/labelManage/escApproval/index.vue @@ -82,7 +82,7 @@ 驳回 - + @@ -103,7 +106,7 @@ import { onMounted, ref } from 'vue' import { useRouter } from 'vue-router' import Breadcrumb from '@/components/Breadcrumb' import { customerDeptTreeSelect, listUser } from "@/api/system/user" -import { getUserLabelPageList } from "@/api/labelManage/labelManage" +import { getUserLabelPageList, deleteBusUserLabel } from "@/api/labelManage/labelManage" const { proxy } = getCurrentInstance() const { bus_label } = proxy.useDict("bus_label") @@ -207,7 +210,16 @@ const resetQuery = () => { proxy.resetForm("queryRef") handleQuery() } - +// 删除 +const handleDetele = (record) => { + const rowId = record.id + proxy.$modal.confirm('是否确认删除该项数据项?').then(function () { + return deleteBusUserLabel(rowId) + }).then(() => { + getBusUserLabelPageList(); + proxy.$modal.msgSuccess("删除成功") + }).catch(() => { }) +} const handleView = (record) => { router.push('/labelManage/labelEscApprovalDetail/' + record.id) } diff --git a/src/views/qualification/escTask/components/LableApply.vue b/src/views/qualification/escTask/components/LableApply.vue index 91df000..15e26f9 100644 --- a/src/views/qualification/escTask/components/LableApply.vue +++ b/src/views/qualification/escTask/components/LableApply.vue @@ -68,9 +68,9 @@ 查看 - - 删除 + + 删除 diff --git a/src/views/qualification/escTask/components/QualificationApply.vue b/src/views/qualification/escTask/components/QualificationApply.vue index b3be78e..f563fc7 100644 --- a/src/views/qualification/escTask/components/QualificationApply.vue +++ b/src/views/qualification/escTask/components/QualificationApply.vue @@ -72,7 +72,7 @@ 查看 - + 删除 diff --git a/src/views/qualification/escTask/components/QualificationApplyForm.vue b/src/views/qualification/escTask/components/QualificationApplyForm.vue index 87a019a..241781f 100644 --- a/src/views/qualification/escTask/components/QualificationApplyForm.vue +++ b/src/views/qualification/escTask/components/QualificationApplyForm.vue @@ -94,7 +94,7 @@ + style="width: 100%;" readonly/> @@ -126,7 +126,7 @@ + style="width: 100%;" readonly/> @@ -185,7 +185,7 @@ + style="width: 100%;" readonly/> @@ -260,7 +260,21 @@ + placeholder="请选择急救证到期日期" style="width: 60%;" readonly/> + + + + + + + + + + diff --git a/src/views/qualification/escTask/components/QualificationDetailForm.vue b/src/views/qualification/escTask/components/QualificationDetailForm.vue index 15676d1..ab8fdcb 100644 --- a/src/views/qualification/escTask/components/QualificationDetailForm.vue +++ b/src/views/qualification/escTask/components/QualificationDetailForm.vue @@ -307,6 +307,27 @@ placeholder="请选择急救证到期日期" style="width: 60%;" disabled /> + + + + + + + + + + + + + + + + style="width: 100%;" readonly/> @@ -125,7 +125,7 @@ + style="width: 100%;" readonly/> @@ -184,7 +184,7 @@ + style="width: 100%;" readonly/> @@ -259,7 +259,21 @@ + placeholder="请选择急救证到期日期" style="width: 60%;" readonly/> + + + + + + + + + + diff --git a/src/views/qualification/myQualifications/components/LableApply.vue b/src/views/qualification/myQualifications/components/LableApply.vue index 74acc87..89bccfe 100644 --- a/src/views/qualification/myQualifications/components/LableApply.vue +++ b/src/views/qualification/myQualifications/components/LableApply.vue @@ -39,9 +39,10 @@ 查看 - - 删除 + + 删除 diff --git a/src/views/qualification/myQualifications/components/QualificationApply.vue b/src/views/qualification/myQualifications/components/QualificationApply.vue index 6810f97..2d03120 100644 --- a/src/views/qualification/myQualifications/components/QualificationApply.vue +++ b/src/views/qualification/myQualifications/components/QualificationApply.vue @@ -40,7 +40,7 @@ 查看 - + 删除 diff --git a/src/views/qualification/myQualifications/components/QualificationApplyForm.vue b/src/views/qualification/myQualifications/components/QualificationApplyForm.vue index f2c19d5..0559c49 100644 --- a/src/views/qualification/myQualifications/components/QualificationApplyForm.vue +++ b/src/views/qualification/myQualifications/components/QualificationApplyForm.vue @@ -74,7 +74,7 @@ + style="width: 100%;" readonly /> @@ -105,7 +105,7 @@ + style="width: 100%;" readonly/> @@ -161,7 +161,7 @@ + style="width: 100%;" readonly/> @@ -233,9 +233,23 @@ + style="width: 100%;" readonly/> + + + + + + + + + + diff --git a/src/views/qualification/myQualifications/components/QualificationDetailForm.vue b/src/views/qualification/myQualifications/components/QualificationDetailForm.vue index 142176c..9fc6bf1 100644 --- a/src/views/qualification/myQualifications/components/QualificationDetailForm.vue +++ b/src/views/qualification/myQualifications/components/QualificationDetailForm.vue @@ -307,6 +307,27 @@ placeholder="请选择急救证到期日期" style="width: 60%;" disabled /> + + + + + + + + + + + + + + + + style="width: 100%;" readonly/> @@ -105,7 +105,7 @@ + style="width: 100%;" readonly/> @@ -161,7 +161,7 @@ + style="width: 100%;" readonly/> @@ -233,7 +233,21 @@ + style="width: 100%;" readonly/> + + + + + + + + + +