feat(web-admin): TinyMCE 表格类 Excel 调宽调高,并优化出入库/分装/送货单排版

支持合同、报价与入库/分装/送(提)货单在富文本中拖拽调整列宽行高;单据侧加宽物料编号与名称、收窄规格单位、放大 logo、加宽条款区,并弱化调整手柄绿色阴影。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-07-25 18:02:22 +08:00
1 parent 2380b01a4f
commit 3d5866aa14
13 files changed
+31815 -30418

No files matched your search

@@ -10,6 +10,7 @@ import Editor from '@tinymce/tinymce-vue';
import { fetchUploadAttachment } from '@/api/admin';
import { resolveCrmAttachmentUrl } from '@/utils/crm-attachment';
import { buildAttachmentUploadFormData, CRM_UPLOAD_BIZ_TYPE } from '@/utils/crm-upload';
import { setupExcelLikeTableResize } from '@/utils/tinymce-table-excel-resize';
/** 须高于 ElConfigProvider 初始 z-index(3000) 及多层 el-dialog 叠加 */
const TINYMCE_Z_INDEX_FLOOR = 10000;
@@ -180,12 +181,13 @@ const editorInit = computed(() => {
]
: [],
// 工具栏 - 简化并突出表格功能,添加盖章按钮
// 工具栏:突出表格排版(合并/拆分、单元格属性、对齐、换行)+ 盖章
toolbar: props.toolbar
? 'undo redo | ' +
'bold italic underline | ' +
'alignleft aligncenter alignright | ' +
'table | ' +
'table tableprops tablecellprops tablemergecells tablesplitcells | ' +
'tablecellhalign tablecellvalign tablecellwrap | ' +
'bullist numlist | ' +
'link image | ' +
'sealMode cancelSeal | ' +
@@ -206,24 +208,41 @@ const editorInit = computed(() => {
width: 100%;
}
${backgroundStyle}
/* 确保所有内容在水印上方 */
body > * {
/* 确保内容在水印上方;排除 snooker / Excel 调整层 */
body > *:not(.ephox-snooker-resizer-bar):not(#sx-excel-resize-layer) {
position: relative;
z-index: 1;
}
/* 表格:固定布局;不抢占内联宽高/对齐/边框 */
table {
border-collapse: collapse !important;
border-collapse: collapse;
width: 100%;
max-width: 100%;
margin-bottom: 20px;
box-sizing: border-box;
}
table td, table th {
border: 1px solid #ddd !important;
padding: ${props.tableCellPadding} !important;
border: 1px solid #ddd;
padding: ${props.tableCellPadding};
word-wrap: break-word;
overflow-wrap: break-word;
white-space: normal;
vertical-align: middle;
cursor: inherit;
/* 与 getBoundingClientRect 量到的边框盒宽度一致,避免快照写回时每列被放大 */
box-sizing: border-box;
}
table th {
background-color: #f5f5f5 !important;
font-weight: bold !important;
text-align: center !important;
background-color: #f5f5f5;
font-weight: bold;
}
/* 原生调整条仅作可视提示,勿改 width/height/margin(会破坏拖拽几何) */
.ephox-snooker-resizer-bar {
background-color: #10b981 !important;
}
.ephox-snooker-resizer-bar:hover,
.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {
opacity: 0.9 !important;
}
h1 {
text-align: center;
@@ -269,12 +288,14 @@ const editorInit = computed(() => {
extended_valid_elements: '*[*]',
valid_elements: '*[*]',
// 表格配置
// 表格配置:像素列宽 + 拖拽条,表格总宽在拖列时尽量保持(更接近 Excel)
table_toolbar:
'tableprops tabledelete | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol',
'tableprops tablecellprops tabledelete | tablemergecells tablesplitcells | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol | tablecellhalign tablecellvalign tablecellwrap',
table_appearance_options: true,
table_grid: true,
table_resize_bars: true,
table_sizing_mode: 'fixed',
table_column_resizing: 'preservetable',
table_use_colgroups: true,
table_default_attributes: {
border: '1'
@@ -336,8 +357,8 @@ const editorInit = computed(() => {
image_caption: true,
image_title: true,
// 允许调整图片大小
object_resizing: true,
// 图片可拖拽缩放;表格列宽/行高改由 setupExcelLikeTableResize(类 Excel 边线拖拽)
object_resizing: 'img,figure.image,div,video,iframe',
// 允许拖拽图片
draggable_modal: false,
@@ -345,6 +366,65 @@ const editorInit = computed(() => {
// 初始化回调
setup: (editor: any) => {
props.editorSetupExtra?.(editor)
setupExcelLikeTableResize(editor)
const applyCellStyle = (style: string, value: string) => {
if (editor.queryCommandSupported('mceTableApplyCellStyle')) {
editor.execCommand('mceTableApplyCellStyle', false, { [style]: value });
}
};
// 单元格水平对齐(table 插件自带垂直对齐 tablecellvalign,水平需自建)
editor.ui.registry.addMenuButton('tablecellhalign', {
icon: 'align-center',
tooltip: '单元格水平对齐',
fetch: (callback: (items: any[]) => void) => {
callback([
{
type: 'menuitem',
text: '左对齐',
icon: 'align-left',
onAction: () => applyCellStyle('text-align', 'left')
},
{
type: 'menuitem',
text: '居中',
icon: 'align-center',
onAction: () => applyCellStyle('text-align', 'center')
},
{
type: 'menuitem',
text: '右对齐',
icon: 'align-right',
onAction: () => applyCellStyle('text-align', 'right')
}
]);
}
});
// 单元格文字换行
editor.ui.registry.addMenuButton('tablecellwrap', {
icon: 'line',
tooltip: '单元格文字换行',
fetch: (callback: (items: any[]) => void) => {
callback([
{
type: 'menuitem',
text: '自动换行',
onAction: () => {
applyCellStyle('white-space', 'normal');
applyCellStyle('word-wrap', 'break-word');
applyCellStyle('overflow-wrap', 'break-word');
}
},
{
type: 'menuitem',
text: '不换行',
onAction: () => applyCellStyle('white-space', 'nowrap')
}
]);
}
});
const refreshFloatingLayers = () => {
const z = resolveTinyMceBaseZIndex();