286 lines
7.4 KiB
Vue
286 lines
7.4 KiB
Vue
<template>
|
||
<view class="container">
|
||
<view class="page-body">
|
||
<view class="wrapper">
|
||
<view
|
||
class="toolbar"
|
||
@click="format"
|
||
style="max-height: 240px; overflow-y: auto"
|
||
>
|
||
<!-- 加粗 -->
|
||
<view
|
||
:class="data.formats.bold ? 'ql-active' : ''"
|
||
class="iconfont icon-zitijiacu"
|
||
data-name="bold"
|
||
></view>
|
||
<!-- 斜体 -->
|
||
<view
|
||
:class="data.formats.italic ? 'ql-active' : ''"
|
||
class="iconfont icon-zitixieti"
|
||
data-name="italic"
|
||
></view>
|
||
<!-- 下划线 -->
|
||
<view
|
||
:class="data.formats.underline ? 'ql-active' : ''"
|
||
class="iconfont icon-zitixiahuaxian"
|
||
data-name="underline"
|
||
></view>
|
||
<!-- 对齐方式:左 -->
|
||
<view
|
||
:class="data.formats.align === 'left' ? 'ql-active' : ''"
|
||
class="iconfont icon-zuoduiqi"
|
||
data-name="align"
|
||
data-value="left"
|
||
></view>
|
||
<!-- 对齐方式:居中 -->
|
||
<view
|
||
:class="data.formats.align === 'center' ? 'ql-active' : ''"
|
||
class="iconfont icon-juzhongduiqi"
|
||
data-name="align"
|
||
data-value="center"
|
||
></view>
|
||
<!-- 对齐方式:右 -->
|
||
<view
|
||
:class="data.formats.align === 'right' ? 'ql-active' : ''"
|
||
class="iconfont icon-youduiqi"
|
||
data-name="align"
|
||
data-value="right"
|
||
></view>
|
||
<!-- 对齐方式:两侧 -->
|
||
<view
|
||
:class="data.formats.align === 'justify' ? 'ql-active' : ''"
|
||
class="iconfont icon-zuoyouduiqi"
|
||
data-name="align"
|
||
data-value="justify"
|
||
></view>
|
||
<!-- 有序排列 -->
|
||
<view
|
||
:class="data.formats.list === 'ordered' ? 'ql-active' : ''"
|
||
class="iconfont icon-youxupailie"
|
||
data-name="list"
|
||
data-value="ordered"
|
||
></view>
|
||
<!-- 无序排列 -->
|
||
<view
|
||
:class="data.formats.list === 'bullet' ? 'ql-active' : ''"
|
||
class="iconfont icon-wuxupailie"
|
||
data-name="list"
|
||
data-value="bullet"
|
||
></view>
|
||
<!-- 取消缩进 -->
|
||
<view
|
||
class="iconfont icon-outdent"
|
||
data-name="indent"
|
||
data-value="-1"
|
||
></view>
|
||
<!-- 缩进 -->
|
||
<view
|
||
class="iconfont icon-indent"
|
||
data-name="indent"
|
||
data-value="+1"
|
||
></view>
|
||
<!-- 添加分割线 -->
|
||
<view class="iconfont icon-fengexian" @click="insertDivider"></view>
|
||
<!-- 插入图片 -->
|
||
<view class="iconfont icon-image" @click="insertImage"></view>
|
||
<!-- 设置标题 -->
|
||
<view
|
||
:class="data.formats.header === 3 ? 'ql-active' : ''"
|
||
class="iconfont icon-H"
|
||
data-name="header"
|
||
:data-value="3"
|
||
></view>
|
||
<!-- 下标 -->
|
||
<view
|
||
:class="data.formats.script === 'sub' ? 'ql-active' : ''"
|
||
class="iconfont icon-zitixiabiao"
|
||
data-name="script"
|
||
data-value="sub"
|
||
></view>
|
||
<!-- 上标 -->
|
||
<view
|
||
:class="data.formats.script === 'super' ? 'ql-active' : ''"
|
||
class="iconfont icon-zitishangbiao"
|
||
data-name="script"
|
||
data-value="super"
|
||
></view>
|
||
<view class="iconfont icon-undo" @click="undo"></view>
|
||
<view class="iconfont icon-redo" @click="redo"></view>
|
||
</view>
|
||
<view class="editor-wrapper">
|
||
<editor
|
||
id="editor"
|
||
class="ql-container"
|
||
:placeholder="data.placeholder"
|
||
@statuschange="onStatusChange"
|
||
:show-img-resize="true"
|
||
@ready="onEditorReady"
|
||
@input="getCtx"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { reactive } from "vue";
|
||
import Taro from "@tarojs/taro";
|
||
import { BASE_URL } from "@/utils/request";
|
||
|
||
const { content } = defineProps<{
|
||
content: string;
|
||
}>();
|
||
|
||
let emits = defineEmits(["input"]);
|
||
|
||
const data = reactive<any>({
|
||
editorCtx: "",
|
||
readOnly: false,
|
||
placeholder: "请输入商品详情...",
|
||
richText: "",
|
||
formats: {},
|
||
});
|
||
|
||
function onEditorReady() {
|
||
// 富文本节点渲染完成
|
||
Taro.createSelectorQuery()
|
||
.select("#editor")
|
||
.context((res) => {
|
||
data.editorCtx = res.context;
|
||
// 初始化数据
|
||
if (content) {
|
||
data.editorCtx.setContents({
|
||
html: content,
|
||
});
|
||
}
|
||
})
|
||
.exec();
|
||
}
|
||
|
||
// 失去焦点时,获取富文本的内容
|
||
function getCtx(e: { detail: { html: any } }) {
|
||
data.richText = e.detail.html;
|
||
emits("input", e.detail.html);
|
||
}
|
||
|
||
// 撤销操作
|
||
function undo() {
|
||
data.editorCtx.undo();
|
||
}
|
||
|
||
// 复原操作
|
||
function redo() {
|
||
data.editorCtx.redo();
|
||
}
|
||
|
||
//修改样式
|
||
function format(e: { target: { dataset: { name: any; value: any } } }) {
|
||
// console.log("format", e.target.dataset);
|
||
let { name, value } = e.target.dataset;
|
||
if (!name) return;
|
||
data.editorCtx.format(name, value);
|
||
}
|
||
|
||
//通过 Context 方法改变编辑器内样式时触发,返回选区已设置的样式
|
||
function onStatusChange(e: { detail: any }) {
|
||
data.formats = e.detail;
|
||
}
|
||
|
||
// 插入分割线
|
||
function insertDivider() {
|
||
data.editorCtx.insertDivider();
|
||
}
|
||
|
||
// 插入图片
|
||
function insertImage() {
|
||
Taro.chooseImage({
|
||
count: 1,
|
||
sizeType: ["original", "compressed"],
|
||
sourceType: ["album", "camera"],
|
||
success: (res) => {
|
||
// 上传图片的逻辑各有不同,自行调整即可
|
||
Taro.uploadFile({
|
||
url: `${BASE_URL}/upload`,
|
||
name: "file",
|
||
header: { token: Taro.getStorageSync("token") },
|
||
filePath: res.tempFilePaths[0],
|
||
success: (res) => {
|
||
console.log(res);
|
||
const imgData = JSON.parse(res.data) as {
|
||
code: number;
|
||
msg: string;
|
||
data: any;
|
||
};
|
||
if (imgData.code === 200) {
|
||
// 将图片展示在编辑器中
|
||
data.editorCtx.insertImage({
|
||
width: "100%",
|
||
height: "auto",
|
||
src: imgData.data.data,
|
||
alt: "图像",
|
||
success: function () {
|
||
console.log("insert image success");
|
||
},
|
||
});
|
||
} else {
|
||
console.log("上传失败");
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.log(err);
|
||
},
|
||
});
|
||
},
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
@import "../static/font_4211210_2x20brbrv94.css";
|
||
|
||
.wrapper {
|
||
height: 100%;
|
||
}
|
||
|
||
.editor-wrapper {
|
||
height: calc(
|
||
100vh - var(--window-top) - var(--status-bar-height) - 280px - 650px
|
||
);
|
||
overflow: scroll;
|
||
background: rgba(153, 153, 153, 0.05);
|
||
border-radius: 20px;
|
||
margin: 20px 0;
|
||
color: #000;
|
||
}
|
||
|
||
.iconfont {
|
||
display: inline-block;
|
||
margin: 20px 20px;
|
||
width: 32px;
|
||
height: 32px;
|
||
cursor: pointer;
|
||
font-size: 32px;
|
||
}
|
||
|
||
.toolbar {
|
||
box-sizing: border-box;
|
||
border-bottom: 0;
|
||
font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||
}
|
||
|
||
.ql-container {
|
||
box-sizing: border-box;
|
||
padding: 24px 30px;
|
||
width: 100%;
|
||
min-height: 30vh;
|
||
height: 100%;
|
||
font-size: 28px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.ql-active {
|
||
color: #f38e48;
|
||
}
|
||
</style>
|