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