支持vue3.0+ts 的富文本记录

前言

用过很多的富文本,还是蛮喜欢鹅毛富文本,轻量级,
一般遇到富文本的案例,都会首先想到的是quill
目前vue3.0与react分裂两极,typescript霸占一方,javascript还在坚持,vue2.0日薄西山。
本文介绍vue3.0 ts支持的富文本的简单实用和文档地址

富文本Vue-Quill-Editor

官网文档

  • 安装
    yarn add @vueup/vue-quill@beta
    
  • 使用
    import { QuillEditor } from '@vueup/vue-quill'
    import '@vueup/vue-quill/dist/vue-quill.snow.css';
    
    <QuillEditor
            ref="QuillEditorRef"
            :options="options"
            :content="textarea"
            content-type="html"
            @update:content="textChange"
          />
    
    const options = {
      debug: 'info',
      modules: {
        // toolbar: ['bold', 'italic', 'underline'],
      },
      placeholder: '批量输入/粘贴 企业名称或者域名,以“行”为分割线;例如以下:\n北京安全共识科技有限公司\n百度网讯科技有限公司',
      readOnly: false,
      theme: 'snow',
    }
    
    const textChange = (e:any) => {
      textarea.value = e
      console.log(e, '98', textarea.value);
    }
    
    	// 设置ref:便于调用quill支持的方法
    	// 比如 弹窗打开时设置内容为空
    	const userSecret = async () => {
    	  if (QuillEditorRef.value) {
    	    QuillEditorRef.value.setText('')
    	  }
    	};
    
    注解:
    @update:content 内容改变事件
    options:配置项
    设置ref:便于调用quill支持的方法
    更多详细的请参考文档
    
    效果图
    在这里插入图片描述

富文本wangeditor

官网文档

  • 效果图
    在这里插入图片描述

  • 安装

	npm install wangeditor --save-dev
  • 封装
	<template>
     <div>
       <div className="shop">
         <div className="text-area">
           <div
             ref="editorElemMenu"
   
             style="backgroundColor: '#f1f1f1'; border:'1px solid #ccc'"
   
             className="editorElem-menu"
           />
           <div
             ref="editorElemBody"
             :className="value&&value!=='<p><br></p>'?'contentClass editorElem-body':'placeholderClass editorElem-body'"
             style="height: 300;border: '1px solid #ccc',borderTop: 'none'"
           />
         </div>
       </div>
     </div>
   </template>
   <script lang="ts" setup>
   import {
     onMounted, reactive, toRefs, defineComponent, ref,
   } from 'vue'
   
   import E from 'wangeditor'
   
   interface DataProps {
   	editor: any;
   	editorContent: string
   	getContent:(ref?: any) => void
   }
   const props = defineProps({
     options: {
       type: Object,
       default: () => ({}),
     },
     value: {
       type: String,
       default: '',
     },
   });
   const editorElemMenu = ref();
   const editorElemBody = ref();
   const data: DataProps = reactive({
     editorContent: '',
     editor: {},
     getContent: () => {
       data.editor.txt.html(props.value)
     },
   })
   const emit = defineEmits(['onEditor', 'update:value']);
   onMounted(() => {
     const elemMenu = editorElemMenu.value;
     const elemBody = editorElemBody.value;
     data.editor = new E(elemMenu, elemBody)
     // 使用 onchange 函数监听内容的变化,并实时更新到 state 中
     data.editor.config.placeholder = '批量输入/粘贴 企业名称或者域名,以“行”为分割线;例如以下:<br/>北京安全共识科技有限公司<br/>百度网讯科技有限公司<br/>百度移信网络技术(北京)有限公司<br/>百度时代网络技术(北京)有限公司<br/>中电国佳(北京)投资基金管理有限公司<br/>北京安全共识科技有限公司<br/>百度网讯科技有限公司<br/>百度移信网络技术(北京)有限公司<br/>百度时代网络技术(北京)有限公司<br/>中电国佳(北京)投资基金管理有限公司',
     data.editor.config.onchange = () => {
       console.log(data.editor.txt.html())
       data.editorContent = data.editor.txt.html()
       // 向外部返回一个处理过的值
       emit('onEditor', data.editor.txt.html())
       emit('update:value', data.editor.txt.html())
     }
     data.editor.config.customUploadImg = function (files: any, insert: any) {
       // files 是 input 中选中的文件列表insert 是获取图片 url 后,插入到编辑器的方法
       // let file;
       // if (files && files.length) {
       //     file = files[0];
       // } else {
       //     return
       // }
       // 图片上传
       console.log('files1', files)
       const formData = new FormData();
       formData.append('file', files[0]);
       console.log('files', files, insert)
       // formData.append('Banners',{id:editorinfo.id,naviChildId:editorinfo.naviChildId})
     }

     data.editor.config.menus = [
       'head', // 标题
       'bold', // 粗体
       'fontSize', // 字号
       'fontName', // 字体
       'italic', // 斜体
       'underline', // 下划线
       'strikeThrough', // 删除线
       'foreColor', // 文字颜色
       'backColor', // 背景颜色
       'link', // 插入链接
       'list', // 列表
       'justify', // 对齐方式
       'quote', // 引用
       'emoticon', // 表情
       'image', // 插入图片
       'table', // 表格
       'video', // 插入视频
       'code', // 插入代码
       'undo', // 撤销
       'redo', // 重复
     ]
     data.editor.config.uploadImgShowBase64 = true
     data.editor.create()
   
     // data.getContent()
   })
   const refData = toRefs(data);
   </script>
   <style scoped lang="scss">
   :deep(.w-e-text-container){
       min-height: 349px;
       max-height: 449px;
       overflow-y: auto;
       line-height: 32px;
       &:focus{
           outline: 0;
           box-shadow: 0 0 0 1px rgba(0, 88, 240, 1) inset;
       }
       &:active{
           outline: 0;
           box-shadow: 0 0 0 1px rgba(0, 88, 240, 1) inset;
       }
       &:visited{
           outline: 0;
           box-shadow: 0 0 0 1px rgba(0, 88, 240, 1) inset;
       }
       &::-webkit-scrollbar {/*滚动条整体样式*/
           width:0px;/*高宽分别对应横竖滚动条的尺寸*/
       }
       .placeholder{
           line-height: 32px;
           left: 16px;
           top: 16px;
           width: calc(100% - 16px);
       }
       .w-e-text{
           max-height: 449px;
           line-height: 32px;
           padding: 16px 16px;
           &::-webkit-scrollbar {/*滚动条整体样式*/
             width:0px;/*高宽分别对应横竖滚动条的尺寸*/
           }
           p{
               line-height: 32px;
               margin: 0px;
           }
       }
   }
   .placeholderClass{
         :deep(.w-e-text-container){
           background: #F2F3F5;
           border-radius: 2px;
           color: #D1D5DD;
           &:hover{
             outline: 0;
             box-shadow: none;
             // box-shadow: 0 0 0 1px #F2F3F5 inset;
           }
           &:focus{
             outline: 0;
             box-shadow: 0 0 0 1px rgba(0, 88, 240, 1) inset;
           }
         }
       }
       .contentClass{
         :deep(.w-e-text-container){
           background: #fff;
           border-radius: 2px;
           color: #171B23;
           box-shadow: 0 0 0 1px rgba(0, 88, 240, 1) inset;
           &:hover{
             outline: 0;
             box-shadow: none;
             box-shadow: 0 0 0 1px rgba(0, 88, 240, 1) inset;
           }
           &:focus{
             outline: 0;
             box-shadow: 0 0 0 1px rgba(0, 88, 240, 1) inset;
           }
         }
       }
   </style>
  • 使用
  <Editor
    ref="refEditor"
     :value="textarea"
     @onEditor="onEditor"
   />
   // 导入     
   import Editor from './Editor/wangEditor.vue'
   const textarea = ref<any>('')
   const refEditor = ref<any>();
   const onEditor = (value:any) => {
     console.log('父组件', value)
     textarea.value = value
   }

富文本数据处理

// 针对纯文本 提取里面的文字 push到data中
// <p>123</p>
let data = []
if(val !== undefined){
	// 段落文本
	val.replace(/<p>(([\s\S])*?)<\/p>/g, (match, capture) => {
	  data.push( capture );
	});
	// 图片
	val.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/g, (match, capture) => {
	  data.push({
		files_url:capture,
		});
	});
}
多个标签,复杂的属性
// 匹配标签(不包括内容),过滤标签,保留内容。
var reg1 = /(<\/?font.*?>)/gi;
var reg2 = /(<\/?span.*?>)/gi;
var reg3 = /(<\/?a.*?>)/gi;
var reg4= /(<\/?font.*?>)|(<\/?span.*?>)|(<\/?a.*?>)/gi;

var str = '<span>span数据</span><a href="asdasd">a数据<font>font数据</font></a>';
console.log(str.replace(reg1, ''));
console.log(str.replace(reg2, ''));
console.log(str.replace(reg3, ''));
console.log(str.replace(reg4, ''));


// 只去掉标签内的属性(比如style和class)
var reg1 = /<span[^>]*>/gi;
var str1 = '<span style="color:red;">去除样式属性</span>123123';
console.log(str1.replace(reg1, '<span>'));
<think>好的,用户想在Vue 3.0和Ant Design Vue的<a-table>组件中实现自适应列宽,根据列标题或内容调整宽度。我需要结合提供的引用资料来找出解决方法。 首先,引用[2]提到在a-table中使用scroll属性设置{x:&#39;max-content&#39;}可以让水平滚动适应内容宽度,同时保持其他列固定宽度。这可能是一个关键点。但用户想要的是自适应,可能需要动态计算列宽。 然后,引用[1]中提到一个npm包aotu-layout-col,用于自动调整列宽。虽然这个包是针对el-table的,但或许类似的思路可以应用到a-table上,比如手动计算每列的宽度。 接下来,用户可能需要监听数据变化或内容变化,动态调整列宽。例如,在updated生命周期钩子中执行计算函数,获取表头的DOM元素,测量文字宽度,然后设置对应的列宽。 需要注意的是,Ant Design Vue的表格列配置是通过columns数组定义的,每个列对象可以设置width属性。因此,动态修改columns中的width值应该可以触发重新渲染,调整列宽。 另外,引用[3]和[4]提到了高度自适应的内容,但用户的问题是关于宽度自适应,所以可能需要忽略高度相关的部分,专注于宽度调整的方法。 可能的步骤包括:使用scroll设置最大内容宽度,监听表格内容变化,计算每列最大宽度(包括标题和内容),然后更新columns配置的width值。同时,可能需要处理表格容器的响应式布局,比如使用CSS的overflow-auto或max-width来允许滚动。 还需要考虑性能问题,比如防抖处理,避免频繁计算影响性能。此外,对于包含大量数据的表格,动态计算可能会比较耗时,需要优化计算方法,比如只计算可见区域的内容。 总结可能的解决方案:结合使用scroll设置、动态计算列宽并更新columns配置,可能借助第三方库或自定义函数来实现测量文本宽度,最后应用样式和配置使表格列自适应内容。</think>以下是在Vue 3.0与Ant Design Vue的$<a-table>$组件中实现自适应列宽的解决方案: ### 一、基础实现方法 1. **响应式容器设置** ```vue <template> <div class="table-container"> <a-table :columns="columns" :data-source="data" :scroll="{ x: &#39;max-content&#39; }" /> </div> </template> <style> .table-container { overflow-x: auto; max-width: 100vw; } </style> ``` 通过设置$scroll="{ x: &#39;max-content&#39; }$"使表格水平滚动条适应内容宽度[^2],结合容器CSS实现响应式布局 2. **动态列宽计算** ```javascript const calculateColumnWidth = (dataIndex, title) => { const contentWidths = data.map(item => measureTextWidth(item[dataIndex]) ); const titleWidth = measureTextWidth(title); return Math.max(...contentWidths, titleWidth) + 32; // 增加32px留白 }; const measureTextWidth = (text) => { const span = document.createElement(&#39;span&#39;); span.style.visibility = &#39;hidden&#39;; span.style.whiteSpace = &#39;nowrap&#39;; span.innerText = text; document.body.appendChild(span); const width = span.offsetWidth; document.body.removeChild(span); return width; }; ``` ### 二、进阶优化方案 1. **自动布局指令(基于引用[1]原理)** ```javascript import { createApp } from &#39;vue&#39; const autoWidth = { updated(el, { value }) { const table = el.querySelector(&#39;.ant-table&#39;) const headers = table.querySelectorAll(&#39;th&#39;) headers.forEach((header, index) => { const cells = table.querySelectorAll(`td:nth-child(${index + 1})`) const contentWidths = [...cells].map(cell => cell.scrollWidth ) const headerWidth = header.scrollWidth header.style.minWidth = `${Math.max(...contentWidths, headerWidth)}px` }) } } // 全局注册 createApp().directive(&#39;auto-width&#39;, autoWidth) // 组件使用 <template> <a-table v-auto-width /> </template> ``` 2. **性能优化方案** ```javascript // 使用ResizeObserver监听容器变化 const observer = new ResizeObserver(entries => { entries.forEach(entry => { if (entry.contentRect.width !== prevWidth) { calculateColumnsWidth() } }) }) onMounted(() => { observer.observe(document.querySelector(&#39;.table-container&#39;)) }) onUnmounted(() => { observer.disconnect() }) // 添加防抖处理 const debouncedCalculate = _.debounce(calculateColumnsWidth, 300) ``` ### 三、混合方案实践 ```vue <template> <a-table :columns="dynamicColumns" :scroll="{ x: &#39;max-content&#39; }" :components="{ header: { cell: ResizableHeader } }" /> </template> <script> // 实现可拖拽调整列宽 const ResizableHeader = { // 拖拽处理逻辑... } // 响应式columns配置 const dynamicColumns = computed(() => { return columns.map(col => ({ ...col, width: calculateColumnWidth(col.dataIndex, col.title) })) }) </script> ``` ### 四、注意事项 1. 对于大数据量表格,建议使用虚拟滚动技术 2. 固定列与自适应列并存时,需明确指定固定列宽度 3. 复杂表头建议采用手动指定$width$与自动计算结合的方式 4. 表格内容动态更新时需触发重新计算: ```javascript watch(data, () => { nextTick(() => { calculateColumnsWidth() }) }) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值