vue2使用富文本
安装
npm install vue-quill-editor --save
全局引入(main)
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)
vue2 使用打印功能
安装
npm install vue-print-nb --save
全局引入(main)
//引入打印机插件
import Print from 'vue-print-nb'
//注册
Vue.use(Print); //注册
组件应用
<template>
<div class="bg-white p-5">
<!-- 给要打印的区块添加一个id,这时候给点击打印的按钮添加一个指令v-print,指定为要打印的区域id即可 -->
<div class="flex justify-end">
<el-button v-print="printObj" size="mini" type="primary">打印</el-button>
</div>
<div id="printTest" >
<h3 style="text-align: center;">虚惊轻微事故调查表</h3>
<div class="mt-5" >
<table style="width: 100%;">
<tr>
<td class="border p printcolor" style="text-align: center;background-color: #E7F3FC;">事故等级</td>
<td class="border p" style="text-align: center;">虚惊/轻微</td>
<td class="border p printcolor" style="text-align: center;background-color: #E7F3FC;">事故类别</td>
<td class="border p" style="text-align: center;">物体打击</td>
<td class="border p printcolor" style="text-align: center;background-color: #E7F3FC;">事故时间</td>
<td class="border p" style="text-align: center;">2023-04-14</td>
<td class="border p printcolor" style="text-align: center;background-color: #E7F3FC;">事故地点</td>
<td class="border p" style="text-align: center;">上海</td>
</tr>
<tr>
<td class="border p printcolor" style="text-align: center;background-color: #E7F3FC;">事故所属Bu</td>
<td class="border p" style="text-align: center;">BU1</td>
<td class="border p printcolor" style="text-align: center;background-color: #E7F3FC;">事故部门</td>
<td class="border p" style="text-align: center;"></td>
<td class="border p printcolor" style="text-align: center;background-color: #E7F3FC;">事故区域负责人</td>
<td class="border p" style="text-align: center;"></td>
<td class="border p printcolor" style="text-align: center;background-color: #E7F3FC;">电话</td>
<td class="border p" style="text-align: center;">19822773234</td>
</tr>
<tr>
<td class="border p printcolor" colspan="8" style="background-color: #E7F3FC;">1.事故状况描述:(时间、地点、人物、受伤经过)</td>
</tr>
<tr>
<td class="border p" style="height: 100px;" colspan="8">
<input type="file" style="display:none;" id="input" @change.prevent="upLoad()"/>
<quill-editor class="editorimg" v-model="content" ref="myQuillEditor" :options="editorOption" @change="onEditorChange($event)" >
</quill-editor>
</td>
</tr>
<tr>
<td class="border p printcolor" colspan="8" style="background-color: #E7F3FC;">2.事故图片:</td>
</tr>
<tr>
<td class="border p" style="height: 100px;" colspan="8">
</td>
</tr>
<tr>
<td class="border p printcolor" colspan="8" style="background-color: #E7F3FC;">3.事故处理情况(事故现场处理措施与状况、伤员处理情况等)</td>
</tr>
<tr>
<td class="border p" style="height: 100px;" colspan="8">
</td>
</tr>
<tr>
<td class="border p printcolor" colspan="8" style="background-color: #E7F3FC;">4、事故直接损失统计</td>
</tr>
<tr>
<td class="border p" colspan="8">□无损失 □轻微损失(说明):</td>
</tr>
<tr>
<td class="border p printcolor" colspan="8" style="background-color: #E7F3FC;">5、原因分析与改善方案</td>
</tr>
<tr>
<td class="border p" colspan="8" style="height: 100px;" >
</td>
</tr>
<tr>
<td class="border p printcolor" colspan="8" style="background-color: #E7F3FC;">6、预防改善对策:</td>
</tr>
<tr style="background-color: #E7F3FC;text-align: center;">
<td class="border p printcolor" colspan="3" >改善对策</td>
<td class="border p printcolor" >完成时间</td>
<td class="border p printcolor" >佐证资料<el-button @click="drawer3 = true" type="text" style="margin-left: 16px">描述</el-button></td>
<td class="border p printcolor" >责任人</td>
<td class="border p printcolor" >核实结果</td>
<td class="border p printcolor" >核实人员</td>
</tr>
<tr style="text-align: center;height: 30px;">
<td class="border p" colspan="3" ></td>
<td class="border p" ></td>
<td class="border p" ></td>
<td class="border p" ></td>
<td class="border p" ></td>
<td class="border p" ></td>
</tr>
<tr style="text-align: center;height: 30px;">
<td class="border p" colspan="3" ></td>
<td class="border p" ></td>
<td class="border p" ></td>
<td class="border p" ></td>
<td class="border p" ></td>
<td class="border p" ></td>
</tr>
</table>
</div>
<el-drawer title="佐证资料" :visible.sync="drawer3" direction="rtl" :close-on-press-escape='false'
:wrapperClosable='false' destroy-on-close size='50%'>
<div style="font-size: 10px; padding: 10px;overflow-y: auto;" :style="{ height: (h + 'px') }" v-html>
可根据具体对策提供相关佐证资料:<br/>
改善现场照片、管理培训记录、书面培训教材、培训会议签到表、培训会议照片、制定相关管理要求文件等
</div>
</el-drawer>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
const windowHeight = window.innerHeight || document.body.clientHeight
export default {
data() {
return {
drawer3: false,
printObj:{
id:'printTest',//打印的id
popTitle:'管理系统平台',//打印的标题
},
// 富文本编辑器配置
editorOption: {
modules: {
toolbar: { // 菜单栏(工具栏),手动配置自己需要的富文本功能
container: [
["bold", "italic", "underline", "strike"], //加粗,斜体,下划线,删除线
[{ indent: "-1" }, { indent: "+1" }], // 左右缩进
[{ color: [] }, { background: [] }], // 字体颜色,字体背景颜色
[{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、2表示字体大小
[{ align: [] }], //对齐方式
["image", /*"video"*/], //上传图片、上传视频
],
handlers: {
'image': function (value) { // 劫持图片上传,自定义图片上传
if (value) {
// 通过input的type=file唤醒选择弹框,选择之后自定义上传路径
document.querySelector('#input').click()
}
}
}
},
},
placeholder: "",
},
content:''
}
},
computed: {
...mapState({
h: state => {
let a = windowHeight - 64 - 44
return state.screenHeight ? state.screenHeight : a
}
}),
},
methods:{
// 内容改变事件
onEditorChange({ quill, html, text }) {
//一张图片好几万的字符串
this.content = html;
},
upLoad() {
let files = window.event.target.files[0];
console.log(files)
//上传文件
// 在这里进行一系列的校验
const formData = new FormData();
formData.append("withc",'还发快手看');
formData.append("hegidnh",'和立铠四大皆空福建省');
formData.append("上传的key值",files); //formData.append(‘filename’, file) 必须放到最后,否则ctx.getFileStream()取流完成时stream.fields仍然是空的
this.$axios.post('/admin/shuguuploade',formData,{
'Content-type' : 'multipart/form-data'
}).then(res=>{
// 上传成功后的处理
console.log(res)
})
//文件转成blob
const blob = URL.createObjectURL(files);
console.log(blob)
//blob转成文件
const file = new File([blob], '图片', { type: 'image/png', lastModified: Date.now() });
console.log(file)
//BLOB(url) 转 base64
const image = new Image();
image.src = blob;
image.onload = () => {
// 构建canvas节点
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
const context = canvas.getContext('2d');
context.drawImage(image, 0, 0, image.width, image.height);
// 转换
const imgBase64 = canvas.toDataURL();
// console.log(imgBase64);
//base64 转 BLOB
// 分割base64
const temp = imgBase64.split(',');
// 获取类型
const mime = temp[0].match(/:(.*?);/)[1];
// 解码使用 base-64 编码的字符串
const raw = window.atob(temp[1]);
const rawLength = raw.length;
// base64文件数据读取
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; i += 1) {
uInt8Array[i] = raw.charCodeAt(i);
}
const blob1 = new Blob([uInt8Array], { type: mime });
console.log(blob1)
};
if (files.size / 1024 / 1024 > 2) { // 定义上传的图片的大小
this.$message.warning('请上传小于2M的文件');
return;
}
let fileType = ['.jpg', '.png', '.jpeg'];
let suffix = files.name.substring(
files.name.lastIndexOf('.'),
files.name.length
);
if (fileType.indexOf(suffix) === -1) {
this.$message.warning('请上传格式为png、jpg的文件');
return;
}
// 将图片转换成base64
let reader=new FileReader()
reader.readAsDataURL(files)
reader.onload=(e)=>{
let abs=`<img src='${e.target.result}'/>`
this.content=this.content+abs
document.querySelector('#input').value=''
}
},
upLoad1() {
let files = window.event.target.files[0];
if (files.size / 1024 / 1024 > 2) { // 定义上传的图片的大小
this.$message.warning('请上传小于2M的文件');
return;
}
let fileType = ['.jpg', '.png', '.jpeg'];
let suffix = files.name.substring(
files.name.lastIndexOf('.'),
files.name.length
);
if (fileType.indexOf(suffix) === -1) {
this.$message.warning('请上传格式为png、jpg的文件');
return;
}
// 将图片转换成base64
let reader=new FileReader()
reader.readAsDataURL(files)
reader.onload=(e)=>{
let abs=`<img src='${e.target.result}'/>`
this.content=this.content+abs
document.querySelector('#input').value=''
}
},
}
}
</script>
<style>
/* 控制富文本的图片大小 */
.editorimg img{
max-height: 100px;
}
.editorimg{
max-height: 220px;
overflow: auto;
}
/* 给要打印的背景的元素添加样式 */
.printcolor{
-webkit-print-color-adjust: exact;
}
</style>
eggjs开启流模式
配置 config/config.default.js
eggjs文档
config.multipart = {
fileSize: '50mb',
mode: 'stream', //'file' 可以配置文件模式 https://www.eggjs.org/zh-CN/basics/controller 官方文档 'stream' 配置流模式 配置的模式不一样 接收的方法方式就不一样
fileExtensions: ['.xlsx', '.txt', '.jpg', '.JPG', '.png', '.PNG', '.gif', '.GIF', '.jpeg', '.JPEG','.apk'], // 扩展几种上传的文件格式
};
controller/shigu.js
'use strict';
const Controller = require('egg').Controller;
// 引入
const fs = require('fs');
const path = require('path');
//故名思意 异步二进制 写入流
const awaitWriteStream = require('await-stream-ready').write;
//管道读入一个虫洞。
const sendToWormhole = require('stream-wormhole');
const dayjs = require('dayjs');
class ShiguController extends Controller {
async shuguuploade() {
let { ctx, app } = this;
const stream = await ctx.getFileStream();
console.log(stream.fields)
console.log(stream.filename)
ctx.apiSuccess('ok');
}
}
module.exports = ShiguController;
egg后台打印结果 vue前端 劫持图片上传,自定义图片上传
注意:
只支持上传一个文件。
上传文件必须在所有其他的 fields 后面,否则在拿到文件流时可能还获取不到 fields。
vue3使用富文本
引入tinymce富文本编辑器
npm i tinymce
npm i @tinymce/tinymce-vue
node_modules/tinymce引入 语音包下载
中文语言包zh-Hans.js下载地址
新建组件src/components/Editor.vue
<template>
<editor v-model="content" tag-name="div" :init="init" />
<ChooseImage :preview="false" ref="ChooseImageRef" :limit="9" />
</template>
<script setup>
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import ChooseImage from '~/components/ChooseImage.vue'
import { ref, watch } from "vue"
import "tinymce/themes/silver/theme"; // 引用主题文件
import "tinymce/icons/default"; // 引用图标文件
import 'tinymce/models/dom'
// tinymce插件可按自己的需要进行导入
// 更多插件参考:https://www.tiny.cloud/docs/plugins/
import "tinymce/plugins/advlist"
import "tinymce/plugins/anchor"
import "tinymce/plugins/autolink"
import "tinymce/plugins/autoresize"
import "tinymce/plugins/autosave"
import "tinymce/plugins/charmap" // 特殊字符
import "tinymce/plugins/code" // 查看源码
import "tinymce/plugins/codesample" // 插入代码
import "tinymce/plugins/directionality"
import "tinymce/plugins/emoticons"
import "tinymce/plugins/fullscreen" //全屏
import "tinymce/plugins/help"
import "tinymce/plugins/image" // 插入上传图片插件
import "tinymce/plugins/importcss" //图片工具
import "tinymce/plugins/insertdatetime" //时间插入
import "tinymce/plugins/link"
import "tinymce/plugins/lists" // 列表插件
import "tinymce/plugins/media" // 插入视频插件
import "tinymce/plugins/nonbreaking"
import "tinymce/plugins/pagebreak" //分页
import "tinymce/plugins/preview" // 预览
import "tinymce/plugins/quickbars"
import "tinymce/plugins/save" // 保存
import "tinymce/plugins/searchreplace" //查询替换
import "tinymce/plugins/table" // 插入表格插件
import "tinymce/plugins/template" //插入模板
import "tinymce/plugins/visualblocks"
import "tinymce/plugins/visualchars"
import "tinymce/plugins/wordcount" // 字数统计插件
// v-model
const props = defineProps({
modelValue: String,
})
const emit = defineEmits(["update:modelValue"])
const ChooseImageRef=ref(null)
// 配置
const init = {
language_url: '/tinymce/langs/zh-Hans.js', // 中文语言包路径
language: "zh-Hans",
skin_url: '/tinymce/skins/ui/oxide', // 编辑器皮肤样式
content_css: "/tinymce/skins/content/default/content.min.css",
menubar: false, // 隐藏菜单栏
autoresize_bottom_margin: 50,
max_height: 500,
min_height: 400,
// height: 320,
toolbar_mode: "none",
plugins:
'wordcount visualchars visualblocks template searchreplace save quickbars preview pagebreak nonbreaking media insertdatetime importcss image help fullscreen directionality codesample code charmap link code table lists advlist anchor autolink autoresize autosave',
//新增自定义按钮 这里需要注册如:imageUpload
toolbar:
"formats undo redo fontsizeselect fontselect ltr rtl searchreplace media imageUpload | outdent indent aligncenter alignleft alignright alignjustify lineheight underline quicklink h2 h3 blockquote numlist bullist table removeformat forecolor backcolor bold italic strikethrough hr link preview fullscreen help ",
content_style: "p {margin: 5px 0; font-size: 14px}",
fontsize_formats: "12px 14px 16px 18px 24px 36px 48px 56px 72px",
font_formats: "微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方= PingFang SC, Microsoft YaHei, sans- serif; 宋体 = simsun, serif; 仿宋体 = FangSong, serif; 黑体 = SimHei, sans - serif; Arial = arial, helvetica, sans - serif;Arial Black = arial black, avant garde;Book Antiqua = book antiqua, palatino; ",
branding: false,
elementpath: false,
resize: false, // 禁止改变大小
statusbar: false, // 隐藏底部状态栏
setup:(editor)=>{ //插入图片功能 自定义按钮imageUpload
editor.ui.registry.addButton('imageUpload',{
tooltip:'插入图片',//文字提示
icon:'image',//内置图标
onAction(){
ChooseImageRef.value.open((data)=>{
data.forEach(url=>{
//插入内容 图片
editor.insertContent(`<img src='${url}' style='width:100%'/>`)
})
})
}
})
}
}
tinymce.init; // 初始化
const content = ref(props.modelValue)
watch(props, (newVal) => content.value = newVal.modelValue)
watch(content, (newVal) => emit("update:modelValue", newVal))
</script>
<style>
.tox-tinymce-aux {
z-index: 9999 !important;
}
</style>
调用content.vue
<template>
<FormDrawer ref="formDrawerRef" title="设置商品详情" @submit="submit" destroy-on-close>
<el-form :model="form">
<el-form-item >
<Editor v-model="form.content"/>
</el-form-item>
</el-form>
</FormDrawer>
</template>
<script setup>
import { reactive, ref } from 'vue';
import FormDrawer from '~/components/FormDrawer.vue';
import {readGoods,updateGoods} from '~/api/goods.js'
import Editor from '~/components/Editor.vue';
import { toast } from '~/composables/util';
const formDrawerRef=ref(null)
const form=reactive({
content:''
})
const goodsId=ref(0)
const open=(row)=>{
goodsId.value=row.id
row.contentLoading=true
readGoods(goodsId.value).then(res=>{
form.content=res.content
formDrawerRef.value.open()
}).finally(()=>{
row.contentLoading=false
})
}
const emit=defineEmits(['reloadData'])
const submit=()=>{
formDrawerRef.value.showLoading()
updateGoods(goodsId.value,form)
.then(res=>{
toast('设置商品详情成功')
formDrawerRef.value.close()
emit('reloadData')
})
.finally(()=>{
formDrawerRef.value.hideLoading()
})
}
defineExpose({
open
})
</script>
vue3使用打印功能
npm install vue3-print-nb --save
全局引入(main)
// 1. 全局挂载
import { createApp } from 'vue'
import App from './App.vue'
import print from 'vue3-print-nb'
const app = createApp(App)
app.use(print)
app.mount('#app')