785 lines
22 KiB
Plaintext
785 lines
22 KiB
Plaintext
<script lang="ts">
|
|
import { type PropType } from "vue"
|
|
import { getUid } from "../../core/util/xCoreUtil.uts"
|
|
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
|
|
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
|
|
import { xConfig } from "../../config/xConfig.uts"
|
|
// #ifdef MP
|
|
import { Marked } from "./marked.uts"
|
|
// #endif
|
|
type DATATYP = {
|
|
action : string
|
|
}
|
|
type xEditeListType = "ordered" | "bullet" | "unchecked" | "checked" | ""
|
|
type xEditeAlign = "center" | "left" | "right" | ""
|
|
type xEditeOptsType = {
|
|
img : string,
|
|
link : string,
|
|
b : boolean,
|
|
i : boolean,
|
|
s : boolean,
|
|
u : boolean,
|
|
align : xEditeAlign,
|
|
list : xEditeListType,
|
|
indent : number,
|
|
header : number,
|
|
color : string,
|
|
background : string,
|
|
size : string
|
|
}
|
|
type xEditeListItemType = {
|
|
name : xEditeListType,
|
|
icon : string
|
|
}
|
|
|
|
/**
|
|
* @name 富文本编辑器 xEditor
|
|
* @description 传递正常markdown或者html内容即可,传递markdown时会自动转换为html,如果直接传递html不会转换直接赋值.
|
|
* 值得注意的是:在微信小程序端它是没有样式高亮显示的.其它平台有样式指示,这是因为受限于微信官方本身就不支持.
|
|
* 另外我测试发现HBX4.53 sdk ios端的输入框焦点有问题,导致无法设置样式,待官方修复。
|
|
* @page /pages/biaodan/editor
|
|
* @category 表单组件
|
|
* @constant 平台兼容
|
|
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
|
|
*/
|
|
export default {
|
|
|
|
data() {
|
|
return {
|
|
id: ("xEdte-" + getUid()) as string,
|
|
webviewContext: null as WebviewContext | null,
|
|
isLoaded: false,
|
|
boxWidth: 10,
|
|
boxHeight: 0,
|
|
tid: 0,
|
|
tid2: 0,
|
|
realLoaded: false,
|
|
isMp: false,
|
|
|
|
optsStatus: {
|
|
img: '',
|
|
link: '',
|
|
b: false,
|
|
i: false,
|
|
s: false,
|
|
u: false,
|
|
align: '',
|
|
list: '',
|
|
indent: 0,
|
|
header: 0,
|
|
color: '',
|
|
background: '',
|
|
size: ''
|
|
} as xEditeOptsType,
|
|
|
|
listDataItems: [
|
|
{ icon: 'list-ordered', name: 'ordered' },
|
|
{ icon: 'list-unordered', name: 'bullet' },
|
|
{ icon: 'list-check-2', name: 'unchecked' },
|
|
{ icon: 'list-check-3', name: 'checked' },
|
|
] as xEditeListItemType[],
|
|
|
|
|
|
// #ifdef MP
|
|
markdownObj: new Marked(),
|
|
htmlMpContent: "",
|
|
editorCtx: null
|
|
// #endif
|
|
}
|
|
},
|
|
emits: [
|
|
/**
|
|
* 特定的a,img标签被点击触发,小程序不支持,其它平台支持.
|
|
* @return {Object<{text,tag,attr}>}
|
|
*/
|
|
'tagClick',
|
|
/**
|
|
* 是否初始化成功
|
|
*/
|
|
'init',
|
|
/**
|
|
* 需要通过ref函数调用getHtml才会触发此函数
|
|
*/
|
|
'getValue'
|
|
],
|
|
props: {
|
|
/**
|
|
* 窗口宽
|
|
*/
|
|
width: {
|
|
type: String,
|
|
default: 'auto'
|
|
},
|
|
/**
|
|
* 窗口高,可以传递所支持的任意单位高.
|
|
*/
|
|
height: {
|
|
type: String,
|
|
default: '350'
|
|
},
|
|
/**
|
|
* 需要渲染的markdow或者html内容。
|
|
*/
|
|
value: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
|
|
/**
|
|
* 是否启用纯html渲染。如果你的内容含有特殊字符比如:%,^&%这种不要出现在里面
|
|
* 此时你启用isHtml会经过数据处理直接跳过插件,直接赋值内容到html.就不要启用Markdown了.
|
|
*/
|
|
isHtml: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
/**
|
|
* 富文本的style样式,不可以动态更改.
|
|
* 为了对齐所有端,默认已经把所有平台的样式删除.因此你可以自己设置默认样式来对齐所有平台.
|
|
*/
|
|
nodeStyle: {
|
|
type: String,
|
|
default: "line-height:1.6;color:#000"
|
|
},
|
|
/**
|
|
* 同上,暗黑时的样式.
|
|
*/
|
|
nodeDarkStyle: {
|
|
type: String,
|
|
default: "line-height:1.6;color:#fff"
|
|
},
|
|
/**
|
|
* 默认的按钮背景色
|
|
*/
|
|
color: {
|
|
type: String,
|
|
default: "#f5f5f5"
|
|
},
|
|
/**
|
|
* 激活时的选中背景色
|
|
* 空值取全局
|
|
*/
|
|
activeColor: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
/**
|
|
* 自定义默认的文字/背景颜色
|
|
*/
|
|
customColos: {
|
|
type: Array as PropType<string[]>,
|
|
default: () : string[] => ['#ff0000', '#ff00ff', '#00ff00', '#00ffff', '#ffff00', '#ffffff', '#000000'] as string[]
|
|
}
|
|
},
|
|
computed: {
|
|
_customColos() : string[] {
|
|
return this.customColos
|
|
},
|
|
_color() : string {
|
|
if (this._isDark) return "#222222"
|
|
return getDefaultColor(this.color)
|
|
},
|
|
_activeColor() : string {
|
|
return getDefaultColor(this.activeColor == '' ? 'primary' : this.activeColor)
|
|
},
|
|
|
|
_width() : string {
|
|
return checkIsCssUnit(this.width, xConfig.unit)
|
|
},
|
|
_height() : string {
|
|
return checkIsCssUnit(this.height, xConfig.unit)
|
|
},
|
|
_value() : string {
|
|
return this.value
|
|
},
|
|
_nodeStyle() : string {
|
|
return xConfig.dark == 'dark' ? this.nodeDarkStyle : this.nodeStyle
|
|
},
|
|
_isDark() : boolean {
|
|
return xConfig.dark == 'dark'
|
|
}
|
|
|
|
|
|
|
|
},
|
|
watch: {
|
|
value() {
|
|
// #ifdef APP||WEB
|
|
this.drawer(this._value, this.isHtml)
|
|
// #endif
|
|
// #ifdef MP
|
|
this.setContent(this.value);
|
|
// #endif
|
|
}
|
|
},
|
|
mounted() {
|
|
let t = this;
|
|
// #ifdef MP
|
|
t.isMp = true;
|
|
// #endif
|
|
t.isLoaded = true;
|
|
this.tid = setTimeout(function () {
|
|
// #ifdef APP
|
|
t.webviewContext = uni.createWebviewContext(t.id, t);
|
|
// #endif
|
|
// #ifdef WEB
|
|
t.webviewContext = document.getElementById(t.id) as HTMLElement;
|
|
// #endif
|
|
// #ifdef MP
|
|
t.mpOnInit()
|
|
// #endif
|
|
}, 50);
|
|
|
|
this.onAddlisentMesage();
|
|
|
|
|
|
},
|
|
beforeMount() {
|
|
clearTimeout(this.tid)
|
|
},
|
|
beforeUnmount() {
|
|
|
|
},
|
|
methods: {
|
|
// #ifdef MP
|
|
|
|
mpOnInit() {
|
|
let _this = this;
|
|
uni.createSelectorQuery()
|
|
.in(this)
|
|
.select('#editor').context((res) => {
|
|
console.log(res)
|
|
_this.editorCtx = res.context
|
|
_this.realLoaded = true;
|
|
_this.setContent(_this.value);
|
|
/**
|
|
* 图表加载初始化完成后触发此事件。
|
|
*/
|
|
this.$emit("init")
|
|
}).exec()
|
|
},
|
|
setContent(str) {
|
|
if (!this.realLoaded) {
|
|
uni.showToast({ title: "未初始化完成", icon: 'none' })
|
|
return;
|
|
}
|
|
if (this.isHtml) {
|
|
this.htmlMpContent = str
|
|
} else {
|
|
const htmlcontent = this.markdownObj.parse(str)
|
|
this.htmlMpContent = htmlcontent
|
|
}
|
|
console.log(this.htmlMpContent)
|
|
this.editorCtx.setContents({ html: this.htmlMpContent })
|
|
},
|
|
onItemClick(event) {
|
|
console.log(event)
|
|
},
|
|
|
|
onfocus(event) {
|
|
// console.log(this.editorCtx)
|
|
},
|
|
onstatuschange(event) {
|
|
const detail = event.detail || {}
|
|
let key = ''
|
|
let value = ''
|
|
|
|
|
|
if (Object(detail).hasOwnProperty('bold')) {
|
|
this.optsStatus.b = value as boolean;
|
|
} else if (Object(detail).hasOwnProperty('underline')) {
|
|
this.optsStatus.u = value as boolean;
|
|
} else if (Object(detail).hasOwnProperty('italic')) {
|
|
this.optsStatus.italic = value as boolean;
|
|
} else if (Object(detail).hasOwnProperty('header')) {
|
|
this.optsStatus.header = value as number;
|
|
console.log(detail)
|
|
} else if (Object(detail).hasOwnProperty('align')) {
|
|
this.optsStatus.align = value;
|
|
} else if (Object(detail).hasOwnProperty('list')) {
|
|
this.optsStatus.list = value as xEditeListType;
|
|
} else if (Object(detail).hasOwnProperty('background')) {
|
|
this.optsStatus.list = value as string;
|
|
} else if (Object(detail).hasOwnProperty('size')) {
|
|
this.optsStatus.size = value as string;
|
|
}
|
|
},
|
|
setFormart(key, value) {
|
|
const eobj = {
|
|
'b': 'bold', //true,false
|
|
'i': 'italic', //true,false
|
|
's': 'strike', //true,false
|
|
'u': 'underline', //true,false
|
|
'link': 'link',
|
|
'img': 'image',
|
|
'video': 'video',
|
|
'align': 'align', //left,right,center
|
|
/**
|
|
* ordered:有序列表(编号列表)
|
|
* bullet:无序列表(项目符号列表)
|
|
* unchecked:未选中的复选框列表项
|
|
* checked:选中的复选框列表项
|
|
*/
|
|
'list': 'list',
|
|
'blockquote': 'blockquote', //true,false
|
|
'indent': 'indent', //缩进值0-8
|
|
'color': 'color',
|
|
'background': 'backgroundColor',
|
|
'size': 'fontSize',
|
|
'script': 'script', //super,sub
|
|
'header': 'header', //1-6
|
|
}
|
|
console.log(eobj[key], value)
|
|
this.editorCtx?.format(eobj[key], value)
|
|
},
|
|
// #endif
|
|
// h5端
|
|
onAddlisentMesage() {
|
|
// #ifdef WEB
|
|
let t = this;
|
|
window.addEventListener('message', function (event) {
|
|
if (event.data.iframeId == t.id && event.data.action == 'onJSBridgeReady') {
|
|
clearTimeout(t.tid2)
|
|
t.tid2 = setTimeout(function () {
|
|
t.realLoaded = true;
|
|
t.drawer(t._value, t.isHtml)
|
|
t.$emit("init")
|
|
t.eventJsCall('setBodyStyle', t._nodeStyle)
|
|
}, 50);
|
|
}
|
|
|
|
if (event.data.iframeId == t.id && event.data.action == 'offsetHeight') {
|
|
t.boxHeight = event.data.data + 20
|
|
|
|
} else if (event.data.iframeId == t.id && event.data.action == 'toValue') {
|
|
t.$emit('getValue', event.data.data)
|
|
} else if (event.data.iframeId == t.id && event.data.action == 'click') {
|
|
let dataStr = JSON.stringify(event.data.data);
|
|
let dataJson = JSON.parseObject(dataStr)!
|
|
t.$emit('tagClick', dataJson)
|
|
} else if (event.data.iframeId == t.id && event.data.action == 'fontStyleOpts') {
|
|
let localOptsNow = event.data.data as xEditeOptsType;
|
|
t.optsStatus = localOptsNow;
|
|
}
|
|
|
|
|
|
});
|
|
// #endif
|
|
},
|
|
|
|
onMessage(event : WebViewMessageEvent) {
|
|
let t = this;
|
|
|
|
let msgdatas = event.detail.data
|
|
|
|
if (msgdatas.length == 0) return;
|
|
|
|
// #ifdef APP-ANDROID || APP-HARMONY
|
|
if (msgdatas.length > 0) {
|
|
let dataStr = JSON.stringify(event.detail);
|
|
let dataJson = JSON.parseObject(dataStr)!
|
|
let msgeAr = dataJson.getArray<UTSJSONObject>('data')!
|
|
|
|
let msg = msgeAr[0]!
|
|
|
|
let ac = msg["action"] as string;
|
|
if (ac == 'offsetHeight') {
|
|
// const h = msg["data"]! as number;
|
|
// t.boxHeight = h + 25
|
|
} else if (ac == 'toValue') {
|
|
t.$emit('getValue', msg["data"]! as string)
|
|
} else if (ac == 'click') {
|
|
|
|
t.$emit('tagClick', msg['data']! as UTSJSONObject)
|
|
} else if (ac == 'fontStyleOpts') {
|
|
|
|
let datamsg = JSON.stringify(msg['data']!)!;
|
|
let localOptsNow = JSON.parseObject<xEditeOptsType>(datamsg! as string)! as xEditeOptsType;
|
|
t.optsStatus = localOptsNow;
|
|
}
|
|
}
|
|
// #endif
|
|
|
|
// #ifdef APP-IOS
|
|
if (msgdatas.length > 0) {
|
|
let msg = msgdatas[0]
|
|
let ac = msg['action'] as string;
|
|
if (ac == 'offsetHeight') {
|
|
// t.boxHeight = (msg['data']! as Number) + 25
|
|
} else if (ac == 'toValue') {
|
|
t.$emit('getValue', msg['data'])
|
|
} else if (ac == 'click') {
|
|
let dataStr = JSON.stringify(msg['data']);
|
|
let dataJson = JSON.parseObject(dataStr)!
|
|
t.$emit('tagClick', dataJson)
|
|
} else if (ac == 'fontStyleOpts') {
|
|
let localOptsNow = msg['data']! as xEditeOptsType;
|
|
t.optsStatus = localOptsNow;
|
|
}
|
|
}
|
|
// #endif
|
|
|
|
},
|
|
|
|
drawer(value : string, isHtml : boolean) {
|
|
if (!this.realLoaded) {
|
|
uni.showToast({ title: "未初始化完成", icon: 'none' })
|
|
return;
|
|
}
|
|
// #ifdef WEB
|
|
this.eventJsCall('markdown', JSON.stringify({ value: encodeURIComponent(value), render: true }))
|
|
// #endif
|
|
// #ifdef APP-IOS
|
|
this.eventJsCall('markdown', JSON.stringify({ value: encodeURIComponent(value), render: true }))
|
|
// #endif
|
|
// #ifdef APP-ANDROID || APP-HARMONY
|
|
|
|
this.eventJsCall('markdown', JSON.stringify({ value: isHtml ? btoa(encodeURIComponent(value)!) : value, render: isHtml ? 'android' : '' }))
|
|
// #endif
|
|
|
|
},
|
|
|
|
eventJsCall(callfun : string, str : string) {
|
|
// #ifdef WEB
|
|
var iframe = document.getElementById(this.id);
|
|
if (!iframe) return;
|
|
if ((iframe.contentWindow[callfun] || null)) {
|
|
iframe.contentWindow[callfun](str, this.isHtml);
|
|
}
|
|
// #endif
|
|
// #ifdef APP
|
|
this.webviewContext?.evalJS(`${callfun}(${str},${this.isHtml})`)
|
|
// #endif
|
|
|
|
},
|
|
|
|
setEventContent(callanem : string, value : string, ishtml : boolean) {
|
|
if (!this.realLoaded) {
|
|
uni.showToast({ title: "未初始化完成", icon: 'none' })
|
|
return;
|
|
}
|
|
const _this = this;
|
|
function evenjscall(callfun : string, str : string, isHtml : boolean) {
|
|
// #ifdef WEB
|
|
var iframe = document.getElementById(_this.id);
|
|
if (!iframe) return;
|
|
if ((iframe.contentWindow[callfun] || null)) {
|
|
iframe.contentWindow[callfun](str, isHtml);
|
|
}
|
|
// #endif
|
|
// #ifdef APP
|
|
_this.webviewContext?.evalJS(`${callfun}(${str},${isHtml})`)
|
|
// #endif
|
|
}
|
|
|
|
|
|
// #ifdef WEB
|
|
evenjscall(callanem, JSON.stringify({ value: encodeURIComponent(value), render: true }), ishtml)
|
|
// #endif
|
|
// #ifdef APP-IOS || APP-HARMONY
|
|
evenjscall(callanem, JSON.stringify({ value: encodeURIComponent(value), render: true }), ishtml)
|
|
// #endif
|
|
// #ifdef APP-ANDROID
|
|
let rendervalue = (ishtml ? 'android' : '') as string
|
|
let cvalue = (ishtml ? btoa(encodeURIComponent(value)!) : value) as any
|
|
let resultValue = JSON.stringify({ value: cvalue, render: rendervalue })! as string
|
|
evenjscall(callanem, resultValue, ishtml)
|
|
// #endif
|
|
|
|
|
|
},
|
|
|
|
/**
|
|
* 获取html内容。注意本函数不会返回内容,你要通过事件getValue得到html内容.
|
|
* @public
|
|
*/
|
|
getHtml() {
|
|
// #ifdef WEB
|
|
var iframe = document.getElementById(this.id);
|
|
if (!iframe) return;
|
|
|
|
iframe.contentWindow['getHtml']()
|
|
// #endif
|
|
// #ifdef APP
|
|
this.webviewContext?.evalJS(`getHtml()`)
|
|
// #endif
|
|
// #ifdef MP
|
|
this.$emit('getValue', this.htmlMpContent)
|
|
// #endif
|
|
},
|
|
appWebViewLoaded() {
|
|
this.realLoaded = true;
|
|
this.drawer(this._value, this.isHtml)
|
|
/**
|
|
* 图表加载初始化完成后触发此事件。
|
|
*/
|
|
this.$emit("init")
|
|
// setBodyStyle
|
|
this.eventJsCall('setBodyStyle', this._nodeStyle)
|
|
},
|
|
/**
|
|
* 获取选区
|
|
*/
|
|
getSelected() {
|
|
|
|
},
|
|
setFontStyle(key : string, value : boolean | number | string) {
|
|
// #ifndef MP
|
|
this.setEventContent('setSelectedStyle', JSON.stringify({ key: key, value: value }), true)
|
|
this.$nextTick(() => {
|
|
if (key == 'b') {
|
|
this.optsStatus.b = value as boolean;
|
|
} else if (key == 'u') {
|
|
this.optsStatus.u = value as boolean;
|
|
} else if (key == 'i') {
|
|
this.optsStatus.i = value as boolean;
|
|
} else if (key == 's') {
|
|
this.optsStatus.s = value as boolean;
|
|
} else if (key == 'header') {
|
|
this.optsStatus.header = value as number;
|
|
} else if (key == 'align') {
|
|
this.optsStatus.align = value as xEditeAlign;
|
|
} else if (key == 'list') {
|
|
this.optsStatus.list = value as xEditeListType;
|
|
} else if (key == 'background') {
|
|
this.optsStatus.list = value as string;
|
|
} else if (key == 'size') {
|
|
this.optsStatus.size = value as string;
|
|
}
|
|
|
|
})
|
|
// #endif
|
|
// #ifdef MP
|
|
this.setFormart(key, value)
|
|
console.log(key)
|
|
// #endif
|
|
|
|
},
|
|
|
|
getBgColor(isActive : boolean) : string {
|
|
if (isActive) return this._activeColor;
|
|
return this._color
|
|
},
|
|
getFontColor(isActive : boolean) : string {
|
|
if (isActive || this._isDark) return "#ffffff";
|
|
return "#333333"
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<template>
|
|
<view class="xEdite" :style="{width:_width,height:_height}">
|
|
<view v-if="!isLoaded"
|
|
style="width:100%;height:100%;display: flex;justify-content: center;align-items: center;flex-direction: row;">
|
|
<x-icon color="primary" :spin="true" name="loader-4-line"></x-icon>
|
|
</view>
|
|
|
|
<view class="FontTabsGroup">
|
|
<view class="fontTabs" style="flex:1;margin-right: 2px;">
|
|
<view @click="setFontStyle('b',!optsStatus.b)" class="xEditorBtns"
|
|
:style="{backgroundColor:getBgColor(optsStatus.b)}">
|
|
<x-icon :color="getFontColor(optsStatus.b)" name="bold"></x-icon>
|
|
</view>
|
|
<view @click="setFontStyle('i',!optsStatus.i)" class="xEditorBtns"
|
|
:style="{backgroundColor:getBgColor(optsStatus.i)}">
|
|
<x-icon :color="getFontColor(optsStatus.i)" name="italic"></x-icon>
|
|
</view>
|
|
<view @click="setFontStyle('u',!optsStatus.u)" class="xEditorBtns"
|
|
:style="{backgroundColor:getBgColor(optsStatus.u)}">
|
|
<x-icon :color="getFontColor(optsStatus.u)" name="underline"></x-icon>
|
|
</view>
|
|
<view @click="setFontStyle('s',!optsStatus.s)" class="xEditorBtns"
|
|
:style="{backgroundColor:getBgColor(optsStatus.s)}">
|
|
<x-icon :color="getFontColor(optsStatus.s)" name="strikethrough"></x-icon>
|
|
</view>
|
|
|
|
</view>
|
|
<view class="fontTabs" style="width: 36px;align-self: stretch;margin-right: 2px;">
|
|
<x-popover position="bc" style="flex:1;align-self: stretch;">
|
|
<view class="xEditorBtns" :style="{backgroundColor:getBgColor(optsStatus.size!='')}">
|
|
<x-icon :color="getFontColor(optsStatus.size!='')" name="font-size"></x-icon>
|
|
</view>
|
|
<template #menu>
|
|
<view style="width:110px">
|
|
<view v-for="item in ['12px', '14px', '16px', '18px', '24px']" :key="item"
|
|
@click="setFontStyle('size',optsStatus.size==item?'':item)" class="fontTabsCirlItem"
|
|
:style="{backgroundColor:getBgColor(optsStatus.size==item)}">
|
|
<x-text :color="getFontColor(optsStatus.size==item)">{{item}}</x-text>
|
|
</view>
|
|
</view>
|
|
|
|
</template>
|
|
</x-popover>
|
|
</view>
|
|
|
|
<view class="fontTabs" style="width: 120px;align-self: stretch;">
|
|
|
|
<x-popover position="bc" style="flex:1;align-self: stretch;">
|
|
<view class="xEditorBtns" :style="{backgroundColor:getBgColor(optsStatus.background!='')}">
|
|
<view class="fontTabsCirl"
|
|
:style="{backgroundColor:optsStatus.background==''?'transparent':optsStatus.background}">
|
|
</view>
|
|
<x-icon :color="getFontColor(optsStatus.background!='')" name="drop-fill"></x-icon>
|
|
</view>
|
|
<template #menu>
|
|
<view style="width:110px">
|
|
<view @click="setFontStyle('background',item)" v-for="(item,index) in _customColos"
|
|
:key="index" class="fontTabsCirlItem"
|
|
:style="{backgroundColor:_isDark?'#222222':'#ffffff'}">
|
|
<view class="fontTabsCirl2" :style="{backgroundColor:item}"></view>
|
|
</view>
|
|
</view>
|
|
|
|
</template>
|
|
</x-popover>
|
|
|
|
<x-popover position="br" style="flex:1;align-self: stretch;">
|
|
<view class="xEditorBtns" :style="{backgroundColor:getBgColor(optsStatus.color!='')}">
|
|
<view class="fontTabsCirl"
|
|
:style="{backgroundColor:optsStatus.color==''?'transparent':optsStatus.color}"></view>
|
|
<x-icon :color="getFontColor(optsStatus.color!='')" name="font-color"></x-icon>
|
|
</view>
|
|
<template #menu>
|
|
<view style="width:110px">
|
|
<view @click="setFontStyle('color',item)" v-for="(item,index) in _customColos" :key="index"
|
|
class="fontTabsCirlItem" :style="{backgroundColor:_isDark?'#222222':'#ffffff'}">
|
|
<view class="fontTabsCirl2" :style="{backgroundColor:item}"></view>
|
|
</view>
|
|
</view>
|
|
|
|
</template>
|
|
</x-popover>
|
|
|
|
|
|
|
|
|
|
|
|
</view>
|
|
</view>
|
|
<view class="FontTabsGroup">
|
|
|
|
<view class="fontTabs" style="width: 36px;align-self: stretch;margin-right: 2px;">
|
|
<x-popover position="bl" style="flex:1;align-self: stretch;">
|
|
<view class="xEditorBtns" :style="{backgroundColor:getBgColor(optsStatus.header!=0)}">
|
|
<x-icon :color="getFontColor(optsStatus.header!=0)" name="heading"></x-icon>
|
|
</view>
|
|
<template #menu>
|
|
<view style="width:110px">
|
|
<view v-for="item in 6" :key="item"
|
|
@click="setFontStyle('header',optsStatus.header==item?0:item)" class="fontTabsCirlItem"
|
|
:style="{backgroundColor:getBgColor(optsStatus.header==item)}">
|
|
<x-icon :color="getFontColor(optsStatus.s)" :name="`h-${item}`"></x-icon>
|
|
</view>
|
|
</view>
|
|
|
|
</template>
|
|
</x-popover>
|
|
|
|
</view>
|
|
<view class="fontTabs" style="flex:1;margin-right: 2px;">
|
|
<view v-for="(item,index) in ['left','center','right']" :key="index"
|
|
@click="setFontStyle('align',optsStatus.align==item?'':item)" class="xEditorBtns"
|
|
:style="{backgroundColor:getBgColor(optsStatus.align==item)}">
|
|
<x-icon :color="getFontColor(optsStatus.align==item)" :name="`align-${item}`"></x-icon>
|
|
</view>
|
|
</view>
|
|
<view class="fontTabs" style="align-self: stretch;flex:1">
|
|
<view v-for="(item,index) in listDataItems" :key="index"
|
|
@click="setFontStyle('list',optsStatus.list==item.name?'':item.name)" class="xEditorBtns"
|
|
:style="{backgroundColor:getBgColor(optsStatus.list==item.name)}">
|
|
<x-icon :color="getFontColor(optsStatus.list==item.name)" :name="item.icon"></x-icon>
|
|
</view>
|
|
|
|
</view>
|
|
</view>
|
|
<view style="height: 8px;"></view>
|
|
|
|
<view style="flex:1;">
|
|
|
|
<!-- #ifdef APP||WEB -->
|
|
<web-view v-if="isLoaded" :horizontalScrollBarAccess="true" :verticalScrollBarAccess="false"
|
|
class="xMarkdownNoevents" @load="appWebViewLoaded" :id="id" src="/hybrid/html/edite.html"
|
|
:style="{width:'100%',height:'100%',opacity:isLoaded?1:0}" @message="onMessage"></web-view>
|
|
|
|
<!-- #endif -->
|
|
<!-- #ifdef MP -->
|
|
<editor class="xMarkdownNoevents" id="editor" @ready="mpOnInit" @itemclick="onItemClick" @focus="onfocus"
|
|
@statuschange="onstatuschange" :selectable="true" placeholder="请输入"
|
|
:style="[{width:'100%',height:'100%'},_nodeStyle]"></editor>
|
|
<!-- #endif -->
|
|
<!-- 兼容安卓。webview到4.19+页面无法滚动 -->
|
|
<!-- <view v-if="!_edite" class="xMarkdownAndrod"></view> -->
|
|
</view>
|
|
</view>
|
|
</template>
|
|
<style scoped>
|
|
.xEdite{
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.xEditorBtns {
|
|
height: 40px;
|
|
flex: 1;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
flex-direction: row;
|
|
}
|
|
|
|
.fontTabsCirl {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 10px;
|
|
border: 1px solid #b2b2b2;
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.fontTabsCirl2 {
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 20px;
|
|
border: 1px solid #b2b2b2;
|
|
}
|
|
|
|
.fontTabsCirlItem {
|
|
height: 36px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
flex-direction: row;
|
|
}
|
|
|
|
.fontTabs {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 2px;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.FontTabsGroup {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.xMarkdownAndrod {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 10;
|
|
}
|
|
|
|
.xMarkdownNoevents {
|
|
/* pointer-events: none; */
|
|
width: 100%;
|
|
/* height: 100%; */
|
|
}
|
|
</style> |