请选择 进入手机版 | 继续访问电脑版

Asher

注册会员

9

主题

15

帖子

86

积分

注册会员

积分
86
Asher
注册会员   /  发表于:2025-12-24 11:05  /   查看:99  /  回复:7
1金币
【V17】只引用@grapecity/spread-sheets-vue组件,如何在右键菜单添加富文本编辑按钮

最佳答案

查看完整内容

您好!“编辑富文本”这个菜单选项在Designer中,您使用的是spread-sheets,如果需要相同的选项和功能,需要自定义菜单选项实现,参考官网教程实现富文本编辑对话框: 富文本

7 个回复

最佳答案
最佳答案
Wilson.Zhang
超级版主   /  发表于:2025-12-24 11:05:52
来自 2#
您好!“编辑富文本”这个菜单选项在Designer中,您使用的是spread-sheets,如果需要相同的选项和功能,需要自定义菜单选项实现,参考官网教程实现富文本编辑对话框:
富文本
回复 使用道具 举报
Asher
注册会员   /  发表于:2025-12-27 14:41:36
3#
本帖最后由 Asher 于 2025-12-29 19:28 编辑
Wilson.Zhang 发表于 2025-12-24 17:45
您好!“编辑富文本”这个菜单选项在Designer中,您使用的是spread-sheets,如果需要相同的选项和功能,需 ...

使用https://docs.grapecity.com.cn/spreadjs/practice//cell/customize-cell-html的方式自定义富文本的编辑框,在回显到sheet上时单元格高度未达到预期,paint方法中获取的高度最小都是40,切wordWrap设置true也没有效果
demo代码如下:输入包含html标签的文本,比如<span>eee</span>
<template>
  <div>
    <div id="ss" style="width: 100%; height: 600px;"></div>
    <div v-if="dialogVisible" class="modal-overlay" @click.self="closeModal">
      <div class="modal-container">
        <div class="modal-header">
          <span class="modal-title">富文本编辑框 </span>
          <button class="modal-close" @click="closeModal">×</button>
        </div>
        <div class="modal-body">
          <input v-model="richValue" />
        </div>
        <div class="modal-footer">
          <button class="modal-btn" @click="handleSave">确定</button>
          <button class="modal-btn modal-btn-cancel" @click="closeModal">取消</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import GC from '@grapecity/spread-sheets';
import '@grapecity/spread-sheets/styles/gc.spread.sheets.excel2016colorful.css';
import '@grapecity/spread-sheets-designer/styles/gc.spread.sheets.designer.min.css';
import '@grapecity/spread-sheets-resources-zh';


const spread = ref();

const dialogVisible = ref(false);

const richValue = ref('');

// 处理保存逻辑
const handleSave = () => {
  const sheet = spread.value.getActiveSheet();

  sheet.suspendPaint();

  sheet.getCell(sheet.getActiveRowIndex(), sheet.getActiveColumnIndex()).cellType(new HTMLCellType());

  sheet.setValue(sheet.getActiveRowIndex(), sheet.getActiveColumnIndex(), { richText: [{ text: richValue.value }] }, GC.Spread.Sheets.SheetArea.viewport);

  sheet.getCell(sheet.getActiveRowIndex(), sheet.getActiveColumnIndex()).wordWrap(true);

  sheet.autoFitRow(sheet.getActiveRowIndex());

  sheet.resumePaint();
  closeModal();
  spread.value.focus(true);
};

// 关闭模态框
const closeModal = () => {
  dialogVisible.value = false;
  richValue.value = '';
  spread.value.focus(true);
};

// 进入单元格事件
const onSpreadEnterCell = (sender, args) => {
  const rich = args.sheet.getValue(args.row, args.col, GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.ValueType.richText);

  if (rich !== null && rich !== args.sheet.getValue(args.row, args.col)) {
    dialogVisible.value = true;
    richValue.value = rich.text;
    args.sheet.endEdit(true);
    spread.value.focus(false);
  }
};

// 初始化
onMounted(() => {
  // 初始化电子表格
  spread.value = new GC.Spread.Sheets.Workbook(document.getElementById('ss'));

  // 绑定双击事件
  spread.value.bind(GC.Spread.Sheets.Events.CellDoubleClick, onSpreadEnterCell);

  // 添加自定义富文本命令
  const commandManager = spread.value.commandManager();
  const richTextCommand = {
    text: '编辑富文本',
    name: 'richText',
    command: 'richText',
    workArea: 'viewport'
  };
  commandManager.register('richText', {
    canUndo: false,
    execute: () => {
      dialogVisible.value = true;
      spread.value.focus(false);
    }
  });
  spread.value.contextMenu.menuData.push(richTextCommand);
});

function HTMLCellType() {}
HTMLCellType.prototype = new GC.Spread.Sheets.CellTypes.Text();
HTMLCellType.prototype.paint = function(ctx, value, x, y, w, h, style, context) {
  if (!value) {
    return;
  }
  var DOMURL = window.URL || window.webkitURL || window;
  var cell = context.sheet.getCell(context.row, context.col);
  var img = cell.tag();
  if (img) {
    try {
      ctx.save();
      ctx.rect(x, y, w, h);
      ctx.clip();
      ctx.drawImage(img, x + 2, y + 2);
      ctx.restore();
      cell.tag(null);
      return;
    } catch (err) {
      GC.Spread.Sheets.CustomCellType.prototype.paint.apply(this, [ctx, '#HTMLError', x, y, w, h, style, context]);
      cell.tag(null);
      return;
    }
  }
  var svgPattern =
    '<svg xmlns="http://www.w3.org/2000/svg" width="{0}" height="{1}">' +
    '<foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="font:{2}">{3}</div></foreignObject></svg>';

  var data = svgPattern
    .replace('{0}', w)
    .replace('{1}', h)
    .replace('{2}', style.font)
    .replace('{3}', value.text);
  var doc = document.implementation.createHTMLDocument('');
  doc.write(data);
  // Get well-formed markup
  data = new XMLSerializer().serializeToString(doc.body.children[0]);

  img = new Image();
  var svg = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
  var url = DOMURL.createObjectURL(svg);
  img.src = url;
  cell.tag(img);
  img.onload = function() {
    context.sheet.repaint(new GC.Spread.Sheets.Rect(x, y, w, h));
  };
};
</script>

<style scoped>
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 999;
}

.modal-container {
  background: #fff;
  border-radius: 8px;
  padding: 16px;
  width: 500px;
  max-width: 90%;
  max-height: 90%;
  overflow: auto;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}

.modal-title {
  font-size: 18px;
  font-weight: bold;
}

.modal-close {
  background: none;
  border: none;
  font-size: 18px;
  cursor: pointer;
}

.modal-body {
  margin-bottom: 16px;
}

.modal-footer {
  text-align: center;
}

.modal-btn {
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.modal-btn {
  background: #409eff;
  color: #fff;
}

.modal-btn-cancel {
  background: #dcdfe6;
  color: #fff;
  margin-left: 16px;
}
</style>
回复 使用道具 举报
Wilson.Zhang
超级版主   /  发表于:2025-12-29 17:25:40
4#
您好!我使用这段代码点击右键菜单中的“编辑富文本”后页面上未显示编辑框,可能环境和依赖不一致,请您整理能够复现问题的可运行demo,帮您进一步分析。
回复 使用道具 举报
Asher
注册会员   /  发表于:2025-12-29 19:28:40
5#
Wilson.Zhang 发表于 2025-12-29 17:25
您好!我使用这段代码点击右键菜单中的“编辑富文本”后页面上未显示编辑框,可能环境和依赖不一致,请您整 ...

已修改demo,麻烦再帮看下
回复 使用道具 举报
Wilson.Zhang
超级版主   /  发表于:2025-12-30 12:14:01
6#
Asher 发表于 2025-12-29 19:28
已修改demo,麻烦再帮看下

富文本其实是由样式作用在普通文本上表现出来的,它包含有style和text,如下图所示结构:


您参考的这个demo,实际上设置在单元格中的是个html标签内容,如下图所示:


也就是说如果要显示文本样式效果,需要输入如下所示文本内容:
  1. <h1 style="text-decoration: line-through;">Hello SpreadJS!</h1><h3>E=mc<sup>2</sup></h3><h2><em style="color:red">I</em> like     <span style="color:white; text-shadow:0 0 2px blue;">     Javascript</span></h2><p>aaaa</p><img style="width:100px;height:100px;display:none;" src="https://img-s-msn-com.akamaized.net/tenant/amp/entityid/AA17SbZ4.img?w=612&h=304&q=90&m=6&f=jpg&u=t"/><p>aaaa</p>
复制代码

如下图所示:


您的代码里只设置了text,没有样式,如下图所示:


如果只是普通文本,直接Worksheet:setValue()就可以了。如果想要用有样式效果的富文本,同时需要有编辑框,建议您参考学习指南demo【富文本】,或者直接使用Designer。使用Designer,可以把顶部工具栏隐藏起来,只显示单元格区域。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 使用道具 举报
Asher
注册会员   /  发表于:2025-12-30 20:07:24
7#
Wilson.Zhang 发表于 2025-12-30 12:14
富文本其实是由样式作用在普通文本上表现出来的,它包含有style和text,如下图所示结构:

已解决,高度和预期不一致是因为foreignObject内的HTML元素会应用浏览器的默认样式,导致元素有了padding

评分

参与人数 1金币 +200 收起 理由
Wilson.Zhang + 200

查看全部评分

回复 使用道具 举报
Wilson.Zhang
超级版主   /  发表于:2025-12-31 08:36:21
8#
Asher 发表于 2025-12-30 20:07
已解决,高度和预期不一致是因为foreignObject内的HTML元素会应用浏览器的默认样式,导致元素有了padding

解决了就好,感谢您分享解决办法,赠送您200金币。

对本帖就结贴了,如果遇到新问题,欢迎发新帖交流。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 立即注册
返回顶部