vue 后端返回数组,前端 赋值的时候变成[{…}, ob: Observer]类型
在Vue中,当从后端接收到数组数据时,有时会遇到前端赋值后变成[{…}, ob: Observer]类型的情况。这通常是由于Vue对数据进行了观察(Observable)导致的。
Vue使用观察者模式来追踪数据的变化,这样当数据发生改变时可以及时更新视图。当你将一个普通的数组赋值给Vue组件的数据时,Vue会将其转换为响应式的数据,这就是为什么你会看到__ob__: Observer的标记。
如果你希望避免将数组转换为响应式的数据,你可以使用Object.freeze()方法来禁止Vue对数据进行观察。
示例:
data() {
return {
responseData: Object.freeze(yourArrayFromBackend)
};
}
通过使用Object.freeze(),你可以告诉Vue不要对数据进行观察,从而避免出现__ob__: Observer的情况。
另外,你也可以使用JSON.parse(JSON.stringify(arrayFromBackend))的方式来深拷贝数组,并重新赋值给前端,这样也能够避免响应式的情况发生。
vue 使用history路由模式页面刷新404问题
nginx添加参数try_files $uri $uri/ /index.html;
worker_processes auto;
events {
worker_connections 1024;
accept_mutex on;
}
http {
include mime.types;
default_type application/octet-stream;
keepalive_timeout 75s;
gzip on;
gzip_min_length 4k;
gzip_comp_level 4;
client_max_body_size 1024m;
client_header_buffer_size 32k;
client_body_buffer_size 8m;
server_names_hash_bucket_size 512;
proxy_headers_hash_max_size 51200;
proxy_headers_hash_bucket_size 6400;
gzip_types application/javascript application/x-javascript text/javascript text/css application/json application/xml;
server {
listen 80;
location / {
try_files $uri $uri/ /index.html;
root /home/student_mange;
index index.html;
}
}
}
element上传附件(el-upload 超详细)
原文参考:element上传附件(el-upload 超详细)
<div class="flex-div uploaditem">
//这里是上传了那些文件的提示,我没有要默认的文件提示
<el-tooltip class="item" effect="dark" :content="tag.name" placement="top-start" v-for="(tag,index) in fileList" :key="index">
<el-tag style="margin-right:10px;display:flex;" :disable-transitions="false" @close="handleClose(index)" closable @click="downloadFile(tag)"><i class="el-icon-paperclip"></i><span class="tagtext">{{tag.name}}</span></el-tag>
</el-tooltip>
<el-upload
class="upload-demo"
action //必要属性,上传文件的地址,可以不给,但必须要有,不给就i调接口上传
:http-request="uploadFile"//这个是就上传文件的方法,把上传的接口写在这个方法里
ref="upload"
:limit="fileLimit"//上传文件个数的限制
:on-remove="handleRemove"//上传之后,移除的事件
:file-list="fileList"//上传了那些文件的列表
:on-exceed="handleExceed"//超出上传文件个数的错误回调
:before-upload="beforeUpload"//文件通过接口上传之前,一般用来判断规则,
//比如文件大小,文件类型
:show-file-list="false"//是否用默认文件列表显示
:headers="headers"//上传文件的请求头
>
<!-- action="/api/file/fileUpload" -->
<el-button class="btn"><i class="el-icon-paperclip"></i>上传附件</el-button>
</el-upload>
</div>
data
//上传后的文件列表
fileList: [],
// 允许的文件类型
fileType: [ "pdf", "doc", "docx", "xls", "xlsx","txt","png","jpg", "bmp", "jpeg"],
// 运行上传文件大小,单位 M
fileSize: 50,
// 附件数量限制
fileLimit: 5,
//请求头
headers: { "Content-Type": "multipart/form-data" },
method方法
//上传文件之前
beforeUpload(file){
if (file.type != "" || file.type != null || file.type != undefined){
//截取文件的后缀,判断文件类型
const FileExt = file.name.replace(/.+\./, "").toLowerCase();
//计算文件的大小
const isLt5M = file.size / 1024 / 1024 < 50; //这里做文件大小限制
//如果大于50M
if (!isLt5M) {
this.$showMessage('上传文件大小不能超过 50MB!');
return false;
}
//如果文件类型不在允许上传的范围内
if(this.fileType.includes(FileExt)){
return true;
}
else {
this.$message.error("上传文件格式不正确!");
return false;
}
}
},
//上传了的文件给移除的事件,由于我没有用到默认的展示,所以没有用到
handleRemove(){
},
//这是我自定义的移除事件
handleClose(i){
this.fileList.splice(i,1);//删除上传的文件
if(this.fileList.length == 0){//如果删完了
this.fileflag = true;//显示url必填的标识
this.$set(this.rules.url,0,{ required: true, validator: this.validatorUrl, trigger: 'blur' })//然后动态的添加本地方法的校验规则
}
},
//超出文件个数的回调
handleExceed(){
this.$message({
type:'warning',
message:'超出最大上传文件数量的限制!'
});return
},
//上传文件的事件
uploadFile(item){
this.$showMessage('文件上传中........')
//上传文件的需要formdata类型;所以要转
let FormDatas = new FormData()
FormDatas.append('file',item.file);
this.$axios({
method: 'post',
url: '/file/fileUpload',
headers:this.headers,
timeout: 30000,
data: FormDatas
}).then(res=>{
if(res.data.id != '' || res.data.id != null){
this.fileList.push(item.file);//成功过后手动将文件添加到展示列表里
let i = this.fileList.indexOf(item.file)
this.fileList[i].id = res.data.id;//id也添加进去,最后整个大表单提交的时候需要的
if(this.fileList.length > 0){//如果上传了附件就把校验规则给干掉
this.fileflag = false;
this.$set(this.rules.url,0,'')
}
//this.handleSuccess();
}
})
},
//上传成功后的回调
handleSuccess(){
},
富文本编辑器
1.各个编辑器之间的较量
UEditor:百度前端的开源项目,功能强大,基于 jQuery,但已经没有再维护,而且限定了后端代码,修改起来比较费劲
bootstrap-wysiwyg:微型,易用,小而美,只是 Bootstrap + jQuery...
kindEditor:功能强大,代码简洁,需要配置后台,而且好久没见更新了
wangEditor:轻量、简洁、易用,但是升级到 3.x 之后,不便于定制化开发。不过作者很勤奋,广义上和我是一家人,打个call
quill:本身功能不多,不过可以自行扩展,api 也很好懂,如果能看懂英文的话...
summernote:没深入研究,UI挺漂亮,也是一款小而美的编辑器,可是我需要大的
tinymce插件:GitHub 上星星很多,功能也齐全; 唯一一个从 word 粘贴过来还能保持绝大部分格式的编辑器;不需要找后端人员扫码改接口,前后端分离
quill 使用
(注意:富文本中的图标以base64格式编码存储需要将数据库类型设置为longtext)
vue 引入quill-image-resize-module 插件报错
vue quill-image-resize-module imports报错处理
vue-cli2中\
解决方法一(推荐):
编辑 vue.config.js 文件
需要重启项目!!!需要重启项目!!!需要重启项目!!!
//别忘了引入
const webpack = require('webpack')
module.exports = {
configureWebpack: {
plugins: [
//此处关键
new webpack.ProvidePlugin({
'window.Quill': 'quill/dist/quill.js',
Quill: 'quill/dist/quill.js'
})
]
}
}
解决方法二(有些项目没有webpack.dev.conf.js这些):
在build文件夹下,找到webpack.dev.conf.js 和 webpack.prod.conf.js 文件,打开,找到plugins属性,然后在其中添加如下代码:
记得是2个webpack 配置文件
new webpack.ProvidePlugin({'window.Quill':'quill/dist/quill.js','Quill':'quill/dist/quill.js'})
quill 使用步骤
第一步下载依赖
npm install vue-quill-editor --save
npm install quill-image-drop-module --save
npm install quill-image-resize-module --save
第二步、然后在main.js文件中,全局注册
//引入quill-editor编辑器
import VueQuillEditor from 'vue-quill-editor'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
Vue.use(VueQuillEditor)
//实现quill-editor编辑器拖拽上传图片
import * as Quill from 'quill'
import { ImageDrop } from 'quill-image-drop-module'
Quill.register('modules/imageDrop', ImageDrop)
//实现quill-editor编辑器调整图片尺寸
import ImageResize from 'quill-image-resize-module'
Quill.register('modules/imageResize', ImageResize)
第三步、vue文件中使用
<quill-editor ref="QuillEditor" v-model:content="postJudge.analysis" v-bind:options="editorOption"
contentType="html" class="editor"/>
样式、配置见文章末尾(搜 quill模板)
quill实现上传文件 并回显url到内容区域
原文参考:quill实现上传文件_quill 上传文件
1.首先工具栏配置加上upload,如图:
这时会发现上传图片没有显示出来,需要自定义一个上传的图标,在阿里云矢量图标库下载一个就可以了
2.图标样式修改
/deep/.ql-upload{
background: url("./../../assets/img/upload.svg") !important;
background-size: 20px 20px !important;
background-position: center center !important;
background-repeat:no-repeat !important;
}
这时候就有了:
加上上传回调的函数
完整代码在文末,搜(quill上传文件完整代码)
vue封装即引即用的评论组件
单独下载组件进行二开修改的话到JYeontu/JYeontu组件仓库 - 码云
- 安装emoji依赖
npm i v-emoji-picker - 把下载到的组件代码加入到我们自己的组件文件夹,像引入自己的组件一样引入
- 用到了less,可以到在线网站里将less 转 为 原生 css就可以不用下载less-loader依赖
elementUI中对table数据显示进行转换
<el-table-column label="性别" align="center" prop="cadreSex" :formatter="formatSex" />
// 性别数据转换
formatSex(row){
return row.cadreSex === 0 ? "男" : row.cadreSex === 1 ? "女" : "未填写";
},
在Vue实例的filters选项中定义一个日期过滤器,用于格式化日期
<div class="h_time">
{{ item.publishTime | formatDate }}
</div>
# filters 不在methods中,是一个独立的模块在export default中
filters: {
formatDate(value) {
if (value) {
const date = new Date(value);
const year = date.getFullYear();
const month = date.getMonth() + 1;
return year + '-' + (month < 10 ? '0' + month : month);
}
return '';
}
}
前端实现搜索并高亮文字的两种方式
sass、node-sass、sass-loader安装失败、不兼容
以下是一些可能的解决方法:
- 安装Python:确保您的计算机上已安装Python,并将其添加到系统的环境变量中。您可以从Python官方网站下载并安装最新版本的Python。
- 配置Python路径:如果已经安装了Python,请检查您的系统环境变量是否正确设置。确保将Python的二进制目录(例如
C:\PythonXX)添加到PATH环境变量中。 - 使用node-sass替代品:考虑使用
sass库作为sass-loader的替代品,因为node-sass可能会导致兼容性问题。首先,卸载当前的node-sass:
npm uninstall node-sass
然后安装sass
npm install sass --save-dev
Vue项目导入导出csv文件
vue跳转页面常用的几种方法
1、this.$router.push()
跳转到指定url路径,并向history栈中添加一个记录,点击后退会返回到上一个页面。
1. 不带参数
this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})
2. query传参
this.$router.push({name:'home',query: {id:'123456'}})
this.$router.push({path:'/home',query: {id:'123456'}})
// html 取参 $route.query.id script 取参 this.$route.query.id
3. params传参
this.$router.push({name:'home',params: {id:'123456'}}) // 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id script 取参 this.$route.params.id
4. query和params区别
query类似get, 跳转之后页面url后面会拼接参数,类似?id=123456, 非重要性的可以这样传, 密码之类还是用params刷新页面id还在
params类似post, 跳转之后页面url后面不会拼接参数, 但是刷新页面id会消失。
2、this.$router.replace()
跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上个页面 (直接替换当前页面)。
用法同上,和第2个的this.$router.push方法一样。
3、this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
<button @click="upPage">[上一页]</button>
<button @click="downPage">[下一页]</button>
upPage() {
this.$router.go(-1); // 后退一步记录,等同于 history.back()
},
downPage() {
this.$router.go(1); // 在浏览器记录中前进一步,等同于 history.forward()
}
4、router-link跳转
1.不带参数
<router-link :to="{name:'home'}">
<router-link :to="{path:'/home'}"> //name,path都行, 建议用name
// 注意:router-link中链接如果是'/'开始就是从根路由开始;如果不带'/',则从当前路由开始。
2.带params参数
<router-link :to="{name:'home', params: {id:123456}}">
// params传参数 (类似post)
// 路由配置 path: "/home/:id" 或者 path: "/home:id"
// 不配置path ,第一次可请求,刷新页面id会消失;配置path,刷新页面id会保留。
// html 取参 $route.params.id script 取参 this.$route.params.id
3.带query参数
<router-link :to="{name:'home', query: {id:123456}}">
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置
// html 取参 $route.query.id script 取参 this.$route.query.id
EasyExcel + Vue +Springboot 前后端联动,快捷导出Excel文件
参考文章
注意点:
后端注意点:
response.setContentType("application/vnd.ms-excel;chartset=utf-8"); //文件扩展名为excel格式
response.setHeader("Content-Disposition", "attachment;filename=" + fileName); //触发文件名为filename的“另存为”对话框
// 内容样式
HorizontalCellStyleStrategy horizontalCellStyleStrategy = ContentStyle.getContentStyle();
//将OutputStream对象附着到EasyExcel的ExcelWriter实例
EasyExcel.write(response.getOutputStream(), WeekProPlanExcel.class) //(输出流, 文件头)
.excelType(ExcelTypeEnum.XLSX)
.autoCloseStream(true)
.sheet("第" + weekNo + "周") //第一个sheet的名
.doWrite(list); //写入数据
前端请求因为需要鉴权所以不能直接使用超链接发起请求
//将当前页数据导出为Excel
exportExcel(){
axios({
method: 'post',
url: '/xxxx/exportExcel',
responseType: 'blob', //设置返回信息为二进制文件,默认为json
data: this.tableInfo, //后台照常用@RequestBody接收即可
}).then(res => {
// debugger;
let blob = new Blob([res], { type: 'application/xlsx' });
let url = window.URL.createObjectURL(blob);
const link = document.createElement('a'); //创建a标签
link.href = url;
link.download = '通用订单信息.xlsx'; //重命名文件
link.click();
URL.revokeObjectURL(url);
});
},
关于测试环境以及生产环境跨域问题
测试环境设置代理
设置changeOrigin: true
devServer: {
open: true, // 启动项目后自动开启浏览器
host: "localhost", // 对应的主机名,默认localhost
port: 8080, // 端口号
proxy: { // 主要配置
// api 自定义标识,用来识别带api的请求
"/api": {
target: "http://a.baidu.com.cn", // 对 http://localhost:8080/api/test 的请求会代理到 http://a.baidu.com.cn/api/test
changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
// secure: false, // 如果是https接口,需要配置这个参数
pathRewrite: {
"^/api": "", // 路径重写,替换 target中请求地址,http://a.baidu.com.cn/api/test --> http://a.baidu.com.cn/test
},
},
},
},
发布环境设置
前端跨域请求需要携带cookie,如果是vue项目的话配置axios
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: url,
timeout: 5000,
withCredentials: true,
});
或
axios.defaults.withCredentials = true;
//当**Credentials为true时,**Origin不能为星号,需为具体的ip地址【如果接口不带cookie,**Origin无需设成具体ip】
response.setHeader("Access-Control-Allow-Origin", "http://IPv4:端口");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET,POST,PATCH,PUT,OPTIONS,DELETE");
response.setHeader("Access-Control-Allow-Headers", "Origin,Content-Type,Cookie,Accept,Token");
向后端发送请求返回200,但是响应内容为空(postman在发送了登录接口后访问其他接口又正常,网页登录后数据请求失败,请求不报错,返回200,但是响应内容为空)且测试环境正常,发布生产则出问题
axios设置withCredentials导致“跨域”的解决方案
1、检查是否是cookie问题
前端跨域请求需要携带cookie,如果是vue项目的话配置axios
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: url,
timeout: 5000,
withCredentials: true,
});
或
axios.defaults.withCredentials = true;
2、测试环境没问题是因为设置了 changeOrigin: true
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/': {
target: 'http://localhost:9201',//本地地址
changeOrigin: true // 如果接口跨域,需要进行这个参数配置,允许代理修改Origin的值为目标服务器,这样就不存在跨域了
}
},
/deep/, >>> 和 ::v-deep 都是用于在 Vue 中深度作用选择器样式的三种方式,它们在使用上有一些微小的差异。
/deep/选择器 (已废弃):/deep/选择器是 Vue 2 中深度作用选择器的一种方式。它用于在某个组件样式中,能够影响嵌套在该组件内的子组件。但自 Vue 2.6.0 开始,/deep/选择器被废弃了,不再推荐使用。>>>选择器:>>>选择器是 Vue 2.2+ 中深度作用选择器的一种方式。它和/deep/功能类似,也用于影响嵌套组件的样式,但和/deep/不同的是,>>>只在以非 scoped 方式引入的样式中生效,而在带有 scoped 属性的样式中被忽略。::v-deep选择器 (推荐使用):::v-deep选择器是 Vue 2.6.0+ 新增的一种方式,用于取代/deep/选择器。它和>>>选择器类似,但比>>>选择器更强大,能够在任何样式中,无论是否带有 scoped 属性,都生效。::v-deep选择器通常被推荐作为深度作用选择器的首选方式。
vue刷新组件的方式
vue $forceUpdate() 强制重新渲染及四种方案对比\
前言
-Vue的双向绑定属于自动档;在特定的情况下,需要手动触发“刷新”操作,目前有四种方案可以选择:
1、刷新整个页面(最low的,可以借助route机制,不推荐)
2、使用v-if标记(比较low的,有时候不生效,不推荐)
3、使用内置的forceUpdate方法(较好的)
4、使用key-changing优化组件(最好的,实测好用)
<span :key="key"></span>
想要更新的时候key++即可(我的使用场景,模态框点击确定后关闭模态框,刷新组件,然组件的一些记录刷新)
quill上传文件完整代码
<template>
<div class="editor-page">
<!-- 图片上传组件辅助-->
<el-upload
class="avatar-uploader"
:action="serverUrl"
name="file"
:headers="header"
:show-file-list="false"
:on-success="uploadSuccess"
:on-error="uploadError"
:before-upload="beforeUpload"
></el-upload>
<el-upload
class="uploadFile"
:action="serverUrl"
name="file"
:headers="header"
:show-file-list="false"
:on-success="uploadSuccess2"
:on-error="uploadError"
:before-upload="beforeUpload"
></el-upload>
<quill-editor
class="editor"
v-model="content"
:disabled="disabled"
ref="myQuillEditor"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)"
></quill-editor>
</div>
</template>
<script>
// 工具栏配置
import userUtils from '@/utils/user'
const toolbarOptions = [
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
// [{ script: "sub" }, { script: "super" }], // 上标/下标
[{ indent: "-1" }, { indent: "+1" }], // 缩进
[{ size: ["small", false, "large", "huge"] }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ font: [] }], // 字体种类
[{ align: [] }], // 对齐方式
["clean"], // 清除文本格式
['link', 'image', 'upload'], // 链接、图片、文件
];
import { quillEditor } from "vue-quill-editor";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
// 自定义插入a链接
import { Quill } from 'vue-quill-editor'
let Link = Quill.import('formats/link')
class FileBlot extends Link {
// 继承Link Blot
static create(value) {
let node = undefined
if (value && !value.href) {
// 适应原本的Link Blot
node = super.create(value)
} else {
// 自定义Link Blot
node = super.create(value.href)
// node.setAttribute('download', value.innerText); // 左键点击即下载
node.innerText = value.innerText
node.download = value.innerText
}
return node
}
}
FileBlot.blotName = 'link'
FileBlot.tagName = 'A'
Quill.register(FileBlot)
export default {
props: {
/*编辑器的内容*/
value: {
type: String,
},
disabled: {
type: Boolean,
},
/*图片大小*/
maxSize: {
type: Number,
default: 4000, //kb
},
},
components: {
quillEditor,
},
watch: {
value(newValue) {
this.content = newValue
},
},
data() {
const AUTH_TOKEN = userUtils.getToken()
return {
content: this.value,
previewShow: false, //预览弹框
quillUpdateImg: false, // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示
TiLength: 0, //富文本框里的文字长度
editorOption: {
theme: "snow", // or 'bubble'
placeholder: "请输入文本内容",
modules: {
toolbar: {
container: toolbarOptions,
// container: "#toolbar",
handlers: {
image: function (value) {
if (value) {
// 触发input框选择图片文件
document.querySelector(".avatar-uploader input").click()
} else {
this.quill.format("image", false);
}
},
link: function(value) {
if(value)
document.querySelector(".editor-display-button").click()
else
this.quill.format("link", false)
},
upload: value => { //编辑器-上传文件
if (value) {
document.querySelector('.uploadFile input').click()
}
}
},
},
},
},
serverUrl: "/api/file/saveFile", // 这里写你要上传的图片服务器地址
header: {
Authorization: AUTH_TOKEN
}, // 有的图片服务器要求请求头需要有token
};
},
mounted() {
this.TiLength =this.$refs.myQuillEditor.quill.getLength() - 1
this.content = this.value
},
methods: {
onEditorBlur() {
//失去焦点事件
},
onEditorFocus() {
//获得焦点事件
},
onEditorChange(event) {
event.quill.deleteText(2000,1);
if(this.content === ''){
this.TiLength = 0
}
else{
this.TiLength = event.quill.getLength()-1
}
this.$emit('change', event.html)
},
// 富文本图片上传前
beforeUpload(file) {
console.log(file);
// let formData = new FormData()
// 显示loading动画
this.quillUpdateImg = true;
},
// 图片上传
uploadSuccess(res, file) {
// res为图片服务器返回的数据
// 获取富文本组件实例
let quill = this.$refs.myQuillEditor.quill;
// 如果上传成功
if (res.code === 200) {
// 获取光标所在位置
let length = quill.getSelection().index;
// 插入图片 res.url为服务器返回的图片地址
quill.insertEmbed(length, "image", res.data);
// 调整光标到最后
quill.setSelection(length + 1);
} else {
this.$message.error("图片插入失败");
}
// loading动画消失
this.quillUpdateImg = false;
},
// 文件上传
uploadSuccess2(res, file) {
// res为图片服务器返回的数据
// 获取富文本组件实例
let quill = this.$refs.myQuillEditor.quill;
// 如果上传成功
if (res.code === 200) {
let fileNameLength = file.name.length
// 插入链接
let length = quill.getSelection().index;
// quill.insertEmbed(length, 'link', {href:res.data, innerText:file.name}, "api")
quill.insertEmbed(length, 'link', {href: res.data, innerText: file.name})
quill.setSelection(length + fileNameLength)
} else {
this.$message.error("插入失败");
}
// loading动画消失
this.quillUpdateImg = false;
},
// 富文本图片上传失败
uploadError() {
// loading动画消失
this.quillUpdateImg = false;
this.$message.error("上传失败");
},
previewArticle() {
console.log(121212);
},
handleCancel() {
this.previewShow = false
}
},
};
</script>
<style lang="scss" scoped>
.editor-page{
height: calc(100%);
}
.editor {
line-height: normal !important;
height: calc(100%);
}
.avatar-uploader{
display: none;
}
.editor-counter{
padding-right: 20px;
}
/deep/.ql-upload{
background: url("./../../assets/img/upload.svg") !important;
background-size: 20px 20px !important;
background-position: center center !important;
background-repeat:no-repeat !important;
}
</style>
quill模板
<template>
<div class="add">
<quill-editor ref="QuillEditor0" v-model:content="postChange.analysis" v-bind:options="editorOption2"
contentType="html" class="editor"/>
</div>
</template>
<script>
const toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }], // custom button values
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub' }, { 'script': 'super' }], // superscript/subscript
[{ 'indent': '-1' }, { 'indent': '+1' }], // outdent/indent
[{ 'direction': 'rtl' }], // text direction
[{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
[{ 'font': [] }],
[{ 'align': [] }],
['link', 'image'],
['clean'] // remove formatting button
];
export default {
data() {
return {
uploadUrlPath: "没有文件上传",
quillUpdateImg: false,
content: '', //最终保存的内容
editorOption: {
placeholder: '请输入题目内容',
modules: {
imageResize: {
displayStyles: {
backgroundColor: 'black',
border: 'none',
color: 'white'
},
modules: ['Resize', 'DisplaySize', 'Toolbar']
},
toolbar: {
container: toolbarOptions, // 工具栏
}
}
},
}
},
mounted() {
this.getQuestion();
},
created() {
},
methods: {
},
};
</script>
<style lang="less" scoped>
.editor {
line-height: normal !important;
height: 400px;
margin-bottom: 50px;
}
.ql-snow .ql-tooltip[data-mode="link"]::before {
content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0px;
content: "保存";
padding-right: 0px;
}
.ql-snow .ql-tooltip[data-mode="video"]::before {
content: "请输入视频地址:";
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: "14px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
content: "10px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
content: "18px";
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
content: "32px";
}
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: "文本";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: "标题1";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: "标题2";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: "标题3";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: "标题4";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: "标题5";
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: "标题6";
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: "标准字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
content: "衬线字体";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
content: "等宽字体";
}
</style>