123
This commit is contained in:
parent
75e13dff0e
commit
918331c0ad
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<view>
|
||||
<tn-nav-bar :isBack="false" backTitle="" :bottomShadow="true" backgroundColor="#FFFFFF">
|
||||
<view class="custom-nav tn-flex tn-flex-col-center tn-flex-row-left">
|
||||
<view style="padding-left: 15rpx;" @click="goBack()">
|
||||
<text class="tn-icon-left" style="font-size: 40rpx;"></text>
|
||||
</view>
|
||||
<view class="tn-margin-top"
|
||||
style=";text-shadow: 1rpx 0 0 #FFF, 0 1rpx 0 #FFF, -1rpx 0 0 #FFF , 0 -1rpx 0 #FFF;">
|
||||
<tn-tabs :list="[{name:'活动签到'}]" :current="topCurrent" activeColor="#000" :bold="false"
|
||||
:fontSize="36"></tn-tabs>
|
||||
</view>
|
||||
</view>
|
||||
</tn-nav-bar>
|
||||
<view :style="{paddingTop: vuex_custom_bar_height + 20+'px'}" v-if="info!=''">
|
||||
<view style="padding:0rpx 30rpx;text-align: center;">
|
||||
<view style="">
|
||||
<text class="tn-icon-trust-fill" style="font-size: 250rpx;color: #01BEFF;"></text>
|
||||
</view>
|
||||
<view style="font-size: 38rpx;font-weight: 600;margin-top: 40rpx;">{{info.activity_name}}</view>
|
||||
<view style="margin-top: 100rpx;font-size: 32rpx;font-weight: 600;">
|
||||
<view>开始时间:{{info.activity_start_time}}</view>
|
||||
<view style="margin: 20rpx 0rpx;">-</view>
|
||||
<view>结束时间:{{info.activity_end_time}}</view>
|
||||
</view>
|
||||
<view style="margin-top: 100rpx;">
|
||||
<tn-button @click="sign()" backgroundColor="#01BEFF" :shadow="true" fontColor="#ffffff"
|
||||
width="200rpx" height="200rpx" :fontSize="40" shape="icon" margin="10rpx 10rpx">签到</tn-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
activitySign,
|
||||
eventInfo
|
||||
} from '@/util/api.js';
|
||||
import store from '@/store/index.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
topCurrent: 1,
|
||||
activity_id: '',
|
||||
member_id: '',
|
||||
association_id: '',
|
||||
info: ''
|
||||
}
|
||||
},
|
||||
onLoad(query) {
|
||||
const q = decodeURIComponent(query.q) // 获取到二维码原始链接内容
|
||||
var data = this.getUrlParams(q);
|
||||
this.activity_id = data.id;
|
||||
this.association_id = data.association_id;
|
||||
var that = this;
|
||||
console.log('---1---');
|
||||
getApp().getUserLogin((r) => {
|
||||
console.log('---2---');
|
||||
that.member_id = r.data.id;
|
||||
that.getEventInfo();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getEventInfo() {
|
||||
console.log('---3---');
|
||||
eventInfo({
|
||||
association_id: this.association_id,
|
||||
id: this.activity_id
|
||||
})
|
||||
.then(res => {
|
||||
console.log(res);
|
||||
if (res.code == 1) {
|
||||
this.info = res.data;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
uni.showToast({
|
||||
title: error,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
})
|
||||
},
|
||||
sign() {
|
||||
activitySign({
|
||||
activity_id: this.activity_id,
|
||||
member_id: this.member_id,
|
||||
association_id: this.association_id,
|
||||
})
|
||||
.then(res => {
|
||||
console.log(res);
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
uni.showToast({
|
||||
title: error,
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
})
|
||||
},
|
||||
goBack() {
|
||||
if (getCurrentPages().length > 1) {
|
||||
uni.navigateBack()
|
||||
} else {
|
||||
uni.redirectTo({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
|
||||
}
|
||||
},
|
||||
getUrlParams(url) {
|
||||
// \w+ 表示匹配至少一个(数字、字母及下划线), [\u4e00-\u9fa5]+ 表示匹配至少一个中文字符
|
||||
let pattern = /(\w+|[\u4e00-\u9fa5]+)=(\w+|[\u4e00-\u9fa5]+)/ig;
|
||||
let result = {};
|
||||
url.replace(pattern, ($, $1, $2) => {
|
||||
result[$1] = $2;
|
||||
})
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
|
@ -0,0 +1,109 @@
|
|||
## 1.4.0(2024-05-31)
|
||||
1. 更新示例工程
|
||||
## 1.3.9(2024-05-31)
|
||||
1. 修复调色板无法正常选色的问题
|
||||
## 1.3.8(2024-05-13)
|
||||
1. 更新示例三(微信小程序上使用setContents造成聚焦滚动的处理)
|
||||
## 1.3.7(2024-05-10)
|
||||
1. 修复添加超链接后,不触发input更新当前最新内容的bug
|
||||
## 1.3.6(2024-05-09)
|
||||
1. 文档迁移
|
||||
## 1.3.5(2024-05-09)
|
||||
1. 所有事件携带编辑器id参数,以便循环时能区分处理
|
||||
2. 更新示例工程
|
||||
## 1.3.4(2024-05-08)
|
||||
1. 更新示例工程
|
||||
2. 新增editorId参数
|
||||
## 1.3.3(2024-03-22)
|
||||
1. 修复微信小程序长按无法粘贴的问题
|
||||
## 1.3.2(2024-03-14)
|
||||
1. 更新了toolbar样式与配置,见文档
|
||||
2. 更新示例工程,媒体查询响应式写法
|
||||
3. 优化了只读模式效果,开启只读模式后,文章内容的超链接可正常点击并跳转
|
||||
## 1.3.1(2024-03-14)
|
||||
1. 优化了只读功能,开启只读后自动隐藏工具栏
|
||||
2. 更新示例工程
|
||||
## 1.3.0(2024-03-07)
|
||||
1. 新增addLink的emit事件
|
||||
## 1.2.9(2024-02-23)
|
||||
1. 更新文档
|
||||
## 1.2.8(2024-02-23)
|
||||
1. 新增了添加超链接的工具,toolbar中link字段,默认开启
|
||||
2. 优化了部分逻辑
|
||||
3. 更新文档、更新示例工程
|
||||
## 1.2.7(2024-02-23)
|
||||
1. 更新文档,更新示例工程
|
||||
2. 添加toolbar中图标字体大小可配置项
|
||||
## 1.2.6(2024-02-22)
|
||||
1. 添加导出工具按钮,可将当前已编辑的html导出至页面解析
|
||||
2. 超链接工具按钮正在尝试开发中(貌似目前官方不支持)
|
||||
## 1.2.5(2024-02-19)
|
||||
1. 更新示例工程(吸顶写法)
|
||||
2. 完善调色板功能
|
||||
## 1.2.4(2024-02-18)
|
||||
1. 修复工具栏颜色按钮底色动态切换问题
|
||||
## 1.2.3(2024-02-18)
|
||||
1. 更新示例工程
|
||||
## 1.2.2(2024-02-18)
|
||||
1. 删除log调试打印
|
||||
## 1.2.1(2024-02-18)
|
||||
1. 修复了颜色图标不会动态切换的问题
|
||||
## 1.2.0(2024-02-18)
|
||||
1. 修复选择颜色时会将所选文字删除的bug
|
||||
## 1.1.9(2024-02-04)
|
||||
1. 更新示例工程
|
||||
## 1.1.8(2024-02-04)
|
||||
1. 文档修改
|
||||
## 1.1.7(2024-02-04)
|
||||
1. 新增toolbar配置项,可自由配置工具栏工具列表
|
||||
2. 移除组件内原templates属性,默认初始化编辑器内容请看文档使用方式示例
|
||||
3. 更新文档
|
||||
## 1.1.6(2024-01-31)
|
||||
1. 更好的兼容vue2了,修复在vue2下高度可能超出的问题
|
||||
2. 示例工程兼容vue2
|
||||
## 1.1.5(2024-01-30)
|
||||
1. 修复工具栏字体按钮无效的问题
|
||||
## 1.1.4(2024-01-30)
|
||||
1. 解决默认初始化内容时前缀空格或缩进无效的问题
|
||||
2. 解决点击工具栏高亮状态后输入内容时便失去高亮的bug
|
||||
3. 更新示例工程
|
||||
## 1.1.3(2024-01-23)
|
||||
1. 重写高度动态计算逻辑,现在对不同屏幕尺寸的适应性更强了
|
||||
## 1.1.2(2024-01-17)
|
||||
1. 修复分割线会生成多条的问题
|
||||
## 1.1.1(2024-01-15)
|
||||
1. 更新文档
|
||||
## 1.1.0(2024-01-15)
|
||||
1. insertText方法在插入内容的时候会移动光标聚焦,导致焦点回滚到视口处
|
||||
2. 更新示例工程
|
||||
## 1.0.9(2024-01-04)
|
||||
1. 更新文档
|
||||
## 1.0.8(2024-01-04)
|
||||
1. 修复h5端官方cdn请求失败的问题,详见问答贴:https://ask.dcloud.net.cn/article/40900
|
||||
## 1.0.7(2024-01-03)
|
||||
1. 移除v-bind="$attrs",该写法在微信小程序不支持
|
||||
## 1.0.6(2023-12-29)
|
||||
1. 更新文档
|
||||
## 1.0.5(2023-12-29)
|
||||
1. 更新了init方法,可以使用返回的editor实例尽情自定义
|
||||
2. 组件在<editor>上添加v-bind="$attrs"属性穿透,可以使用原editor参数,官方文档:https://uniapp.dcloud.net.cn/component/editor.html
|
||||
## 1.0.4(2023-12-29)
|
||||
1. 优化了切换文字和背景颜色是,可能会导致切换后不生效的问题
|
||||
2. 修复在部分设备上的微信小程序中可能会存在颜色版无法正常滑动的问题
|
||||
3. 更友好的交互体验:添加图标悬停字样描述、添加格式化文本弹窗确认
|
||||
4. 有插入视频的需求,暂时可能无法实现,官方给予的回复是:目前各端的eidtor组件都不能直接插入视频,编辑时可以采用视频封面占位,并在图片中保存视频信息,在预览时再还原为视频。
|
||||
## 1.0.3(2023-10-13)
|
||||
1. 更新readme文档
|
||||
2. 更新调整组件示例项目,添加插件代码中部分注释
|
||||
## 1.0.2(2023-10-13)
|
||||
1. 更新uni_modules规范,可一键导入组件
|
||||
2. 更新组件示例项目(包括使用uniCloud.uploadFile多选上传图片示例方法)
|
||||
## 1.0.1(2023-10-12)
|
||||
1. 修复小程序中自动聚焦滚动到富文本组件区域的bug
|
||||
2. 略微调整了富文本上方toolbar工具栏中按钮的大小尺寸
|
||||
## 1.0.0(2023-9-19)
|
||||
1. 新增字体与背景颜色板
|
||||
2. 可自定义预设内容模板
|
||||
3. 解决官方样例在小程序和app部分报错不兼容的问题
|
||||
4. 可配合云存储上传富文本中插入的图片 本质上是基于官方内置富文本editor组件改版封装,所以官方有的功能都有,官方能兼容的也都兼容
|
||||
|
|
@ -0,0 +1,825 @@
|
|||
<template>
|
||||
<view v-if="show" class="t-wrapper" @touchmove.stop.prevent="moveHandle">
|
||||
<view class="t-mask" :class="{ active: active }" @click.stop="close"></view>
|
||||
<view class="t-box" :class="{ active: active }">
|
||||
<view class="t-header">
|
||||
<view class="t-header-button" @click="close">取消</view>
|
||||
<view class="t-header-button" @click="confirm">确认</view>
|
||||
</view>
|
||||
<view class="t-color__box" :style="{ background: 'rgb(' + bgcolor.r + ',' + bgcolor.g + ',' + bgcolor.b + ')' }">
|
||||
<view
|
||||
class="t-background boxs"
|
||||
@touchstart="touchstart($event, 0)"
|
||||
@touchmove="touchmove($event, 0)"
|
||||
@touchend="touchend($event, 0)"
|
||||
>
|
||||
<view class="t-color-mask"></view>
|
||||
<view class="t-pointer" :style="{ top: site[0].top - 8 + 'px', left: site[0].left - 8 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-control__box">
|
||||
<view class="t-control__color">
|
||||
<view
|
||||
class="t-control__color-content"
|
||||
:style="{ background: 'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + rgba.a + ')' }"
|
||||
></view>
|
||||
</view>
|
||||
<view class="t-control-box__item">
|
||||
<view
|
||||
class="t-controller boxs"
|
||||
@touchstart="touchstart($event, 1)"
|
||||
@touchmove="touchmove($event, 1)"
|
||||
@touchend="touchend($event, 1)"
|
||||
>
|
||||
<view class="t-hue">
|
||||
<view class="t-circle" :style="{ left: site[1].left - 12 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="t-controller boxs"
|
||||
@touchstart="touchstart($event, 2)"
|
||||
@touchmove="touchmove($event, 2)"
|
||||
@touchend="touchend($event, 2)"
|
||||
>
|
||||
<view class="t-transparency">
|
||||
<view class="t-circle" :style="{ left: site[2].left - 12 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-result__box">
|
||||
<view v-if="mode" class="t-result__item">
|
||||
<view class="t-result__box-input">{{ hex }}</view>
|
||||
<view class="t-result__box-text">HEX</view>
|
||||
</view>
|
||||
<template v-else>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{ rgba.r }}</view>
|
||||
<view class="t-result__box-text">R</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{ rgba.g }}</view>
|
||||
<view class="t-result__box-text">G</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{ rgba.b }}</view>
|
||||
<view class="t-result__box-text">B</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{ rgba.a }}</view>
|
||||
<view class="t-result__box-text">A</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view class="t-result__item t-select" @click="select">
|
||||
<view class="t-result__box-input">
|
||||
<view>切换</view>
|
||||
<view>模式</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-alternative">
|
||||
<view class="t-alternative__item" v-for="(item, index) in colorList" :key="index">
|
||||
<view
|
||||
class="t-alternative__item-content"
|
||||
:style="{ background: 'rgba(' + item.r + ',' + item.g + ',' + item.b + ',' + item.a + ')' }"
|
||||
@click="selectColor(item)"
|
||||
></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
spareColor: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
active: false,
|
||||
// rgba 颜色
|
||||
rgba: {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
},
|
||||
// hsb 颜色
|
||||
hsb: {
|
||||
h: 0,
|
||||
s: 0,
|
||||
b: 0
|
||||
},
|
||||
site: [
|
||||
{
|
||||
top: 0,
|
||||
left: 0
|
||||
},
|
||||
{
|
||||
left: 0
|
||||
},
|
||||
{
|
||||
left: 0
|
||||
}
|
||||
],
|
||||
index: 0,
|
||||
bgcolor: {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
},
|
||||
hex: '#000000',
|
||||
mode: true,
|
||||
colorList: [
|
||||
{
|
||||
r: 244,
|
||||
g: 67,
|
||||
b: 54,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 233,
|
||||
g: 30,
|
||||
b: 99,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 156,
|
||||
g: 39,
|
||||
b: 176,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 103,
|
||||
g: 58,
|
||||
b: 183,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 63,
|
||||
g: 81,
|
||||
b: 181,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 33,
|
||||
g: 150,
|
||||
b: 243,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 3,
|
||||
g: 169,
|
||||
b: 244,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 0,
|
||||
g: 188,
|
||||
b: 212,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 0,
|
||||
g: 150,
|
||||
b: 136,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 76,
|
||||
g: 175,
|
||||
b: 80,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 139,
|
||||
g: 195,
|
||||
b: 74,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 205,
|
||||
g: 220,
|
||||
b: 57,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 255,
|
||||
g: 235,
|
||||
b: 59,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 255,
|
||||
g: 193,
|
||||
b: 7,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 255,
|
||||
g: 152,
|
||||
b: 0,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 255,
|
||||
g: 87,
|
||||
b: 34,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 121,
|
||||
g: 85,
|
||||
b: 72,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 158,
|
||||
g: 158,
|
||||
b: 158,
|
||||
a: 1
|
||||
},
|
||||
{
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0.5
|
||||
},
|
||||
{
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.ready()
|
||||
},
|
||||
methods: {
|
||||
ready() {
|
||||
this.rgba = this.color
|
||||
if (this.spareColor.length !== 0) {
|
||||
this.colorList = this.spareColor
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
init() {
|
||||
// hsb 颜色
|
||||
this.hsb = this.rgbToHex(this.rgba)
|
||||
// this.setColor();
|
||||
this.setValue(this.rgba)
|
||||
},
|
||||
moveHandle() {},
|
||||
open() {
|
||||
this.show = true
|
||||
this.$nextTick(() => {
|
||||
this.init()
|
||||
setTimeout(() => {
|
||||
this.active = true
|
||||
setTimeout(() => {
|
||||
this.getSelectorQuery()
|
||||
}, 350)
|
||||
}, 50)
|
||||
})
|
||||
},
|
||||
close() {
|
||||
this.active = false
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.show = false
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
confirm() {
|
||||
this.close()
|
||||
this.$emit('confirm', {
|
||||
rgba: this.rgba,
|
||||
hex: this.hex
|
||||
})
|
||||
},
|
||||
// 选择模式
|
||||
select() {
|
||||
this.mode = !this.mode
|
||||
},
|
||||
// 常用颜色选择
|
||||
selectColor(item) {
|
||||
this.setColorBySelect(item)
|
||||
},
|
||||
touchstart(e, index) {
|
||||
const { pageX, pageY, clientX, clientY } = e.touches[0]
|
||||
// 部分机型可能没有pageX或clientX,因此此处需要做兼容
|
||||
this.moveX = clientX || pageX
|
||||
this.moveY = clientY || pageY
|
||||
this.setPosition(this.moveX, this.moveY, index)
|
||||
},
|
||||
touchmove(e, index) {
|
||||
const { pageX, pageY, clientX, clientY } = e.touches[0]
|
||||
this.moveX = clientX || pageX
|
||||
this.moveY = clientY || pageY
|
||||
this.setPosition(this.moveX, this.moveY, index)
|
||||
},
|
||||
touchend(e, index) {},
|
||||
/**
|
||||
* 设置位置
|
||||
*/
|
||||
setPosition(x, y, index) {
|
||||
this.index = index
|
||||
const { top, left, width, height } = this.position[index]
|
||||
// 设置最大最小值
|
||||
|
||||
this.site[index].left = Math.max(0, Math.min(parseInt(x - left), width))
|
||||
if (index === 0) {
|
||||
this.site[index].top = Math.max(0, Math.min(parseInt(y - top), height))
|
||||
// 设置颜色
|
||||
this.hsb.s = parseInt((100 * this.site[index].left) / width)
|
||||
this.hsb.b = parseInt(100 - (100 * this.site[index].top) / height)
|
||||
this.setColor()
|
||||
this.setValue(this.rgba)
|
||||
} else {
|
||||
this.setControl(index, this.site[index].left)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 设置 rgb 颜色
|
||||
*/
|
||||
setColor() {
|
||||
const rgb = this.HSBToRGB(this.hsb)
|
||||
this.rgba.r = rgb.r
|
||||
this.rgba.g = rgb.g
|
||||
this.rgba.b = rgb.b
|
||||
},
|
||||
/**
|
||||
* 设置二进制颜色
|
||||
* @param {Object} rgb
|
||||
*/
|
||||
setValue(rgb) {
|
||||
this.hex = '#' + this.rgbToHex(rgb)
|
||||
},
|
||||
setControl(index, x) {
|
||||
const { top, left, width, height } = this.position[index]
|
||||
|
||||
if (index === 1) {
|
||||
this.hsb.h = parseInt((360 * x) / width)
|
||||
this.bgcolor = this.HSBToRGB({
|
||||
h: this.hsb.h,
|
||||
s: 100,
|
||||
b: 100
|
||||
})
|
||||
this.setColor()
|
||||
} else {
|
||||
this.rgba.a = (x / width).toFixed(1)
|
||||
}
|
||||
this.setValue(this.rgba)
|
||||
},
|
||||
/**
|
||||
* rgb 转 二进制 hex
|
||||
* @param {Object} rgb
|
||||
*/
|
||||
rgbToHex(rgb) {
|
||||
let hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)]
|
||||
hex.map(function (str, i) {
|
||||
if (str.length == 1) {
|
||||
hex[i] = '0' + str
|
||||
}
|
||||
})
|
||||
return hex.join('')
|
||||
},
|
||||
setColorBySelect(getrgb) {
|
||||
const { r, g, b, a } = getrgb
|
||||
let rgb = {}
|
||||
rgb = {
|
||||
r: r ? parseInt(r) : 0,
|
||||
g: g ? parseInt(g) : 0,
|
||||
b: b ? parseInt(b) : 0,
|
||||
a: a ? a : 0
|
||||
}
|
||||
this.rgba = rgb
|
||||
this.hsb = this.rgbToHsb(rgb)
|
||||
this.changeViewByHsb()
|
||||
},
|
||||
changeViewByHsb() {
|
||||
const [a, b, c] = this.position
|
||||
this.site[0].left = parseInt((this.hsb.s * a.width) / 100)
|
||||
this.site[0].top = parseInt(((100 - this.hsb.b) * a.height) / 100)
|
||||
this.setColor(this.hsb.h)
|
||||
this.setValue(this.rgba)
|
||||
this.bgcolor = this.HSBToRGB({
|
||||
h: this.hsb.h,
|
||||
s: 100,
|
||||
b: 100
|
||||
})
|
||||
|
||||
this.site[1].left = (this.hsb.h / 360) * b.width
|
||||
this.site[2].left = this.rgba.a * c.width
|
||||
},
|
||||
/**
|
||||
* hsb 转 rgb
|
||||
* @param {Object} 颜色模式 H(hues)表示色相,S(saturation)表示饱和度,B(brightness)表示亮度
|
||||
*/
|
||||
HSBToRGB(hsb) {
|
||||
let rgb = {}
|
||||
let h = Math.round(hsb.h)
|
||||
let s = Math.round((hsb.s * 255) / 100)
|
||||
let v = Math.round((hsb.b * 255) / 100)
|
||||
if (s == 0) {
|
||||
rgb.r = rgb.g = rgb.b = v
|
||||
} else {
|
||||
let t1 = v
|
||||
let t2 = ((255 - s) * v) / 255
|
||||
let t3 = ((t1 - t2) * (h % 60)) / 60
|
||||
if (h == 360) h = 0
|
||||
if (h < 60) {
|
||||
rgb.r = t1
|
||||
rgb.b = t2
|
||||
rgb.g = t2 + t3
|
||||
} else if (h < 120) {
|
||||
rgb.g = t1
|
||||
rgb.b = t2
|
||||
rgb.r = t1 - t3
|
||||
} else if (h < 180) {
|
||||
rgb.g = t1
|
||||
rgb.r = t2
|
||||
rgb.b = t2 + t3
|
||||
} else if (h < 240) {
|
||||
rgb.b = t1
|
||||
rgb.r = t2
|
||||
rgb.g = t1 - t3
|
||||
} else if (h < 300) {
|
||||
rgb.b = t1
|
||||
rgb.g = t2
|
||||
rgb.r = t2 + t3
|
||||
} else if (h < 360) {
|
||||
rgb.r = t1
|
||||
rgb.g = t2
|
||||
rgb.b = t1 - t3
|
||||
} else {
|
||||
rgb.r = 0
|
||||
rgb.g = 0
|
||||
rgb.b = 0
|
||||
}
|
||||
}
|
||||
return {
|
||||
r: Math.round(rgb.r),
|
||||
g: Math.round(rgb.g),
|
||||
b: Math.round(rgb.b)
|
||||
}
|
||||
},
|
||||
rgbToHsb(rgb) {
|
||||
let hsb = {
|
||||
h: 0,
|
||||
s: 0,
|
||||
b: 0
|
||||
}
|
||||
let min = Math.min(rgb.r, rgb.g, rgb.b)
|
||||
let max = Math.max(rgb.r, rgb.g, rgb.b)
|
||||
let delta = max - min
|
||||
hsb.b = max
|
||||
hsb.s = max != 0 ? (255 * delta) / max : 0
|
||||
if (hsb.s != 0) {
|
||||
if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta
|
||||
else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta
|
||||
else hsb.h = 4 + (rgb.r - rgb.g) / delta
|
||||
} else hsb.h = -1
|
||||
hsb.h *= 60
|
||||
if (hsb.h < 0) hsb.h = 0
|
||||
hsb.s *= 100 / 255
|
||||
hsb.b *= 100 / 255
|
||||
return hsb
|
||||
},
|
||||
getSelectorQuery() {
|
||||
const views = uni.createSelectorQuery().in(this)
|
||||
views
|
||||
.selectAll('.boxs')
|
||||
.boundingClientRect((data) => {
|
||||
if (!data || data.length === 0) {
|
||||
setTimeout(() => this.getSelectorQuery(), 20)
|
||||
return
|
||||
}
|
||||
this.position = data
|
||||
// this.site[0].top = data[0].height;
|
||||
// this.site[0].left = 0;
|
||||
// this.site[1].left = data[1].width;
|
||||
// this.site[2].left = data[2].width;
|
||||
this.setColorBySelect(this.rgba)
|
||||
})
|
||||
.exec()
|
||||
},
|
||||
hex2Rgb(hexColor, alpha = 1) {
|
||||
const color = hexColor.slice(1)
|
||||
const r = parseInt(color.slice(0, 2), 16)
|
||||
const g = parseInt(color.slice(2, 4), 16)
|
||||
const b = parseInt(color.slice(4, 6), 16)
|
||||
return {
|
||||
r: r,
|
||||
g: g,
|
||||
b: b,
|
||||
a: alpha
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
spareColor(newVal) {
|
||||
this.colorList = newVal
|
||||
},
|
||||
color(newVal) {
|
||||
this.ready()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.t-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.t-box {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding: 30upx 0;
|
||||
padding-top: 0;
|
||||
background: #fff;
|
||||
transition: all 0.3s;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.t-box.active {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
.t-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 100upx;
|
||||
border-bottom: 1px #eee solid;
|
||||
box-shadow: 1px 0 2px rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-header-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 150upx;
|
||||
height: 100upx;
|
||||
font-size: 30upx;
|
||||
color: #666;
|
||||
padding-left: 20upx;
|
||||
}
|
||||
|
||||
.t-header-button:last-child {
|
||||
justify-content: flex-end;
|
||||
padding-right: 20upx;
|
||||
}
|
||||
|
||||
.t-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
z-index: -1;
|
||||
transition: all 0.3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.t-mask.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.t-color__box {
|
||||
position: relative;
|
||||
height: 400upx;
|
||||
background: rgb(255, 0, 0);
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
margin: 0 20upx;
|
||||
margin-top: 20upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
|
||||
}
|
||||
|
||||
.t-color-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 400upx;
|
||||
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
.t-pointer {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: -8px;
|
||||
z-index: 2;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: 1px #fff solid;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.t-show-color {
|
||||
width: 100upx;
|
||||
height: 50upx;
|
||||
}
|
||||
|
||||
.t-control__box {
|
||||
margin-top: 50upx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
padding-left: 20upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-control__color {
|
||||
flex-shrink: 0;
|
||||
width: 100upx;
|
||||
height: 100upx;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 36upx 36upx;
|
||||
background-position: 0 0, 18upx 18upx;
|
||||
border: 1px #eee solid;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.t-control__color-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.t-control-box__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 0 30upx;
|
||||
}
|
||||
|
||||
.t-controller {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 32upx 32upx;
|
||||
background-position: 0 0, 16upx 16upx;
|
||||
}
|
||||
|
||||
.t-hue {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
|
||||
}
|
||||
|
||||
.t-transparency {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0));
|
||||
}
|
||||
|
||||
.t-circle {
|
||||
position: absolute;
|
||||
/* right: -10px; */
|
||||
top: -2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.t-result__box {
|
||||
margin-top: 20upx;
|
||||
padding: 10upx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-result__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10upx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-result__box-input {
|
||||
padding: 10upx 0;
|
||||
width: 100%;
|
||||
font-size: 28upx;
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
color: #999;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-result__box-text {
|
||||
margin-top: 10upx;
|
||||
font-size: 28upx;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.t-select {
|
||||
flex-shrink: 0;
|
||||
width: 150upx;
|
||||
padding: 0 30upx;
|
||||
}
|
||||
|
||||
.t-select .t-result__box-input {
|
||||
border-radius: 10upx;
|
||||
border: none;
|
||||
color: #999;
|
||||
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-select .t-result__box-input:active {
|
||||
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.t-alternative {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
/* justify-content: space-between; */
|
||||
width: 100%;
|
||||
padding-right: 10upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-alternative__item {
|
||||
margin-left: 12upx;
|
||||
margin-top: 10upx;
|
||||
width: 50upx;
|
||||
height: 50upx;
|
||||
border-radius: 10upx;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 36upx 36upx;
|
||||
background-position: 0 0, 18upx 18upx;
|
||||
border: 1px #eee solid;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.t-alternative__item-content {
|
||||
width: 50upx;
|
||||
height: 50upx;
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.t-alternative__item:active {
|
||||
transition: all 0.3s;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,152 @@
|
|||
<template>
|
||||
<view class="link-edit-container" v-if="showPopup">
|
||||
<view class="link-edit">
|
||||
<view class="title">添加链接</view>
|
||||
<view class="edit">
|
||||
<view class="description">
|
||||
链接描述:
|
||||
<input v-model="descVal" type="text" class="input" placeholder="请输入链接描述" />
|
||||
</view>
|
||||
<view class="address">
|
||||
链接地址:
|
||||
<input v-model="addrVal" type="text" class="input" placeholder="请输入链接地址" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="control">
|
||||
<view class="cancel" @click="close">取消</view>
|
||||
<view class="confirm" @click="onConfirm">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mask"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
showPopup: false,
|
||||
descVal: '',
|
||||
addrVal: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.showPopup = true
|
||||
this.$emit('open')
|
||||
},
|
||||
close() {
|
||||
this.showPopup = false
|
||||
this.descVal = ''
|
||||
this.addrVal = ''
|
||||
this.$emit('close')
|
||||
},
|
||||
onConfirm() {
|
||||
if (!this.descVal) {
|
||||
uni.showToast({
|
||||
title: '请输入链接描述',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!this.addrVal) {
|
||||
uni.showToast({
|
||||
title: '请输入链接地址',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$emit('confirm', {
|
||||
text: this.descVal,
|
||||
href: this.addrVal
|
||||
})
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.link-edit-container {
|
||||
.link-edit {
|
||||
width: 80%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #ffffff;
|
||||
box-shadow: -2px -2px 4px rgba(0, 0, 0, 0.05), 2px 2px 4px rgba(0, 0, 0, 0.05);
|
||||
border-radius: 12rpx;
|
||||
box-sizing: border-box;
|
||||
z-index: 999;
|
||||
font-size: 14px;
|
||||
|
||||
.title {
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.edit {
|
||||
padding: 24rpx;
|
||||
border-top: 1px solid #eeeeee;
|
||||
border-bottom: 1px solid #eeeeee;
|
||||
box-sizing: border-box;
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
padding: 4px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #eeeeee;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.uni-input-placeholder {
|
||||
color: #dddddd;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.address {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.control {
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
|
||||
.cancel {
|
||||
flex: 1;
|
||||
color: #dd524d;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.confirm {
|
||||
border-left: 1px solid #eeeeee;
|
||||
flex: 1;
|
||||
color: #007aff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
z-index: 998;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,518 @@
|
|||
<template>
|
||||
<view class="sp-editor" :style="{ '--icon-size': iconSize, '--icon-columns': iconColumns }">
|
||||
<view class="sp-editor-toolbar" v-if="!readOnly" @tap="format">
|
||||
<view v-if="toolbarList.includes('header')" :class="formats.header === 1 ? 'ql-active' : ''"
|
||||
class="iconfont icon-format-header-1" title="标题" data-name="header" :data-value="1"></view>
|
||||
<view v-if="toolbarList.includes('bold')" :class="formats.bold ? 'ql-active' : ''"
|
||||
class="iconfont icon-zitijiacu" title="加粗" data-name="bold"></view>
|
||||
<view v-if="toolbarList.includes('italic')" :class="formats.italic ? 'ql-active' : ''"
|
||||
class="iconfont icon-zitixieti" title="斜体" data-name="italic"></view>
|
||||
<view v-if="toolbarList.includes('underline')" :class="formats.underline ? 'ql-active' : ''"
|
||||
class="iconfont icon-zitixiahuaxian" title="下划线" data-name="underline"></view>
|
||||
<view v-if="toolbarList.includes('strike')" :class="formats.strike ? 'ql-active' : ''"
|
||||
class="iconfont icon-zitishanchuxian" title="删除线" data-name="strike"></view>
|
||||
<!-- #ifndef MP-BAIDU -->
|
||||
<view v-if="toolbarList.includes('alignLeft')" :class="formats.align === 'left' ? 'ql-active' : ''"
|
||||
class="iconfont icon-zuoduiqi" title="左对齐" data-name="align" data-value="left"></view>
|
||||
<!-- #endif -->
|
||||
<view v-if="toolbarList.includes('alignCenter')" :class="formats.align === 'center' ? 'ql-active' : ''"
|
||||
class="iconfont icon-juzhongduiqi" title="居中对齐" data-name="align" data-value="center"></view>
|
||||
<view v-if="toolbarList.includes('alignRight')" :class="formats.align === 'right' ? 'ql-active' : ''"
|
||||
class="iconfont icon-youduiqi" title="右对齐" data-name="align" data-value="right"></view>
|
||||
<view v-if="toolbarList.includes('alignJustify')" :class="formats.align === 'justify' ? 'ql-active' : ''"
|
||||
class="iconfont icon-zuoyouduiqi" title="两端对齐" data-name="align" data-value="justify"></view>
|
||||
<!-- #ifndef MP-BAIDU -->
|
||||
<view v-if="toolbarList.includes('lineHeight')" :class="formats.lineHeight ? 'ql-active' : ''"
|
||||
class="iconfont icon-line-height" title="行间距" data-name="lineHeight" data-value="2"></view>
|
||||
<view v-if="toolbarList.includes('letterSpacing')" :class="formats.letterSpacing ? 'ql-active' : ''"
|
||||
class="iconfont icon-Character-Spacing" title="字间距" data-name="letterSpacing" data-value="2em"></view>
|
||||
<view v-if="toolbarList.includes('marginTop')" :class="formats.marginTop ? 'ql-active' : ''"
|
||||
class="iconfont icon-722bianjiqi_duanqianju" title="段前距" data-name="marginTop" data-value="20px"></view>
|
||||
<view v-if="toolbarList.includes('marginBottom')" :class="formats.marginBottom ? 'ql-active' : ''"
|
||||
class="iconfont icon-723bianjiqi_duanhouju" title="段后距" data-name="marginBottom" data-value="20px">
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-BAIDU -->
|
||||
<view v-if="toolbarList.includes('fontSize')" :class="formats.fontFamily ? 'ql-active' : ''"
|
||||
class="iconfont icon-font" title="字体" data-name="fontFamily" data-value="宋体"></view>
|
||||
<view v-if="toolbarList.includes('fontSize')" :class="formats.fontSize === '24px' ? 'ql-active' : ''"
|
||||
class="iconfont icon-fontsize" title="字号" data-name="fontSize" data-value="24px"></view>
|
||||
<!-- #endif -->
|
||||
<view v-if="toolbarList.includes('color')" :style="{ color: formats.color ? textColor : 'initial' }"
|
||||
class="iconfont icon-text_color" title="文字颜色" data-name="color" :data-value="textColor"></view>
|
||||
<view v-if="toolbarList.includes('backgroundColor')"
|
||||
:style="{ color: formats.backgroundColor ? backgroundColor : 'initial' }"
|
||||
class="iconfont icon-fontbgcolor" title="背景颜色" data-name="backgroundColor"
|
||||
:data-value="backgroundColor"></view>
|
||||
<view v-if="toolbarList.includes('date')" class="iconfont icon-date" title="日期" @tap="insertDate"></view>
|
||||
<view v-if="toolbarList.includes('listCheck')" class="iconfont icon--checklist" title="待办" data-name="list"
|
||||
data-value="check"></view>
|
||||
<view v-if="toolbarList.includes('listOrdered')" :class="formats.list === 'ordered' ? 'ql-active' : ''"
|
||||
class="iconfont icon-youxupailie" title="有序列表" data-name="list" data-value="ordered"></view>
|
||||
<view v-if="toolbarList.includes('listBullet')" :class="formats.list === 'bullet' ? 'ql-active' : ''"
|
||||
class="iconfont icon-wuxupailie" title="无序列表" data-name="list" data-value="bullet"></view>
|
||||
<view v-if="toolbarList.includes('divider')" class="iconfont icon-fengexian" title="分割线"
|
||||
@click="insertDivider"></view>
|
||||
<view v-if="toolbarList.includes('indentDec')" class="iconfont icon-outdent" title="减少缩进" data-name="indent"
|
||||
data-value="-1"></view>
|
||||
<view v-if="toolbarList.includes('indentInc')" class="iconfont icon-indent" title="增加缩进" data-name="indent"
|
||||
data-value="+1"></view>
|
||||
<view v-if="toolbarList.includes('scriptSub')" :class="formats.script === 'sub' ? 'ql-active' : ''"
|
||||
class="iconfont icon-zitixiabiao" title="下标" data-name="script" data-value="sub"></view>
|
||||
<view v-if="toolbarList.includes('scriptSuper')" :class="formats.script === 'super' ? 'ql-active' : ''"
|
||||
class="iconfont icon-zitishangbiao" title="上标" data-name="script" data-value="super"></view>
|
||||
<view v-if="toolbarList.includes('direction')" :class="formats.direction === 'rtl' ? 'ql-active' : ''"
|
||||
class="iconfont icon-direction-rtl" title="文本方向" data-name="direction" data-value="rtl"></view>
|
||||
<view v-if="toolbarList.includes('image')" class="iconfont icon-charutupian" title="图片" @tap="insertImage">
|
||||
</view>
|
||||
<view v-if="toolbarList.includes('link')" class="iconfont icon-charulianjie" title="超链接" @tap="insertLink">
|
||||
</view>
|
||||
<view v-if="toolbarList.includes('undo')" class="iconfont icon-undo" title="撤销" @tap="undo"></view>
|
||||
<view v-if="toolbarList.includes('redo')" class="iconfont icon-redo" title="重做" @tap="redo"></view>
|
||||
<view v-if="toolbarList.includes('removeFormat')" class="iconfont icon-clearedformat" title="清除格式"
|
||||
@tap="removeFormat"></view>
|
||||
<view v-if="toolbarList.includes('clear')" class="iconfont icon-shanchu" title="清空" @tap="clear"></view>
|
||||
<view v-if="toolbarList.includes('export')" class="iconfont icon-baocun" title="导出" @tap="exportHtml">
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 自定义功能组件 -->
|
||||
<!-- 调色板 -->
|
||||
<color-picker v-if="toolbarList.includes('color') || toolbarList.includes('backgroundColor')"
|
||||
ref="colorPickerRef" :color="defaultColor" @confirm="confirmColor"></color-picker>
|
||||
<!-- 添加链接的操作弹窗 -->
|
||||
<link-edit v-if="toolbarList.includes('link') && !readOnly" ref="linkEditRef"
|
||||
@confirm="confirmLink"></link-edit>
|
||||
|
||||
<view class="sp-editor-wrapper" @longpress="eLongpress">
|
||||
<editor :id="editorId" class="ql-editor editor-container" :class="{ 'ql-image-overlay-none': readOnly }"
|
||||
show-img-size show-img-toolbar show-img-resize :placeholder="placeholder" :read-only="readOnly"
|
||||
@statuschange="onStatusChange" @ready="onEditorReady" @input="onEditorInput"></editor>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ColorPicker from './color-picker.vue'
|
||||
import LinkEdit from './link-edit.vue'
|
||||
import {
|
||||
addLink,
|
||||
linkFlag
|
||||
} from '../../utils'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ColorPicker,
|
||||
LinkEdit
|
||||
},
|
||||
props: {
|
||||
// 编辑器id可传入,以便循环组件使用,防止id重复
|
||||
editorId: {
|
||||
type: String,
|
||||
default: 'editor'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '写点什么吧 ~'
|
||||
},
|
||||
// 是否只读
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 最大字数限制,-1不限
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: -1
|
||||
},
|
||||
// 工具栏配置
|
||||
toolbarConfig: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
keys: [], // 要显示的工具,优先级最大
|
||||
excludeKeys: [], // 除这些指定的工具外,其他都显示
|
||||
iconSize: '18px', // 工具栏字体大小
|
||||
iconColumns: 10 // 工具栏列数
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
toolbarConfig: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(newToolbar) {
|
||||
/**
|
||||
* 若工具栏配置中keys存在,则以keys为准
|
||||
* 否则以excludeKeys向toolbarAllList中排查
|
||||
* 若keys与excludeKeys皆为空,则以toolbarAllList为准
|
||||
*/
|
||||
if (newToolbar.keys?.length > 0) {
|
||||
this.toolbarList = newToolbar.keys
|
||||
} else {
|
||||
this.toolbarList =
|
||||
newToolbar.excludeKeys?.length > 0 ?
|
||||
this.toolbarAllList.filter((item) => !newToolbar.excludeKeys.includes(item)) :
|
||||
this.toolbarAllList
|
||||
}
|
||||
this.iconSize = newToolbar.iconSize || '18px'
|
||||
this.iconColumns = newToolbar.iconColumns || 10
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formats: {},
|
||||
textColor: '',
|
||||
backgroundColor: '',
|
||||
curColor: '',
|
||||
defaultColor: {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
}, // 调色板默认颜色
|
||||
iconSize: '20px', // 工具栏图标字体大小
|
||||
iconColumns: 10, // 工具栏列数
|
||||
toolbarList: [],
|
||||
toolbarAllList: [
|
||||
'bold', // 加粗
|
||||
'italic', // 斜体
|
||||
'underline', // 下划线
|
||||
'strike', // 删除线
|
||||
'alignLeft', // 左对齐
|
||||
'alignCenter', // 居中对齐
|
||||
'alignRight', // 右对齐
|
||||
'alignJustify', // 两端对齐
|
||||
'lineHeight', // 行间距
|
||||
'letterSpacing', // 字间距
|
||||
'marginTop', // 段前距
|
||||
'marginBottom', // 段后距
|
||||
'fontFamily', // 字体
|
||||
'fontSize', // 字号
|
||||
'color', // 文字颜色
|
||||
'backgroundColor', // 背景颜色
|
||||
'date', // 日期
|
||||
'listCheck', // 待办
|
||||
'listOrdered', // 有序列表
|
||||
'listBullet', // 无序列表
|
||||
'indentInc', // 增加缩进
|
||||
'indentDec', // 减少缩进
|
||||
'divider', // 分割线
|
||||
'header', // 标题
|
||||
'scriptSub', // 下标
|
||||
'scriptSuper', // 上标
|
||||
'direction', // 文本方向
|
||||
'image', // 图片
|
||||
'link', // 超链接
|
||||
'undo', // 撤销
|
||||
'redo', // 重做
|
||||
'removeFormat', // 清除格式
|
||||
'clear', // 清空
|
||||
'export' // 导出
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onEditorReady() {
|
||||
// #ifdef MP-BAIDU
|
||||
this.editorCtx = requireDynamicLib('editorLib').createEditorContext(this.editorId)
|
||||
// #endif
|
||||
|
||||
// #ifdef APP || MP-WEIXIN || H5
|
||||
uni
|
||||
.createSelectorQuery()
|
||||
.in(this)
|
||||
.select('#' + this.editorId)
|
||||
.context((res) => {
|
||||
this.editorCtx = res.context
|
||||
this.$emit('init', this.editorCtx, this.editorId)
|
||||
})
|
||||
.exec()
|
||||
// #endif
|
||||
},
|
||||
undo() {
|
||||
this.editorCtx.undo()
|
||||
},
|
||||
redo() {
|
||||
this.editorCtx.redo()
|
||||
},
|
||||
format(e) {
|
||||
let {
|
||||
name,
|
||||
value
|
||||
} = e.target.dataset
|
||||
if (!name) return
|
||||
switch (name) {
|
||||
case 'color':
|
||||
case 'backgroundColor':
|
||||
this.curColor = name
|
||||
this.showPicker()
|
||||
break
|
||||
default:
|
||||
this.editorCtx.format(name, value)
|
||||
break
|
||||
}
|
||||
},
|
||||
showPicker() {
|
||||
switch (this.curColor) {
|
||||
case 'color':
|
||||
this.defaultColor = this.textColor ?
|
||||
this.$refs.colorPickerRef.hex2Rgb(this.textColor) : {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
}
|
||||
break
|
||||
case 'backgroundColor':
|
||||
this.defaultColor = this.backgroundColor ?
|
||||
this.$refs.colorPickerRef.hex2Rgb(this.backgroundColor) : {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0
|
||||
}
|
||||
break
|
||||
}
|
||||
this.$refs.colorPickerRef.open()
|
||||
},
|
||||
confirmColor(e) {
|
||||
switch (this.curColor) {
|
||||
case 'color':
|
||||
this.textColor = e.hex
|
||||
this.editorCtx.format('color', this.textColor)
|
||||
break
|
||||
case 'backgroundColor':
|
||||
this.backgroundColor = e.hex
|
||||
this.editorCtx.format('backgroundColor', this.backgroundColor)
|
||||
break
|
||||
}
|
||||
},
|
||||
onStatusChange(e) {
|
||||
if (e.detail.color) {
|
||||
this.textColor = e.detail.color
|
||||
}
|
||||
if (e.detail.backgroundColor) {
|
||||
this.backgroundColor = e.detail.backgroundColor
|
||||
}
|
||||
this.formats = e.detail
|
||||
},
|
||||
insertDivider() {
|
||||
this.editorCtx.insertDivider()
|
||||
},
|
||||
clear() {
|
||||
uni.showModal({
|
||||
title: '清空编辑器',
|
||||
content: '确定清空编辑器吗?',
|
||||
success: ({
|
||||
confirm
|
||||
}) => {
|
||||
if (confirm) {
|
||||
this.editorCtx.clear()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
removeFormat() {
|
||||
uni.showModal({
|
||||
title: '文本格式化',
|
||||
content: '确定要清除所选择部分文本块格式吗?',
|
||||
showCancel: true,
|
||||
success: ({
|
||||
confirm
|
||||
}) => {
|
||||
if (confirm) {
|
||||
this.editorCtx.removeFormat()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
insertDate() {
|
||||
const date = new Date()
|
||||
const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
|
||||
this.editorCtx.insertText({
|
||||
text: formatDate
|
||||
})
|
||||
},
|
||||
insertLink() {
|
||||
this.$refs.linkEditRef.open()
|
||||
},
|
||||
/**
|
||||
* 确认添加链接
|
||||
* @param {Object} e { text: '链接描述', href: '链接地址' }
|
||||
*/
|
||||
confirmLink(e) {
|
||||
this.$refs.linkEditRef.close()
|
||||
addLink(this.editorCtx, e)
|
||||
this.$emit('addLink', e, this.editorId)
|
||||
// 修复添加超链接后,不触发input更新当前最新内容的bug,这里做一下手动更新
|
||||
this.editorCtx.getContents({
|
||||
success: (res) => {
|
||||
this.$emit('input', {
|
||||
html: res.html,
|
||||
text: res.text
|
||||
}, this.editorId)
|
||||
}
|
||||
})
|
||||
},
|
||||
insertImage() {
|
||||
// #ifdef APP-PLUS || H5
|
||||
uni.chooseImage({
|
||||
// count: 1, // 默认9
|
||||
success: (res) => {
|
||||
const {
|
||||
tempFiles
|
||||
} = res
|
||||
// 将文件和编辑器示例抛出,由开发者自行上传和插入图片
|
||||
this.$emit('upinImage', tempFiles, this.editorCtx, this.editorId)
|
||||
},
|
||||
fail() {
|
||||
uni.showToast({
|
||||
title: '未授权访问相册权限,请授权后使用',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
|
||||
uni.chooseMedia({
|
||||
// count: 1, // 默认9
|
||||
success: (res) => {
|
||||
// 同上chooseImage处理
|
||||
const {
|
||||
tempFiles
|
||||
} = res
|
||||
this.$emit('upinImage', tempFiles, this.editorCtx, this.editorId)
|
||||
},
|
||||
fail() {
|
||||
uni.showToast({
|
||||
title: '未授权访问相册权限,请授权后使用',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
onEditorInput(e) {
|
||||
// 注意不要使用getContents获取html和text,会导致重复触发onStatusChange从而失去toolbar工具的高亮状态
|
||||
// 复制粘贴的时候detail会为空,此时应当直接return
|
||||
if (Object.keys(e.detail).length <= 0) return
|
||||
const {
|
||||
html,
|
||||
text
|
||||
} = e.detail
|
||||
// 识别到标识立即return
|
||||
if (text.indexOf(linkFlag) !== -1) return
|
||||
|
||||
const maxlength = parseInt(this.maxlength)
|
||||
const textStr = text.replace(/[ \t\r\n]/g, '')
|
||||
if (textStr.length > maxlength && maxlength != -1) {
|
||||
uni.showModal({
|
||||
content: `超过${maxlength}字数啦~`,
|
||||
confirmText: '确定',
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
this.$emit('overMax', {
|
||||
html,
|
||||
text
|
||||
}, this.editorId)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$emit('input', {
|
||||
html,
|
||||
text
|
||||
}, this.editorId)
|
||||
}
|
||||
},
|
||||
// 导出
|
||||
exportHtml() {
|
||||
this.editorCtx.getContents({
|
||||
success: (res) => {
|
||||
this.$emit('exportHtml', res.html, this.editorId)
|
||||
}
|
||||
})
|
||||
},
|
||||
eLongpress() {
|
||||
/**
|
||||
* 微信小程序官方editor的长按事件有bug,需要重写覆盖,不需做任何逻辑,可见下面小程序社区问题链接
|
||||
* @tutorial https://developers.weixin.qq.com/community/develop/doc/000c04b3e1c1006f660065e4f61000
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/uni_modules/sp-editor/icons/editor-icon.css';
|
||||
|
||||
.sp-editor {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sp-editor-toolbar {
|
||||
box-sizing: border-box;
|
||||
padding: calc(var(--icon-size) / 4) 0;
|
||||
border-bottom: 1px solid #e4e4e4;
|
||||
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--icon-columns), 1fr);
|
||||
position: fixed;
|
||||
width: 95%;
|
||||
z-index: 100;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: calc(var(--icon-size) * 1.8);
|
||||
cursor: pointer;
|
||||
font-size: var(--icon-size);
|
||||
}
|
||||
|
||||
.sp-editor-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin-top: 245rpx;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
padding: 8rpx 16rpx;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.ql-image-overlay-none {
|
||||
::v-deep .ql-image-overlay {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .ql-editor.ql-blank::before {
|
||||
font-style: normal;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
::v-deep .ql-container {
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
.ql-active {
|
||||
color: #66ccff;
|
||||
}
|
||||
</style>
|
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -0,0 +1,83 @@
|
|||
{
|
||||
"id": "sp-editor",
|
||||
"displayName": "官方富文本编辑器editor组件改良扩展优化版",
|
||||
"version": "1.4.0",
|
||||
"description": "基于官方的富文本编辑器editor组件,进行改良扩展优化版,添加了调色板,添加超链接等功能,可自定义扩展工具,快来试试吧~",
|
||||
"keywords": [
|
||||
"富文本",
|
||||
"editor",
|
||||
"编辑器"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "插件不采集任何数据",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "n"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "y",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
# sp-editor
|
||||
|
||||
### 文档迁移
|
||||
|
||||
> 防止文档失效,提供下列五个地址,内容一致
|
||||
|
||||
- [地址一](https://sonvee.github.io/sv-app-docs/docs-github/src/plugins/sp-editor/sp-editor.html)
|
||||
- [地址二](https://sv-app-docs.pages.dev/src/plugins/sp-editor/sp-editor.html)
|
||||
- [地址三](https://sv-app-docs.4everland.app/src/plugins/sp-editor/sp-editor.html)
|
||||
- [地址四](https://sv-app-docs.vercel.app/src/plugins/sp-editor/sp-editor.html) (需要梯子)
|
||||
- [地址五](https://static-mp-74bfcbac-6ba6-4f39-8513-8831390ff75a.next.bspapp.com/docs-uni/src/plugins/sp-editor/sp-editor.html) (有IP限制)
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,68 @@
|
|||
// 标识必须独一无二 - 标识是为了使用insertText插入标识文本后,查找到标识所在delta位置的索引
|
||||
export const linkFlag = '#-*=*-*=*-*=*@-link超链接标识link-@*=*-*=*-*=*-#'
|
||||
|
||||
export function addLink(editorCtx, attr) {
|
||||
// 先插入一段文本内容
|
||||
editorCtx.insertText({
|
||||
text: linkFlag
|
||||
})
|
||||
// 获取全文delta内容
|
||||
editorCtx.getContents({
|
||||
success(res) {
|
||||
let options = res.delta.ops
|
||||
const findex = options.findIndex(item => {
|
||||
return item.insert && typeof item.insert !== 'object' && item.insert?.indexOf(linkFlag) !== -1
|
||||
})
|
||||
// 根据标识查找到插入的位置
|
||||
if (findex > -1) {
|
||||
const findOption = options[findex]
|
||||
const findAttributes = findOption.attributes
|
||||
// 将该findOption分成三部分:前内容 要插入的link 后内容
|
||||
const [prefix, suffix] = findOption.insert.split(linkFlag);
|
||||
const handleOps = []
|
||||
// 前内容
|
||||
if (prefix) {
|
||||
const prefixOps = findAttributes ? {
|
||||
insert: prefix,
|
||||
attributes: findAttributes
|
||||
} : {
|
||||
insert: prefix
|
||||
}
|
||||
handleOps.push(prefixOps)
|
||||
}
|
||||
// 插入的link
|
||||
const linkOps = {
|
||||
insert: attr.text,
|
||||
attributes: {
|
||||
link: attr.href,
|
||||
textDecoration: attr.textDecoration || 'none', // 下划线
|
||||
color: attr.color || '#007aff'
|
||||
}
|
||||
}
|
||||
handleOps.push(linkOps)
|
||||
// 后内容
|
||||
if (suffix) {
|
||||
const suffixOps = findAttributes ? {
|
||||
insert: suffix,
|
||||
attributes: findAttributes
|
||||
} : {
|
||||
insert: suffix
|
||||
}
|
||||
handleOps.push(suffixOps)
|
||||
}
|
||||
// 删除原options[findex]并在findex位置插入上述三个ops
|
||||
options.splice(findex, 1);
|
||||
options.splice(findex, 0, ...handleOps);
|
||||
// 最后重新初始化内容,注意该方法会导致光标重置到最开始位置
|
||||
editorCtx.setContents({
|
||||
delta: {
|
||||
ops: options
|
||||
}
|
||||
})
|
||||
// 所以最后建议使富文本光标失焦,让用户手动聚焦光标
|
||||
editorCtx.blur()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
Loading…
Reference in New Issue