1.登陆页面优化

This commit is contained in:
sws
2023-11-22 18:57:45 +08:00
parent a4e37b33fe
commit a27b291c5c
24 changed files with 3406 additions and 1534 deletions

View File

@ -5,9 +5,11 @@
data: {
// 基础配置
// 数据接口请求地址
// request_url: 'https://new.shopxo.vip/',
request_url: 'http://shopxo.com/',
// 静态资源地址如系统根目录不在public目录下面请在静态地址后面加public目录、如https://d1.shopxo.vip/public/
// static_url: 'https://new.shopxo.vip/',
static_url: 'http://shopxo.com/',
// 系统类型默认default、如额外独立小程序、可与程序分身插件实现不同主体小程序及支付独立
@ -754,6 +756,7 @@
* confirm_color [string] 确认按钮的文字颜色,必须是 16 进制格式的颜色字符串(默认 #000000
* object [boject] 回调操作对象点击确认回调参数1取消回调0
* method [string] 回调操作对象的函数
* params [obj] 携带的参数
*/
alert(e) {
var msg = e.msg || null;
@ -764,6 +767,7 @@
var confirm_text = e.confirm_text || '确认';
var cancel_color = e.cancel_color || '#000000';
var confirm_color = e.confirm_color || '#576B95';
var params = e.params || {};
uni.showModal({
title: title,
content: msg,
@ -774,7 +778,8 @@
confirmColor: confirm_color,
success(res) {
if ((e.object || null) != null && typeof e.object === 'object' && (e.method || null) != null) {
e.object[e.method](res.confirm ? 1 : 0);
params['alert_status'] = res.confirm ? 1 : 0;
e.object[e.method](params);
}
},
});

View File

@ -594,6 +594,7 @@ button:before {
.br-grey-9 {
border: solid 1px #999999 !important;
}
.br-grey-c {
border: solid 1px #cccccc !important;
}
@ -1255,9 +1256,11 @@ button[disabled].bg-grey {
.flex-1 {
flex: 1;
}
.flex-2 {
flex: 2;
}
.flex-3 {
flex: 3;
}

View File

@ -30,8 +30,8 @@
<style scoped>
/* iconfont.css全局注册需要将src切换成绝对路径 */
/* @/static/icon/ */
@import url('@/static/icon/iconfont.css');
/* @import url('https://at.alicdn.com/t/c/font_4227145_6avx91bpfzb.css'); */
/* @import url('@/static/icon/iconfont.css'); */
@import url('https://at.alicdn.com/t/c/font_4227145_r2jv37yqfec.css');
.iconfont {
display: inline-block;

View File

@ -1,8 +1,8 @@
<template>
<view :class="theme_view">
<view :class="theme_view + ' ' + propMostClass">
<view :class="'popup ' + (propClassname || '') + ' ' + (propShow ? 'popup-show' : 'popup-hide') + ' ' + (propAnimation ? 'animation' : '')" :disable-scroll="propDisablescroll">
<view class="popup-mask" :style="'z-index: ' + propIndex + ';'" v-if="propMask" @tap="on_mask_tap"></view>
<view :class="'popup-content popup-' + (propPosition || 'bottom') + ' ' + (propIsBar ? 'popup-bar' : '') + ' ' + (propPosition === 'bottom' ? 'bottom-line-exclude' : '')" :style="position_style">
<view :class="'popup-content popup-' + (propPosition || 'bottom') + ' ' + (propIsRadius ? '' : 'popup-radius-0') + ' ' + (propIsBar ? 'popup-bar' : '') + ' ' + (propPosition === 'bottom' ? 'bottom-line-exclude' : '')" :style="position_style">
<slot></slot>
</view>
</view>
@ -19,6 +19,10 @@
},
components: {},
props: {
propMostClass: {
type: String,
default: '',
},
propClassname: {
type: String,
default: '',
@ -47,6 +51,11 @@
type: Boolean,
default: false,
},
// 弹窗是否需要圆角 默认需要
propIsRadius: {
type: Boolean,
default: true,
},
propIndex: {
type: Number,
default: 100,
@ -60,6 +69,10 @@
type: String,
default: '',
},
propStyle: {
type: String,
default: '',
},
},
// 属性值改变监听
watch: {
@ -70,7 +83,7 @@
},
computed: {
position_style() {
let style = 'left:' + this.popup_content_left_value + ';' + (this.propTop ? 'top:' + this.propTop : '') + ';' + (this.propBottom ? 'bottom:' + this.propBottom : '');
let style = 'left:' + this.popup_content_left_value + ';' + (this.propTop ? 'top:' + this.propTop : '') + ';' + (this.propBottom ? 'bottom:' + this.propBottom : '') + ';' + this.propStyle;
return style;
},
},
@ -91,13 +104,13 @@
},
// 左边距位置处理
left_handle() {
var left = 0;
// #ifdef H5
// 处理内容左边距、避免父级设置内边距影响
var width = uni.getSystemInfoSync().windowWidth;
if (width > 800) {
left = (width - 800) / 2;
}
var left = 0;
// #ifdef H5
// 处理内容左边距、避免父级设置内边距影响
var width = uni.getSystemInfoSync().windowWidth;
if (width > 800) {
left = (width - 800) / 2;
}
// #endif
this.popup_content_left_value = left + 'px';
},
@ -180,6 +193,9 @@
border-top-left-radius: 20rpx;
border-bottom-left-radius: 20rpx;
}
.popup-radius-0 {
border-radius: 0 !important;
}
.popup-bar {
/* #ifdef H5 || APP */
bottom: var(--window-bottom) !important;

View File

@ -36,7 +36,7 @@
// 路径类型 默认common
propPathType:{
type:String,
default:'',
default:'common',
}
},
data() {

View File

@ -1,352 +0,0 @@
<template>
<view>
<view class="content">
<view class="detail" v-for="(item,index) in list" :key="index">
<view class="textarea" v-if="item.type==1">
<textarea maxlength="-1" placeholder="请输入内容" v-model="list[index].content"/>
</view>
<view class="image" v-else-if="item.type==2">
<image :src="item.content" mode="widthFix"></image>
</view>
<view class="video" v-else-if="item.type==3">
<video :src="item.content"></video>
</view>
<view class="btn-group">
<view :data-index="index" @tap="delItem">
<uni-icons type="trash" size="14"></uni-icons>
<text>删除</text>
</view>
<view v-if="index>0" :data-index="index" @tap="arrowthinup">
<uni-icons type="arrowthinup" size="14"></uni-icons>
<text>上移</text>
</view>
<view v-if="index<(list.length-1)" :data-index="index" @tap="arrowthindown">
<uni-icons type="arrowthindown" size="14"></uni-icons>
<text>下移</text>
</view>
</view>
</view>
<view class="footer-btn-group">
<view @tap="showShade">
<uni-icons type="plusempty" color="#197ae5" size="16"></uni-icons>
<text>添加</text>
</view>
<view @tap="preview" v-if="showPreview">
<uni-icons type="eye" color="#197ae5" size="16"></uni-icons>
<text>预览</text>
</view>
<view @tap="completed">
<uni-icons type="arrowthinright" color="#197ae5" size="16"></uni-icons>
<text>{{completedText}}</text>
</view>
</view>
</view>
<!-- 遮罩层 -->
<view class="shade" v-if="shade" @tap="closeShade">
<view class="shade-content" @tap.stop>
<view class="shade-content-item" @tap="addText">
<view class="shade-content-item-icon">
<uni-icons type="compose" size="30"></uni-icons>
</view>
<view>文字</view>
</view>
<view class="shade-content-item" @tap="addImage">
<view class="shade-content-item-icon">
<uni-icons type="image" size="30"></uni-icons>
</view>
<view>图片</view>
</view>
<view class="shade-content-item" @tap="addVideo">
<view class="shade-content-item-icon">
<uni-icons type="videocam" size="40"></uni-icons>
</view>
<view>视频</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'textImageVideoEditor',
props: {
completedText: {
type: String,
default: "完成"
},
showPreview: {
type: Boolean,
default: true
},
list2: {
type: Array,
default: function() {
return [
{
type: 1,
content: ""
},
]
}
},
},
data() {
return {
shade: false,
list: [
{
type: 1,
content: ""
}
],
form: {
image: "",
content: "",
}
};
},
mounted() {
this.list = this.list2;
},
methods:{
showShade(){
this.shade = true;
},
closeShade(){
this.shade = false;
},
addText(){
let addjson = {type: 1, content: ""}
this.list.push(addjson)
this.closeShade();
},
addImage(){
this.$emit("addImage")
},
addImageCompleted(imgurl){
let addjson = {type: 2, content: imgurl}
this.list.push(addjson)
this.closeShade();
},
addVideo(){
this.$emit("addVideo")
},
addVideoCompleted(videourl){
let addjson = {type: 3, content: videourl}
this.list.push(addjson)
this.closeShade();
},
delItem(e){
let index = e.currentTarget.dataset.index;
this.list.splice(index, 1)
},
arrowthinup(e){
let index = e.currentTarget.dataset.index;
let newList = [];
if(index>0){
for(let i in this.list){
if(index-i==1){
newList.push(this.list[index])
newList.push(this.list[i])
}else if(index==i){
continue;
}else{
newList.push(this.list[i])
}
}
this.list = newList
}
},
arrowthindown(e){
let index = e.currentTarget.dataset.index;
let newList = [];
if(index<(this.list.length-1)){
for(let i in this.list){
if(i==index){
newList.push(this.list[(parseInt(i)+1)])
newList.push(this.list[i])
}else if((index+1)==i){
continue;
}else{
newList.push(this.list[i])
}
}
this.list = newList
}
},
preview(){
let content = this.setContent();
if(content===false){
uni.showToast({
title: "你还没有添加内容哦",
icon:'none',
duration: 1000
});
}else{
this.$emit("preview", this.form.content)
}
},
completed(){
let content = this.setContent();
if(content===false){
uni.showToast({
title: "你还没有添加内容哦",
icon:'none',
duration: 1000
});
}else{
let data = this.form;
data.json = JSON.stringify(this.list)
this.$emit("completed", data)
}
},
setContent(){
let content = ""
for(let i in this.list){
if(this.list[i].content!=""){
if(this.list[i].type==1){
content += `<div style="width:100%;padding-top: 10px">${this.list[i].content}</div>`;
}
if(this.list[i].type==2){
content += `<div style="width:100%;padding-top: 10px">
<img src="${this.list[i].content}" style="width: 100%" />
</div>`;
// 封面图
if(this.form.image==""){
this.form.image = this.list[i].content;
}
}
if(this.list[i].type==3){
content += `<div style="width:100%;padding-top: 10px">
<video style="width: 100%" controls>
<source src="${this.list[i].content}">
</video>
</div>`;
}
}
}
if(content==""){
return false;
}
this.form.content = content;
},
}
};
</script>
<style lang="scss">
body{
background: #fff;
width: 700rpx;
margin: 0 auto;
}
.content{
margin-bottom: 200rpx;
.detail{
margin-top: 40rpx;
position: relative;
border: 1px solid #eeeeee;
.textarea{
padding-bottom: 66rpx;
textarea{
width: 96%;
padding: 2%;
}
}
.image{
padding-bottom: 56rpx;
image{
width: 100%;
}
}
.video{
padding-bottom: 56rpx;
video{
width: 100%;
};
}
}
}
.btn-group{
display: flex;
position: absolute;
right: 0;
bottom: 0;
view{
width: 140rpx;
height: 60rpx;
line-height: 56rpx;
text-align: center;
font-size: 28rpx;
background: #f5f5f5;
border-top: 1px solid #eeeeee;
border-left: 1px solid #eeeeee;
}
}
.footer-btn-group{
z-index: 9;
display: flex;
justify-content: space-between;
align-items: center;
width: 700rpx;
height: 150rpx;
margin: auto;
background: #fff;
position: fixed;
left: 0;
right: 0;
bottom: 0;
view{
width: 180rpx;
height: 90rpx;
line-height: 90rpx;
text-align: center;
font-size: 32rpx;
color: #197ae5;
border: 1px solid #197ae5;
uni-icons{
position: relative;
left: -10rpx;
}
}
}
.shade{
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 100%;
z-index: 99999999;
background: rgba(12,12,12,.8);
.shade-content{
width: 70%;
padding: 100rpx 15%;
background: #fff;
position: absolute;
bottom: 0;
display: flex;
justify-content: space-between;
align-items: center;
.shade-content-item{
width: 120rpx;
text-align: center;
.shade-content-item-icon{
height: 120rpx;
line-height: 120rpx;
width: 120rpx;
border: 2px solid #ccc;
text-align: center;
}
}
}
}
</style>

View File

@ -1,88 +0,0 @@
###########说明
1. 图标使用uni-icons没有的请到插件市场自行安装。
2. 示例代码如下:
list2: 初始化数据(用于编辑)
addImage: 图片上传
addVideo: 视频上传
showPreview: 是否显示预览
preview: 返回预览的html文本
completedText: 按钮文字
completed: 编辑器返回数据json
3. 提示rich-text不能解析视频建议使用插件市场uparse。
4. 有问题微信同QQ736849829请备注。上传视频太大不会分片也可以v我。
###########示例代码
<template>
<view>
<textImageVideoEditor
ref="textImageVideoEditor"
:list2="initEditorList"
@addImage="addImage"
@addVideo="addVideo"
:showPreview="true"
@preview="preview"
completedText="下一步"
@completed="completed"
></textImageVideoEditor>
</view>
</template>
<script>
import textImageVideoEditor from '../../components/yyc-easy-editor/index.vue'
export default {
data() {
return {
initEditorList: [
{
type:1,
content:""
}
],
}
},
components: {
textImageVideoEditor
},
onLoad(options) {
},
methods: {
preview(data){
console.log("预览preview", data)
},
completed(data){
console.log("完成completed", data)
// data: {
// content: "富文本内容",
// image: "提取一张图片",
// json: "",
// }
},
addImage(){
console.log("上传图片")
let _this = this;
//这里是你上传的代码
//......
//得到imgurl
_this.$refs.textImageVideoEditor.addImageCompleted(imgurl);
},
addVideo(){
console.log("上传视频")
let _this = this;
//这里是你上传的代码
//......
//得到videourl
_this.$refs.textImageVideoEditor.addVideoCompleted(videourl);
},
}
}
</script>
<style lang="scss">
</style>

View File

@ -1,10 +1,15 @@
{
"id": "yyc-easy-editor",
"name": "超简洁 图文视频编辑器 文章发布",
"version": "1.01",
"description": "适合手机端,小程序的编辑器,支持文字,图片,视频,没有太多复杂臃肿的功能,容易操作。安装更简单,为你省去时间。",
"id": "jin-edit",
"name": "jin-edit—仿简书非常好用的富文本编辑器",
"version": "1.1",
"description": "仿简书非常好用的富文本编辑器",
"keywords": [
"编辑器",
"图文视频编辑"
]
"富文本编辑器、富文本"
],
"dcloudext": {
"category": [
"前端组件",
"通用组件"
]
}
}

View File

@ -898,11 +898,21 @@
"navigationBarTitleText": "评论列表"
}
},
{
"path": "user-list/user-list",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的帖子"
}
},
{
"path": "form/form",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "添加/编辑博文"
"navigationBarTitleText": "发布博文",
"enablePullDownRefresh": false,
// 禁止页面下拉
"disableScroll": true
}
}
]

View File

@ -5,52 +5,90 @@ page {
/**
* 基础
*/
.content {
padding-top: 8%;
}
.content .icon {
width: 150rpx;
height: 150rpx !important;
width: 160rpx;
height: 160rpx !important;
}
.top-nav {
padding: 34rpx;
}
/**
* 表单内容
*/
.form-content {
padding: 2% 40rpx 0 40rpx;
padding: 34rpx;
}
.form-content .form-item,
.form-content .code,
.form-content .verify {
border-bottom: solid 1px #f7f7f7;
height: 50px;
line-height: 50px;
background: #F9F9F9;
border-radius: 25px;
border: 0;
}
.form-content .form-item,
.form-content .code input,
.form-content .verify input {
font-size: 28rpx;
color: #4e4e4e;
height: 40px;
line-height: 40px;
border-radius: 0;
height: 50px;
line-height: 50px;
padding: 0 36rpx;
}
.form-content .form-item ::v-deep .input-placeholder,
.form-content .form-item ::v-deep .uni-input-input {
padding: 0 36rpx;
font-size: 30rpx;
}
.form-content .form-item ::v-deep .input-placeholder,
.form-content .code input ::v-deep .input-placeholder,
.form-content .verify input ::v-deep .input-placeholder {
font-size: 30rpx;
}
.form-content .code input,
.form-content .verify input {
width: 63%;
}
.form-content .code .verify-submit {
width: 35%;
height: 35px;
line-height: 35px;
top: 0;
right: 0;
.form-content .form-item ::v-deep .uni-input-input:-webkit-autofill {
/* //这个地方的颜色是字体颜色,可以根据实际情况修改 */
/* -webkit-text-fill-color: #ededed !important; */
/* //设置input输入框的背景颜色为透明色 */
-webkit-box-shadow: 0 0 0px 1000px #f9f9f9 inset !important;
/* //设置input输入框的背景颜色为透明色 */
background-color: #f9f9f9;
background-image: none;
transition: background-color 50000s ease-in-out 0s;
}
.form-content .code ::v-deep .uni-input-input:-webkit-autofill {
/* //设置input输入框的背景颜色为透明色 */
background-color: #f9f9f9;
background-image: none;
transition: background-color 50000s ease-in-out 0s;
}
.form-content .code .verify-submit {
padding: 0 12px;
height: 28px;
line-height: 28px;
top: 11px;
right: 36rpx;
}
.form-content .verify .verify-image {
width: 35%;
height: 35px;
line-height: 35px;
height: 50px;
line-height: 50px;
top: 0;
right: 0;
right: 36rpx;
}
.opt-type-list text:not(:last-child) {
margin-right: 50rpx;
}
@ -59,21 +97,60 @@ page {
* 第三方登录 - 插件
*/
.plugins-thirdpartylogin .item {
width: 35rpx;
height: 35rpx;
width: 76rpx;
height: 76rpx;
padding: 5rpx;
}
.plugins-thirdpartylogin .item:not(:last-child) {
margin-right: 10rpx;
margin-right: 54rpx;
}
.plugins-thirdpartylogin .item image {
width: 25rpx !important;
height: 25rpx !important;
width: 50rpx !important;
height: 50rpx !important;
}
.plugins-thirdpartylogin .item:last-of-type image {
width: 76rpx !important;
height: 76rpx !important;
}
.plugins-thirdpartylogin-bind image {
width: 40rpx !important;
height: 40rpx !important;
}
.plugins-thirdpartylogin-bind button {
line-height: 40rpx;
}
.popup-login .content,
.popup-login .footer .cancel-btn {
border-radius: 32rpx;
}
.popup-login .content .item:not(:last-child) {
border-bottom: 1px solid #eee;
}
.popup-login .content .item image {
width: 36rpx !important;
height: 36rpx !important;
}
.popup-login .content .item .login-width {
width: 180rpx;
}
/**
* 多语言
*/
.popup-language {
height: 100vh;
}
.popup-language .list {
height: calc(100vh - 95rpx);
overflow-y: auto;
}

File diff suppressed because it is too large Load Diff

View File

@ -102,7 +102,7 @@
// 注销提交
logout_submit_event(e) {
// 是否再次确认
if (e != 0 && e != 1) {
if (e.alert_status != 0 && e.alert_status != 1) {
app.globalData.alert({
msg: '账号注销后不可恢复、确定继续吗?',
is_show_cancel: 1,
@ -113,7 +113,7 @@
}
// 注销提交
if (e == 1) {
if (e.alert_status == 1) {
uni.showLoading({
title: '处理中...',
});

View File

@ -73,7 +73,7 @@
return {
theme_view: app.globalData.get_theme_value_view(),
status_bar_height: parseInt(app.globalData.get_system_info('statusBarHeight', 0)),
data_base: data_base,
data_base: {},
data_list: [],
data_total: 0,
data_page_total: 0,

View File

@ -0,0 +1,11 @@
.sp-editor {
height: 500rpx;
}
.more {
transition: all 0.3s ease-in-out;
}
.p-content {
max-height: 50vh;
overflow-y: auto;
}

View File

@ -1,53 +1,416 @@
<template>
<view>
<textImageVideoEditor ref="textImageVideoEditor" :list2="initEditorList" @addImage="addImage" @addVideo="addVideo" :showPreview="true" @preview="preview" completedText="下一步" @completed="completed"></textImageVideoEditor>
<view :class="theme_view">
<view v-if="data_list_loding_status !== 0">
<form @submit="form_submit" class="form-container">
<view class="page-bottom-fixed padding-main">
<view class="bg-white border-radius-main pr oh spacing-mb">
<view class="form-gorup">
<view class="flex-row jc-sb align-c">
<view class="form-gorup-title padding-right-main">标题<text class="form-group-tips-must">*</text></view>
<input type="text" name="title" :value="data.title || ''" maxlength="16" placeholder-class="cr-grey-9" class="cr-base flex-1 flex-width tr" placeholder="请输入" />
</view>
</view>
</view>
<view class="bg-white border-radius-main pr oh spacing-mb">
<view class="form-gorup">
<view class="flex-row jc-sb align-c">
<view class="form-gorup-title padding-right-main">分类<text class="form-group-tips-must">*</text></view>
<view class="flex-1 flex-width tr" @tap="popupOpen">
<text :class="data.type ? 'cr-black' : 'cr-grey-9'">{{ data.type || '请选择' }}</text>
<view class="pr top-sm margin-left-sm dis-inline-block">
<iconfont name="icon-qiandao-jiantou2" color="#999" size="28rpx"></iconfont>
</view>
</view>
</view>
</view>
</view>
<view class="bg-white border-radius-main pr oh spacing-mb">
<view class="form-gorup">
<view class="flex-row jc-sb align-c">
<view class="form-gorup-title padding-right-main">是否启用</view>
<view class="flex-1 flex-width tr">
<switch :color="theme_color" :checked="(data.is_open || 0) == 1 ? true : false" @change="is_open_event" />
</view>
</view>
</view>
</view>
<view class="bg-white border-radius-main pr oh spacing-mb">
<view class="form-gorup">
<view class="form-gorup-title padding-right-main">封面图片</view>
<view class="margin-top-main">
<component-upload :propData="data.image_list" :prop-max-num="1" :prop-path-type="editor_path_type" @call-back="retrun_image_event"></component-upload>
</view>
</view>
</view>
<view class="bg-white border-radius-main pr oh">
<view class="form-gorup">
<view class="form-gorup-title padding-right-main">内容<text class="form-group-tips-must">*</text></view>
<view class="margin-top-main sp-editor">
<sp-editor :templates="data.templates" @input="rich_text_event" @upinImage="up_in_image_event"></sp-editor>
</view>
</view>
</view>
<view class="more oh" :style="'height:' + more_height">
<view class="bg-white border-radius-main pr oh spacing-mb spacing-mt">
<view class="form-gorup">
<view class="form-gorup-title padding-right-main">描述</view>
<textarea name="desc" placeholder-class="cr-grey-9" class="cr-base" placeholder="请输入" maxlength="200" :value="data.desc"></textarea>
</view>
</view>
<view class="bg-white border-radius-main pr oh spacing-mb">
<view class="form-gorup">
<view class="flex-row jc-sb align-c">
<view class="form-gorup-title padding-right-main">SEO标题</view>
<input type="text" name="seo_title" :value="data.seo_title || ''" maxlength="16" placeholder-class="cr-grey-9" class="cr-base flex-1 flex-width tr" placeholder="请输入" />
</view>
</view>
</view>
<view class="bg-white border-radius-main pr oh spacing-mb">
<view class="form-gorup">
<view class="flex-row jc-sb align-c">
<view class="form-gorup-title padding-right-main">SEO关键字</view>
<input type="text" name="seo_key" :value="data.seo_key || ''" maxlength="16" placeholder-class="cr-grey-9" class="cr-base flex-1 flex-width tr" placeholder="请输入" />
</view>
</view>
</view>
<view class="bg-white border-radius-main pr oh">
<view class="form-gorup">
<view class="flex-row jc-sb align-c">
<view class="form-gorup-title padding-right-main">SEO描述</view>
<input type="text" name="seo_desc" :value="data.seo_desc || ''" maxlength="16" placeholder-class="cr-grey-9" class="cr-base flex-1 flex-width tr" placeholder="请输入" />
</view>
</view>
</view>
</view>
<view class="bg-white border-radius-main pr oh spacing-mb spacing-mt">
<view class="padding-main flex-row jc-c align-c cr-grey-9" @tap="more_event">
<text>{{ is_more ? '收起更多' : '展开更多' }}</text>
<view class="margin-left-sm dis-inline-block">
<iconfont :name="is_more ? 'icon-fenlei-top' : 'icon-mendian-jiantou2'" color="#999" size="24rpx"></iconfont>
</view>
</view>
</view>
</view>
<!-- 底部操作 -->
<view class="bottom-fixed btn-bottom bg-white">
<view class="oh bottom-line-exclude">
<button class="cr-white bg-main round text-size wh-auto" type="default" form-type="submit" hover-class="none" :loading="form_submit_loading" :disabled="form_submit_loading">提交</button>
</view>
</view>
<component-popup :propShow="popup_status" propPosition="bottom" @onclose="popup_close_event">
<view class="p-title flex-row jc-sb align-c padding-main br-b-e">
<view class="text-size-lg fw-b">帖子分类</view>
<view class="popup-colse" @tap="popup_close_event">
<iconfont name="icon-huiyuan-guanbi" color="#333" size="28rpx"></iconfont>
</view>
</view>
<view class="p-content padding-main">
<view v-for="(item, index) in type_list" :key="index">
<view class="padding-vertical-main flex-row jc-sb align-c" :class="type_index === index + 1 ? 'cr-main' : ''" :data-value="item" :data-index="index + 1" @tap="get_type_event">
{{ item }}
<iconfont v-if="type_index === index + 1" name="icon-blog-checked" color="#333" size="28rpx"></iconfont>
</view>
</view>
</view>
</component-popup>
</form>
</view>
<view v-else>
<!-- 提示信息 -->
<component-no-data :propStatus="data_list_loding_status"></component-no-data>
</view>
</view>
</template>
<script>
const app = getApp();
import textImageVideoEditor from '@/components/yyc-easy-editor/index.vue';
import componentPopup from '@/components/popup/popup';
import componentUpload from '@/components/upload/upload';
import componentNoData from '@/components/no-data/no-data';
export default {
data() {
return {
initEditorList: [
{
type: 1,
content: '',
},
],
theme_view: app.globalData.get_theme_value_view(),
theme_color: app.globalData.get_theme_color(),
blog_id: '',
data_list_loding_status: 1,
data: {},
index: 0,
// 分类选择弹窗
popup_status: false,
type_list: ['国内资讯', '热点博文', '生活八卦', '同城交流'],
// 分类选中的值
type_index: 0,
// 封面图片
image_list: [],
// 路径类型
editor_path_type: 'common',
// 内容
input_length_max: 500,
input_value: '',
// 查看更多
more_height: '0',
is_more: false,
// 提交按钮
form_submit_loading: false,
};
},
components: {
textImageVideoEditor,
componentPopup,
componentUpload,
componentNoData,
},
onLoad(options) {},
onLoad(params) {
if (params !== null && params.goods_id) {
this.setData({
blog_id: params.id,
});
}
},
onShow() {
// 数据加载
this.init();
},
// 下拉刷新
onPullDownRefresh() {},
// 页面销毁时执行
onUnload: function () {},
methods: {
preview(data) {
console.log('预览preview', data);
init() {
var user = app.globalData.get_user_info(this, 'init');
if (user != false) {
// 用户未绑定手机则转到登录页面
if (app.globalData.user_is_need_login(user)) {
uni.redirectTo({
url: '/pages/login/login?event_callback=init',
});
return false;
}
this.get_data_list();
} else {
// 提示错误
this.setData({
data_list_loding_status: 2,
data_list_loding_msg: '用户未登录',
});
}
},
completed(data) {
console.log('完成completed', data);
// data: {
// content: "富文本内容",
// image: "提取一张图片",
// json: "",
// }
get_data_list() {
if (this.blog_id) {
// 加载loding
uni.showLoading({
title: '加载中...',
});
this.setData({
data_list_loding_status: 1,
});
uni.request({
url: app.globalData.get_request_url('index', 'goodscomments', 'intellectstools'),
method: 'POST',
data: { id: this.blog_id },
success: (res) => {
uni.hideLoading();
if (res.data.code == 0) {
this.setData({
data: res.data.data.data || {},
data_list_loding_status: 3,
});
} else {
this.setData({
data_list_loding_status: 0,
});
if (app.globalData.is_login_check(res.data, this, 'get_data_list')) {
app.globalData.showToast(res.data.msg);
}
}
},
fail: () => {
uni.hideLoading();
this.setData({
data_list_loding_status: 2,
});
app.globalData.showToast('网络开小差了哦~');
},
});
} else {
this.setData({
data_list_loding_status: 3,
});
}
},
addImage() {
console.log('上传图片');
let _this = this;
//这里是你上传的代码
//......
//得到imgurl
_this.$refs.textImageVideoEditor.addImageCompleted(imgurl);
// 弹层打开
popupOpen() {
this.setData({
popup_status: true,
});
},
addVideo() {
console.log('上传视频');
let _this = this;
//这里是你上传的代码
//......
//得到videourl
_this.$refs.textImageVideoEditor.addVideoCompleted(videourl);
// 弹层关闭
popup_close_event(e) {
this.setData({
popup_status: false,
});
},
// 是否启用
is_open_event(e) {
var new_data = this.data;
new_data.is_open = e.detail.value ? 1 : 0;
this.setData({
data: new_data,
});
},
// 获取选中的分类
get_type_event(e) {
var new_data = this.data;
new_data.type = e.currentTarget.dataset.value;
this.setData({
data: new_data,
type_index: e.currentTarget.dataset.index,
popup_status: false,
});
},
// 上传回调
retrun_image_event(data) {
var newData = this.data;
newData.image_list = data;
this.setData({
data: newData,
});
},
// 回调富文本内容
rich_text_event(e) {
console.log('==== input :', e);
var newData = this.data;
newData.templates = e.html;
this.setData({
data: newData,
});
},
// 上传图片
up_in_image_event(tempFiles, editorCtx) {
// 使用 uniCloud.uploadFile 上传图片的示例方法(可适用多选上传)
tempFiles.forEach(async (item) => {
uni.showLoading({
title: '上传中请稍后',
mask: true,
});
await uni.uploadFile({
url: app.globalData.get_request_url('index', 'ueditor'),
// #ifdef APP-PLUS || H5
filePath: item.path,
// #endif
// #ifdef MP-WEIXIN
filePath: item.tempFilePath,
// #endif
name: 'upfile',
formData: {
action: 'uploadimage',
path_type: 'common', // 路径类型默认common
},
success: function (res) {
console.log(res);
let data = JSON.parse(res.data);
if (res.statusCode == 200) {
// 上传完成后处理
editorCtx.insertImage({
src: data.data.url, // 此处需要将图片地址切换成服务器返回的真实图片地址
// width: '50%',
alt: '图片',
success: function (e) {},
});
uni.hideLoading();
}
},
fail: function (e) {
console.log(e);
app.globalData.showToast(e.errMsg);
uni.hideLoading();
},
});
});
},
// 更多内容
more_event() {
this.setData({
more_height: !this.is_more ? '708rpx' : '0',
is_more: !this.is_more,
});
},
// 表单提交
form_submit(e) {
console.log(e);
console.log(this.data);
// 数据验证
var validation = [
{ fields: 'title', msg: '请输入标题' },
{ fields: 'type', msg: '请选择分类' },
{ fields: 'templates', msg: '请输入内容' },
];
var validate = {
type: this.data.type,
templates: this.data.templates,
title: e.detail.value.title,
};
if (app.globalData.fields_check(validate, validation)) {
uni.showLoading({
title: '提交中...',
});
this.setData({
form_submit_loading: true,
});
var newData = {
...e.detail.value,
...validate,
};
// 网络请求
uni.request({
url: app.globalData.get_request_url('save', 'goodscomments', 'intellectstools'),
method: 'POST',
data: newData,
dataType: 'json',
success: (res) => {
uni.hideLoading();
if (res.data.code == 0) {
app.globalData.showToast(res.data.msg, 'success');
setTimeout(function () {
app.globalData.page_back_prev_event();
}, 2000);
} else {
this.setData({
form_submit_loading: false,
});
if (app.globalData.is_login_check(res.data)) {
app.globalData.showToast(res.data.msg);
} else {
app.globalData.showToast('提交失败,请重试!');
}
}
},
fail: () => {
uni.hideLoading();
this.setData({
form_submit_loading: false,
});
app.globalData.showToast('网络开小差了哦~');
},
});
}
},
},
};

View File

@ -0,0 +1,20 @@
.blog-img {
width: 236rpx;
height: 182rpx;
}
.badge {
display: inline-block;
height: 32rpx;
line-height: 32rpx;
background: rgb(82 196 26 / 10%);
border-radius: 18rpx;
font-size: 10px;
color: #52C41A;
padding: 0 16rpx;
}
.add-icon {
height: 80rpx;
line-height: 76rpx;
margin-right: 6rpx;
}

View File

@ -0,0 +1,206 @@
<template>
<view :class="theme_view">
<view v-if="data.length > 0">
<view class="page-bottom-fixed padding-top-main">
<view v-for="(item, index) in data" class="bg-white spacing-mb" :key="index">
<view class="padding-main flex-row">
<view>
<image class="blog-img radius" :src="item.images" mode="aspectFit"></image>
</view>
<view class="flex-1 flex-width padding-left-main">
<view class="multi-text text-size fw-b margin-bottom-xs">{{ item.title }}</view>
<view class="text-size-sm cr-grey-9 margin-bottom-sm">{{ item.date }}</view>
<view class="badge">{{ item.reviewed === 1 ? '已审核' : '未审核' }}</view>
</view>
</view>
<view class="br-t-f5 padding-vertical-main flex-row jc-sa align-c text-size tc">
<view class="cr-base flex-1 divider-r-f5" :data-value="'/pages/plugins/blog/form/form?id=' + item.id" @tap="url_event">
<view class="pr top-xs margin-right-xs dis-inline-block">
<iconfont name="icon-wenda-wytw" size="32rpx"></iconfont>
</view>
编辑
</view>
<view class="cr-main flex-1" :data-id="item.id" @tap="del_event">
<view class="pr top-xs margin-right-xs dis-inline-block">
<iconfont name="icon-blog-del" size="32rpx"></iconfont>
</view>
删除
</view>
</view>
</view>
</view>
<!-- 底部操作 -->
<view class="bottom-fixed btn-bottom bg-white">
<view class="oh bottom-line-exclude">
<button class="cr-main bg-white br-main round text-size wh-auto flex-row align-c jc-c" type="default" hover-class="none" data-value="/pages/plugins/blog/form/form" @tap="url_event">
<view class="add-icon">
<iconfont name="icon-xzdz-tianjiabiaoq" size="32rpx"></iconfont>
</view>
新增
</button>
</view>
</view>
</view>
<view v-else>
<!-- 提示信息 -->
<component-no-data :propStatus="data_list_loding_status"></component-no-data>
</view>
</view>
</template>
<script>
const app = getApp();
import componentNoData from '@/components/no-data/no-data';
export default {
data() {
return {
theme_view: app.globalData.get_theme_value_view(),
theme_color: app.globalData.get_theme_color(),
data_list_loding_status: 1,
data: [
{
id: '1',
images: 'http://shopxo.com/static/upload/images/plugins_homemiddleadv/2023/08/18/1692328288709779.png',
title: '歌手2024惊艳登场陈出生两进入领衔人气爆棚啦',
date: '2023-09-04 10:12:50',
reviewed: 1,
},
],
};
},
components: {
componentNoData,
},
onLoad(params) {},
onShow() {
// 数据加载
this.init();
},
// 下拉刷新
onPullDownRefresh() {},
// 页面销毁时执行
onUnload: function () {},
methods: {
init() {
var user = app.globalData.get_user_info(this, 'init');
if (user != false) {
// 用户未绑定手机则转到登录页面
if (app.globalData.user_is_need_login(user)) {
uni.redirectTo({
url: '/pages/login/login?event_callback=init',
});
return false;
}
this.get_data_list();
} else {
// 提示错误
this.setData({
data_list_loding_status: 2,
data_list_loding_msg: '用户未登录',
});
}
},
get_data_list() {
// 加载loding
uni.showLoading({
title: '加载中...',
});
this.setData({
data_list_loding_status: 1,
});
uni.request({
url: app.globalData.get_request_url('index', 'goodscomments', 'intellectstools'),
method: 'POST',
success: (res) => {
uni.hideLoading();
if (res.data.code == 0) {
this.setData({
// data: res.data.data.data || [],
data_list_loding_status: 3,
});
} else {
this.setData({
data_list_loding_status: 0,
});
if (app.globalData.is_login_check(res.data, this, 'get_data_list')) {
app.globalData.showToast(res.data.msg);
}
}
},
fail: () => {
uni.hideLoading();
this.setData({
data_list_loding_status: 2,
});
app.globalData.showToast('网络开小差了哦~');
},
});
},
// url事件
url_event(e) {
app.globalData.url_event(e);
},
// 删除blog
del_event(e) {
// 是否再次确认
if (e.alert_status != 0 && e.alert_status != 1) {
app.globalData.alert({
msg: '确定要删除这条帖子吗?',
is_show_cancel: 1,
object: this,
params: { id: e.currentTarget.dataset.id },
method: 'del_event',
});
return false;
}
if (e.alert_status == 1) {
// 加载loding
uni.showLoading({
title: '加载中...',
});
this.setData({
data_list_loding_status: 1,
});
uni.request({
url: app.globalData.get_request_url('index', 'goodscomments', 'intellectstools'),
method: 'POST',
data: { id: e.id },
success: (res) => {
uni.hideLoading();
if (res.data.code == 0) {
this.setData({
// data: res.data.data.data || [],
data_list_loding_status: 3,
});
} else {
this.setData({
data_list_loding_status: 0,
});
if (app.globalData.is_login_check(res.data, this, 'del_event')) {
app.globalData.showToast(res.data.msg);
}
}
},
fail: () => {
uni.hideLoading();
this.setData({
data_list_loding_status: 2,
});
app.globalData.showToast('网络开小差了哦~');
},
});
}
},
},
};
</script>
<style>
@import './user-list.css';
</style>

View File

@ -0,0 +1,15 @@
## 1.0.32023-10-13
1. 更新readme文档
2. 更新调整组件示例项目,添加插件代码中部分注释
## 1.0.22023-10-13
1. 更新uni_modules规范可一键导入组件
2. 更新组件示例项目包括使用uniCloud.uploadFile多选上传图片示例方法
## 1.0.12023-10-12
1. 修复小程序中自动聚焦滚动到富文本组件区域的bug
2. 略微调整了富文本上方toolbar工具栏中按钮的大小尺寸
## 1.0.02023-9-19
1. 新增字体与背景颜色板
2. 可自定义预设内容模板
3. 解决官方样例在小程序和app部分报错不兼容的问题
4. 可配合云存储上传富文本中插入的图片 本质上是基于官方内置富文本editor组件改版封装所以官方有的功能都有官方能兼容的也都兼容

View File

@ -0,0 +1,784 @@
<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.rgba = this.color;
if (this.spareColor.length !== 0) {
this.colorList = this.spareColor;
}
},
methods: {
/**
* 初始化
*/
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
} = e.touches[0];
this.pageX = pageX;
this.pageY = pageY;
this.setPosition(pageX, pageY, index);
},
touchmove(e, index) {
const {
pageX,
pageY
} = e.touches[0];
this.moveX = pageX;
this.moveY = pageY;
this.setPosition(pageX, pageY, 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)表示饱和度Bbrightness表示亮度
*/
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();
}
},
watch: {
spareColor(newVal) {
this.colorList = newVal;
}
}
};
</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>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,280 @@
<template>
<view class="wrapper">
<view class="editor-wrapper" :class="tools.length > 8 ? 'two-row' : 'one-row'">
<editor id="editor" class="ql-container" show-img-size show-img-toolbar show-img-resize :placeholder="placeholder" :read-only="readOnly" @statuschange="onStatusChange" @ready="onEditorReady" @input="onEditorInput"></editor>
</view>
<PickerColor ref="colorPicker" :color="{ r: 0, g: 0, b: 0, a: 1 }" @confirm="confirmColor"></PickerColor>
<view class="toolbar" @tap="format">
<!-- 字体加粗 -->
<view v-if="tools.indexOf('bold') !== -1" :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold"></view>
<!-- 斜体 -->
<view v-if="tools.indexOf('italic') !== -1" :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic"></view>
<!-- 下划线 -->
<view v-if="tools.indexOf('underline') !== -1" :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian" data-name="underline"></view>
<!-- 字体删除线 -->
<view v-if="tools.indexOf('strike') !== -1" :class="formats.strike ? 'ql-active' : ''" class="iconfont icon-zitishanchuxian" data-name="strike"></view>
<!-- #ifndef MP-BAIDU -->
<!-- 左对齐 -->
<view v-if="tools.indexOf('left') !== -1" :class="formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi" data-name="align" data-value="left"></view>
<!-- #endif -->
<!-- 居中对齐 -->
<view v-if="tools.indexOf('center') !== -1" :class="formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi" data-name="align" data-value="center"></view>
<!-- 右对齐 -->
<view v-if="tools.indexOf('right') !== -1" :class="formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi" data-name="align" data-value="right"></view>
<!-- #ifndef MP-BAIDU -->
<view v-if="tools.indexOf('fontSize') !== -1" :class="formats.fontSize === '24px' ? 'ql-active' : ''" class="iconfont icon-fontsize" data-name="fontSize" data-value="24px"></view>
<!-- #endif -->
<view v-if="tools.indexOf('color') !== -1" :style="{ color: textColor }" class="iconfont icon-text_color" data-name="color" :data-value="textColor"></view>
<view v-if="tools.indexOf('fontbgcolor') !== -1" :style="{ color: backgroundColor }" class="iconfont icon-fontbgcolor" data-name="background-color" :data-value="backgroundColor"></view>
<view v-if="tools.indexOf('ordered') !== -1" :class="formats.list === 'ordered' ? 'ql-active' : ''" class="iconfont icon-youxupailie" data-name="list" data-value="ordered"></view>
<view v-if="tools.indexOf('bullet') !== -1" :class="formats.list === 'bullet' ? 'ql-active' : ''" class="iconfont icon-wuxupailie" data-name="list" data-value="bullet"></view>
<view v-if="tools.indexOf('images') !== -1" class="iconfont icon-charutupian" @tap="insertImage"></view>
<view v-if="tools.indexOf('undo') !== -1" class="iconfont icon-undo" @tap="undo"></view>
<view v-if="tools.indexOf('redo') !== -1" class="iconfont icon-redo" @tap="redo"></view>
</view>
</view>
</template>
<script>
import PickerColor from './color-picker.vue';
export default {
components: {
PickerColor,
},
props: {
placeholder: {
type: String,
default: '写点什么吧 ~',
},
// 是否只读
readOnly: {
type: Boolean,
default: false,
},
// 最大字数限制,-1不限
maxlength: {
type: Number,
default: -1,
},
// 初始模板
templates: {
type: String,
default: '',
},
tools: {
type: Array,
default: () => {
return ['bold', 'color', 'underline', 'left', 'center', 'right', 'fontSize', 'images'];
},
},
},
data() {
return {
formats: {},
textColor: '',
backgroundColor: '',
curColor: 'text',
};
},
onLoad() {
// #ifndef MP-BAIDU
uni.loadFontFace({
family: 'Pacifico',
source: 'url("https://sungd.github.io/Pacifico.ttf")',
});
// #endif
},
methods: {
onEditorReady() {
// #ifdef MP-BAIDU
this.editorCtx = requireDynamicLib('editorLib').createEditorContext('editor');
// #endif
// #ifdef APP-PLUS || MP-WEIXIN || H5
uni.createSelectorQuery()
.in(this)
.select('#editor')
.context((res) => {
this.editorCtx = res.context;
// 启用preRender方法时会预先渲染templates内容但是在小程序中会导致页面自动聚焦至富文本的区域
if (this.templates) {
this.preRender();
}
})
.exec((result) => {});
// #endif
},
preRender() {
// 初始化富文本时自带的文字模板
this.editorCtx.setContents({
html: this.templates,
});
},
undo() {
this.editorCtx.undo();
},
redo() {
this.editorCtx.redo();
},
format(e) {
let { name, value } = e.target.dataset;
if (!name) return;
console.log('==== name :', name);
switch (name) {
case 'color':
case 'background-color':
this.curColor = name;
this.showPicker();
break;
default:
this.editorCtx.format(name, value);
break;
}
},
showPicker() {
this.$refs.colorPicker.open();
},
confirmColor(e) {
switch (this.curColor) {
case 'color':
this.textColor = e.hex;
this.editorCtx.format('color', e.hex);
break;
case 'background-color':
this.backgroundColor = e.hex;
this.editorCtx.format('background-color', e.hex);
break;
}
},
onStatusChange(e) {
this.formats = e.detail;
// console.log('==== e :', e, this.textColor)
},
insertImage() {
// #ifdef APP-PLUS || H5
uni.chooseImage({
// count: 1, // 默认9
success: (res) => {
const { tempFiles } = res;
/**
* 使用uniCloud.uploadFile进行云存储时需要开启$emit('upinImage')事件,
* 再注释掉insertImage否则在富文本中插入的图片都是本地路径。
* 一般可能需要在传给父组件的upinImage事件中先遍历tempFiles
* 再用uploadFile拿到云存储的图片路径
* 最后将该路径insertImage到src属性中。
* 这一过程等于是将下方注释掉的this.editorCtx.insertImage方法移到父组件中
* 由使用者在父组件中使用子传父的upinImage方法中的editorCtx参数手动调用editorCtx.insertImage操作
*/
this.$emit('upinImage', tempFiles, this.editorCtx);
/* this.editorCtx.insertImage({
src: tempFiles[0].path,
width: '80%',
success: function () {}
}) */
},
fail() {
uni.showToast({
title: '未授权访问相册权限,请授权后使用',
icon: 'none',
});
},
});
// #endif
// #ifdef MP-WEIXIN
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
uni.chooseMedia({
// count: 1, // 默认9
mediaType: ['image', 'video'],
sourceType: ['album', 'camera'],
success: (res) => {
// 同上chooseImage处理
const { tempFiles } = res;
this.$emit('upinImage', tempFiles, this.editorCtx);
},
fail() {
uni.showToast({
title: '未授权访问相册权限,请授权后使用',
icon: 'none',
});
},
});
// #endif
},
onEditorInput(e) {
let maxlength = parseInt(this.maxlength);
this.editorCtx.getContents({
success: (res) => {
let { html, text } = res;
let textStr = text.replace(/\s/g, '');
if (textStr.length > maxlength && maxlength != -1) {
uni.showModal({
content: `超过${maxlength}字数啦~`,
confirmText: '确定',
showCancel: false,
success: ({ confirm, cancel }) => {},
});
} else {
this.$emit('input', { html, text });
}
},
});
},
},
};
</script>
<style scoped>
@import './editor-icon.css';
.wrapper {
height: 100%;
}
.iconfont {
display: inline-block;
padding: 16rpx 0;
width: 12.5%;
cursor: pointer;
font-size: 20px;
text-align: center;
}
.toolbar {
box-sizing: border-box;
border-top: 1px solid #e4e4e4;
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
}
.editor-wrapper {
overflow: auto;
}
.editor-wrapper.one-row {
height: calc(100% - 80rpx);
}
.editor-wrapper.two-row {
height: calc(100% - 160rpx);
}
.ql-container {
padding: 8rpx 16rpx;
box-sizing: border-box;
width: 100%;
height: 100%;
line-height: 1.5;
}
::v-deep .ql-editor.ql-blank::before {
font-style: normal;
color: #cccccc;
}
.ql-active {
color: #66ccff;
}
</style>

View File

@ -0,0 +1,83 @@
{
"id": "sp-editor",
"displayName": "sp-editor",
"version": "1.0.3",
"description": "官方内置富文本editor组件改版",
"keywords": [
"富文本",
"editor",
"编辑器"
],
"repository": "",
"engines": {
"HBuilderX": "^3.5.1"
},
"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"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "u"
},
"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",
"阿里": "y",
"百度": "y",
"字节跳动": "u",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,70 @@
# sp-editor
## props参数
```
props: {
placeholder: {
type: String,
default: '写点什么吧 ~'
},
// 是否只读
readOnly: {
type: Boolean,
default: false
},
// 最大字数限制,-1不限
maxlength: {
type: Number,
default: -1
},
// 初始模板
templates: {
type: String,
default: ''
}
}
```
## emit方法
```
<template>
<sp-editor @input="input" @upinImage="upinImage"></sp-editor>
</template>
<script setup>
function input(e) {
console.log('==== input :', e)
}
function upinImage(tempFiles, editorCtx) {
// 使用 uniCloud.uploadFile 上传图片的示例方法(可适用多选上传)
tempFiles.forEach(async (item) => {
uni.showLoading({
title: '上传中请稍后',
mask: true
})
let upfile = await uniCloud.uploadFile({
filePath: item.path,
// 同名会导致报错 policy_does_not_allow_file_overwrite
// cloudPath可由 想要存储的文件夹/文件名 拼接若不拼文件夹名则默认存储在cloudstorage文件夹中
cloudPath: `cloudstorage/${item.name}`,
cloudPathAsRealPath: true
})
editorCtx.insertImage({
src: upfile.fileID,
width: '80%',
success: function () {
uni.hideLoading()
}
})
})
}
</script>
```
## 注意事项
1. 该组件在使用过程中推荐在外层套上个父盒子,并给父盒子高度,组件在封装时进行了高度计算,会自动撑满父盒子
2. 如遇到在内置浏览器中发生无法拖动调节颜色板的问题只需调出开发者调试面板点击重置左上角选择dom的箭头后便能调出模拟器手势光标便可正常拖动了