客户端app

This commit is contained in:
2023-09-24 23:26:20 +08:00
parent 59f7e39791
commit daa9c98598
628 changed files with 87877 additions and 8 deletions

View File

@@ -0,0 +1,620 @@
<template>
<view class="friendsCircle-content-item">
<image class="friendsCircle-content-avatar" :src="content.avatar" mode="aspectFill"></image>
<view class="friendsCircle-content-right">
<view class="friendsCircle-content-nikeName">{{ content.nikeName }}</view>
<view class="friendsCircle-content-text">{{ content.text }}</view>
<view class="friendsCircle-content-imgs" v-if="content.topicType=='IMAGE'&&content.files.length>0">
<image v-for="(item, ii) in content.files" :key="ii" class="friendsCircle-content-img" :class="'fci' + content.files.length" :src="item.url" mode="aspectFill" @click="pimgs(content.files,ii)"></image>
</view>
<view class="friendsCircle-content-video" @click="openVideo(content.files[0].videoUrl)" v-if="content.topicType=='VIDEO'&&content.files.length>0">
<image class="friendsCircle-content-video-icon" src="../../static/img/bf.png" mode="aspectFill"></image>
<image class="friendsCircle-content-video-img" :src="content.files[0].url" mode="aspectFill"></image>
</view>
<view v-if="content.location" class="friendsCircle-content-location" @click="goMap(content.location)">{{ content.location.name }}</view>
<view class="friendsCircle-content-tools">
<view class="friendsCircle-content-time">{{ content.time }}</view>
<view class="friendsCircle-content-tool" :class="{ 'tabon': tabToolIndex }">
<view class="wxfont caidan" @click="tabTool(i)"></view>
<view class="friendsCircle-content-tool-absolute">
<view class="friendsCircle-popup">
<view class="friendsCircle-popup-item" @click="zan(content)">
<view class="friendsCircle-popup-icon wxfont zan"></view>
<view class="friendsCircle-popup-text" v-if="content.like=='Y'">取消</view>
<view class="friendsCircle-popup-text" v-else></view>
</view>
<view class="friendsCircle-popup-item" @click="comment(content)">
<view class="friendsCircle-popup-icon wxfont pinglun"></view>
<view class="friendsCircle-popup-text">评论</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="friendsCircle-content-item-detail">
<view class="friendsCircle-content-item-detail-item" v-if="content.likeList&&content.likeList.length>0">
<view class="friendsCircle-like-icon wxfont zan"></view>
<view class="friendsCircle-like">
<image class="friendsCircle-like-img" mode="aspectFill" v-for="(z,zindex) in content.likeList" :src="z.portrait"></image>
</view>
</view>
<view class="friendsCircle-content-item-detail-item" v-if="content.replyList&&content.replyList.length>0">
<view class="friendsCircle-comment-icon wxfont pinglun"></view>
<view class="friendsCircle-comment">
<view class="friendsCircle-comment-item" v-for="(c,ci) in content.replyList" :key="ci">
<image class="friendsCircle-comment-img" :src="c.portrait" mode="aspectFill"></image>
<view class="friendsCircle-comment-item-text">
<view class="friendsCircle-comment-item-name">
<text class="friendsCircle-comment-text">{{c.nickName}}</text>
<text class="friendsCircle-comment-item-time">{{c.createTime}}</text>
</view>
<view style="width: 100%;" @click="replyclick(c,ci)">
<view class="friendsCircle-comment-feedback" v-if="c.replyType=='2'">
<text>回复</text>
<text class="friendsCircle-comment-text">{{c.toNickName}}</text>
</view>
<text class="friendsCircle-comment-textx">{{c.content}}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view style="height: 120rpx;"></view>
<view class="zfb-tk-send-tool" :style="{bottom:keyboardHeight+'px'}">
<form @submit="sendMsg(msg)">
<view class="zfb-tk-send-tool-c">
<view class="zfb-tk-send-tool-input-box" @click="msgFocus=true">
<textarea class="zfb-tk-send-tool-input" :adjust-position="false" :focus="msgFocus" v-model="msg" :placeholder="placeholder" confirm-type="send" @confirm="sendMsg(msg)" hold-keyboard :maxlength="-1" auto-height />
</view>
<view class="zfb-tk-send-tool-text" @click="sendMsg(msg)" :style="{ background: msg !== '' ? '#1BC418' : '#F7F7F7', color: msg !== '' ? '#fff' : '#ddd', 'border-color': msg !== '' ? '#1BC418' : '#ddd' }">发送</view>
</view>
</form>
</view>
</template>
<script>
export default {
// 朋友圈列表内容区
data() {
return {
timer:null,
placeholder:'评论',
keyboardHeight:0,
msg: '',
type:'msg',
replydata:'',
msgFocus:false,
tabToolIndex: false,
content:this.detail
};
},
props:{
detail:{
type:Object
}
},
watch:{
detail(v){
this.content=v
}
},
computed:{
userInfo(){
return this.$store.state.userInfo
}
},
mounted() {
// #ifdef APP-PLUS
uni.onKeyboardHeightChange(res => {
this.keyboardHeight=res.height
})
// #endif
},
methods: {
scrolltoBottom() {
this.$nextTick(()=>{
this.timer = setTimeout(() => {
uni.pageScrollTo({
scrollTop: 9999999,
duration: 10
});
this.$forceUpdate()
}, 100);
})
},
replyclick(e,ci){
if(e.canDeleted=='Y'){
this.hidenTool()
uni.showActionSheet({
itemList: ['复制','删除'],
success: (res) => {
switch (res.tapIndex){
case 0:
uni.setClipboardData({
data: e.content,
success: function() {
uni.showToast({
title: '复制成功',
icon: 'none',
position: 'bottom'
})
}
});
break;
case 1:
this.$http.request({
url: '/topic/removeReply/'+e.replyId,
success: (res) => {
if (res.data.code == 200) {
uni.showToast({
title:'删除成功',
icon:'none'
})
this.content.replyList.splice(ci,1)
}
}
});
break;
default:
break;
}
}
});
}else{
this.replydata=e
this.type='reply'
this.placeholder='回复'+e.nickName
this.$nextTick(()=>{
this.msgFocus=true
})
}
},
gofriend(e){
uni.navigateTo({
url:'../../wx/personInfo/detail?param='+e.userId
})
},
pimgs(arr,ii){
var imgs=[]
for (var i = 0; i < arr.length; i++) {
imgs.push(arr[i].url)
}
this.$fc.previewImages(imgs, ii)
},
goMap(e) {
uni.openLocation({
latitude: e.latitude,
longitude: e.longitude,
success: function() {}
});
},
openVideo(e){
this.$fc.plusDownload({onlinePath:e}).then(res=>{
this.$fc.plusOpenFile({filePath:res})
})
},
tabTool(e) {
this.tabToolIndex=!this.tabToolIndex
},
comment(e) {
this.hidenTool()
//评论
this.type='msg'
this.placeholder='评论'
this.$nextTick(()=>{
this.msgFocus=true
})
},
zan(e,i){//点赞
switch (e.like){
case 'N':
this.$http.request({
url: '/topic/like/'+e.topicId,
success: (res) => {
if (res.data.code == 200) {
var like=e.like=='N' ? e.like='Y' : e.like='N'
this.content.like=like
this.content.likeList.push({
userId: this.userInfo.userId,
nickName: this.userInfo.nickName,
portrait: this.userInfo.portrait
})
}
}
});
break;
case 'Y':
this.$http.request({
url: '/topic/cancelLike/'+e.topicId,
success: (res) => {
if (res.data.code == 200) {
var like=e.like=='N' ? e.like='Y' : e.like='N'
this.content.like=like
var thisObj=this.$fc.arrFindkey({arr:this.content.likeList,key:'userId',val:this.userInfo.userId})
this.content.likeList.splice(thisObj.index,1)
}
}
});
break;
default:
break;
}
},
sendMsg(e) {
if (!e) {
return;
}
var replyId=''
var replyType=''
if(this.type=='msg'){
replyId=this.content.topicId
replyType='1'
}
if(this.type=='reply'){
replyId=this.replydata.replyId
replyType='2'
}
this.$http.request({
url: '/topic/reply',
method: 'POST',
data:JSON.stringify({
replyId: replyId,
replyType: replyType,
content: e
}),
success: (res) => {
if (res.data.code == 200) {
this.content.replyList.push(res.data.data)
this.$nextTick(()=>{
this.scrolltoBottom()
})
}
}
});
this.msg=''
},
hidenTool() {//隐藏选项和输入框
this.tabToolIndex = false;
}
}
};
</script>
<style scoped lang="scss">
.zfb-tk-send-tool {
background: #f7f7f7;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
}
.zfb-tk-send-tool-c {
padding: 16rpx 12rpx;
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
border: 1px #ddd solid;
border-left: none;
border-right: none;
}
.zfb-tk-send-tool-c .zfb-tk-send-tool-btn {
transition: color 0.5s;
}
.zfb-tk-send-tool .zfb-tk-send-tool-input {
padding:0 24rpx;
box-sizing: border-box !important;
width: 100%;
background: #fff;
}
.zfb-tk-send-tool-text {
white-space: nowrap;
padding: 10rpx 24rpx;
border-radius: 12rpx;
border: 1px #ddd solid;
background: #f7f7f7;
color: #ddd;
}
.friendsCircle-content-item{
display: flex;
flex-direction: row;
padding:24rpx;
}
.friendsCircle-content-item-detail{
margin:0 24rpx;
border-radius: 8rpx;
margin-top: 24rpx;
padding-bottom: 0;
margin-bottom: 1px;
}
.friendsCircle-content-avatar{
border-radius: 10rpx;
width: 90rpx;
height: 90rpx;
min-width: 90rpx;
}
.friendsCircle-content-right{
display: flex;flex-direction: column;
padding-left: 18rpx;
flex: 1;
box-sizing: border-box;
}
.friendsCircle-content-nikeName{
font-size: 32rpx;
color: #5F698C;
margin-bottom: 10rpx;
}
.friendsCircle-content-text{
font-size: 32rpx;
color: #333;
}
.friendsCircle-content-imgs{
display: flex;
flex-direction: row;
flex-wrap: wrap;
flex: 1;
margin-top: 24rpx;
}
.friendsCircle-content-img{
max-width: 100%;
}
.friendsCircle-content-img.fci1{
height: 550rpx;
max-width: 500rpx;
margin-right: 5px;
}
.friendsCircle-content-img.fci2{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci2:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci3{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci3:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci4{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci4:nth-child(2n){
margin-right: 185rpx;
}
.friendsCircle-content-img.fci5{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci5:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci6{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci6:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci7{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci7:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci8{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci8:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci9{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci9:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-location{
font-size: 24rpx;
color: #5F698C;
margin-top: 12rpx;
}
.friendsCircle-content-time{
font-size: 24rpx;
color: #666666;
margin-right: auto;
}
.friendsCircle-content-tools{
margin-top: 24rpx;
display: flex;flex-direction: row;align-items: center;
}
.friendsCircle-content-tool{
padding:4rpx 16rpx;
border-radius: 10rpx;
background-color: #F8F8FA;
color: #63678C;
display: flex;flex-direction: row;
position: relative;
}
.tabon .friendsCircle-content-tool-absolute{
display: block;
}
.friendsCircle-content-tool-absolute{
position: absolute;
right: 100%;
top: 50%;
transform: translateY(-50%);
display: none;
}
.friendsCircle-popup{
margin-right: 24rpx;
border-radius: 12rpx;
background-color: rgba(0,0,0,0.7);
height: 75rpx;
display: flex;flex-direction: row;align-items: center;
padding:0 24rpx;
}
.friendsCircle-popup-item{
color: #fff;
padding:0 25rpx;
height: 100%;
display: flex;flex-direction: row;align-items: center;
}
.friendsCircle-popup-item:nth-child(1):active .friendsCircle-popup-icon{
font-size: 56rpx;
}
.friendsCircle-popup-icon{
font-size: 46rpx;
}
.friendsCircle-popup-text{
white-space: nowrap;
}
.friendsCircle-like{
display: flex;flex-direction: row;align-items: center;flex-wrap: wrap;
}
.friendsCircle-like-icon,.friendsCircle-like-text,.friendsCircle-comment-text{
color: #5F698C !important;
font-size: 24rpx;
}
.friendsCircle-comment-feedback .friendsCircle-comment-text{
margin:0 6rpx;
}
.friendsCircle-comment-feedback .friendsCircle-comment-text::after{
content: ":";
color: #333;
}
.friendsCircle-comment-item-name{
display: flex;flex-direction: row;
width: 100%;
}
.friendsCircle-comment-item-time{
margin-left: auto;
font-size: 24rpx;
color: #999;
}
.friendsCircle-like-icon,.friendsCircle-comment-icon{
width: 40rpx;
min-width: 40rpx;
height: 60rpx;
display: flex;flex-direction: row;align-items: center;justify-content: center;
font-size: 32rpx;
}
.friendsCircle-like-text{
margin-left: 4rpx;
}
.friendsCircle-like-text::after{
content: ",";
}
.friendsCircle-like-text:nth-last-child(1)::after{
content: "";
}
.friendsCircle-comment{
background-color: #F7F7F7;
border-radius: 8rpx;
flex: 1;
}
.friendsCircle-comment-item{
font-size: 28rpx;
padding: 12rpx;
white-space: pre-wrap;
display: flex;flex-direction: row;
}
.friendsCircle-comment-feedback{
display: inline-block;
}
.friendsCircle-comment-feedback text{
font-size: 28rpx;
color: #333;
}
.friendsCircle-comment-textx{
word-break: break-all;
}
.friendsCircle-like-img,.friendsCircle-comment-img{
width: 60rpx;
height: 60rpx;
border-radius: 12rpx;
margin-left: 10rpx;
margin-bottom: 12rpx;
}
.friendsCircle-comment-img{
margin-right: 12rpx;
margin-left: 0px;
}
.friendsCircle-content-item-detail-item{
display: flex;flex-direction: row;
padding: 12rpx;
padding-bottom: 0;
margin-bottom: 4rpx;
border-radius: 8rpx;
background-color: #F7F7F7;
}
.friendsCircle-comment-item-text{
flex: 1;
display: flex;flex-direction: column;
align-items:flex-start;
}
.friendsCircle-content-video{
height: 550rpx;
max-width: 400rpx;
position: relative;
margin-right: 14rpx;
margin-top: 24rpx;
}
.friendsCircle-content-video-img{
width: 100%;
height: 100%;
}
.friendsCircle-content-video-icon{
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 60rpx !important;
height: 60rpx !important;
transform: translate(-50%,-50%);
}
.zfb-tk-send-tool-input-box{
overflow: auto;
width: 100%;
margin: 0 12rpx;
min-height: 75rpx;
background-color: #fff;
border-radius: 24rpx;
padding-top: 18rpx;
max-height: 225rpx;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,558 @@
<template>
<view class="friendsCircle-content-item" v-for="(v, i) in content" :key="i">
<image class="friendsCircle-content-avatar" :src="v.avatar" mode="aspectFill" @click="gofriend(v)"></image>
<view class="friendsCircle-content-right">
<view class="friendsCircle-content-nickName" @click="gofriend(v)">{{ v.nickName }}</view>
<view class="friendsCircle-content-text">{{ v.text }}</view>
<view class="friendsCircle-content-imgs" v-if="v.topicType=='IMAGE'&&v.files.length>0">
<image v-for="(item, ii) in v.files" :key="ii" class="friendsCircle-content-img" :class="'fci' + v.files.length" :src="item.url" mode="aspectFill" @click="pimgs(v.files,ii)"></image>
</view>
<view class="friendsCircle-content-video" @click="openVideo(v.files[0].videoUrl)" v-if="v.topicType=='VIDEO'&&v.files.length>0">
<image class="friendsCircle-content-video-icon" src="../../static/img/bf.png" mode="aspectFill"></image>
<image class="friendsCircle-content-video-img" :src="v.files[0].url" mode="aspectFill"></image>
</view>
<view class="friendsCircle-content-location" v-if="v.location.name" @click="goMap(v.location)">{{ v.location.name }}</view>
<view class="friendsCircle-content-tools">
<view class="friendsCircle-content-time">{{ v.time }}</view>
<view class="friendsCircle-content-tool" :class="{ tabon: i == showToolIndex }">
<view class="wxfont caidan" @click="showTool(i)"></view>
<view class="friendsCircle-content-tool-absolute">
<view class="friendsCircle-popup">
<view class="friendsCircle-popup-item" @click="zan(v,i)">
<view class="friendsCircle-popup-icon wxfont zan"></view>
<view class="friendsCircle-popup-text" v-if="v.like=='Y'">取消</view>
<view class="friendsCircle-popup-text" v-else></view>
</view>
<view class="friendsCircle-popup-item" @click="comment(v)">
<view class="friendsCircle-popup-icon wxfont pinglun"></view>
<view class="friendsCircle-popup-text">评论</view>
</view>
</view>
</view>
</view>
</view>
<view class="friendsCircle-like" v-if="v.likeList&&v.likeList.length>0">
<view class="friendsCircle-like-icon wxfont zan"></view>
<text class="friendsCircle-like-text" v-for="(z,zindex) in v.likeList">{{z.nickName}}</text>
</view>
<view class="friendsCircle-comment" v-if="v.replyList&&v.replyList.length>0">
<view class="friendsCircle-comment-item" v-for="(c,ci) in v.replyList" :key="ci">
<text class="friendsCircle-comment-text" @click="gofriend(c)">{{c.nickName}}</text>
<view class="friendsCircle-comment-feedback" v-if="c.replyType=='2'">
回复
<text class="friendsCircle-comment-text" @click="gofriend(c)">{{c.toNickName}}</text>
</view>
<view class="friendsCircle-comment-content" v-if="c.content" @click="replyclick(c,ci,i)">
<text>{{c.content}}</text>
</view>
</view>
</view>
</view>
</view>
<view class="zfb-tk-send-tool" v-if="toggleMsgBox" :style="{bottom:keyboardHeight+'px'}">
<form @submit="sendMsg(msg)">
<view class="zfb-tk-send-tool-c">
<view class="zfb-tk-send-tool-input-box" @click="msgFocus=true">
<textarea class="zfb-tk-send-tool-input" :adjust-position="false" :focus="msgFocus" v-model="msg" :placeholder="placeholder" confirm-type="send" @confirm="sendMsg(msg)" hold-keyboard :maxlength="-1" auto-height />
</view>
<view class="zfb-tk-send-tool-text" @click="sendMsg(msg)" :style="{ background: msg !== '' ? '#1BC418' : '#F7F7F7', color: msg !== '' ? '#fff' : '#ddd', 'border-color': msg !== '' ? '#1BC418' : '#ddd' }">发送</view>
</view>
</form>
</view>
</template>
<script>
export default {
// 朋友圈列表内容区
data() {
return {
keyboardHeight:0,
msg: '',
msgFocus:false,
toggleMsgBox: false,
showToolIndex: null,
chooseUserIndex:null,
type:'msg',
placeholder:'评论',
replydata:''
};
},
props:{
content:{
type:Array,
default:[]
}
},
computed:{
userInfo(){
return this.$store.state.userInfo
}
},
mounted() {
// #ifdef APP-PLUS
uni.onKeyboardHeightChange(res => {
this.keyboardHeight=res.height
})
// #endif
},
methods: {
replyclick(e,ci,i){
this.chooseUserIndex=i
if(e.canDeleted=='Y'){
this.hidenTool()
uni.showActionSheet({
itemList: ['复制','删除'],
success: (res) => {
switch (res.tapIndex){
case 0:
uni.setClipboardData({
data: e.content,
success: function() {
uni.showToast({
title: '复制成功',
icon: 'none',
position: 'bottom'
})
}
});
break;
case 1:
this.$http.request({
url: '/topic/removeReply/'+e.replyId,
success: (res) => {
if (res.data.code == 200) {
uni.showToast({
title:'删除成功',
icon:'none'
})
this.content[i].replyList.splice(ci,1)
}
}
});
break;
default:
break;
}
}
});
}else{
this.replydata=e
this.type='reply'
this.placeholder='回复'+e.nickName
this.$nextTick(()=>{
this.toggleMsgBox = true;
this.msgFocus=true
})
}
},
gofriend(e){
uni.navigateTo({
url:'../../wx/personInfo/detail?param='+e.userId
})
},
pimgs(arr,ii){
var imgs=[]
for (var i = 0; i < arr.length; i++) {
imgs.push(arr[i].url)
}
this.$fc.previewImages(imgs, ii)
},
goMap(e) {
uni.openLocation({
latitude: e.latitude,
longitude: e.longitude,
success: function() {}
});
},
openVideo(e){
this.$fc.plusDownload({onlinePath:e}).then(res=>{
this.$fc.plusOpenFile({filePath:res})
})
},
returnParse(txt) {
return JSON.parse(txt);
},
showTool(e) {
this.chooseUserIndex=e
if (this.showToolIndex === e) {
this.showToolIndex = null;
} else {
this.showToolIndex = e;
}
},
comment(e) {
//评论
this.showToolIndex = null;
this.type='msg'
this.placeholder='评论'
this.$nextTick(()=>{
this.toggleMsgBox = true;
this.msgFocus=true
})
},
zan(e,i){//点赞
switch (e.like){
case 'N':
this.$http.request({
url: '/topic/like/'+e.topicId,
success: (res) => {
if (res.data.code == 200) {
this.hidenTool()
var like=e.like=='N' ? e.like='Y' : e.like='N'
this.content[this.chooseUserIndex].like=like
this.content[this.chooseUserIndex].likeList.push({
userId: this.userInfo.userId,
nickName: this.userInfo.nickName,
portrait: this.userInfo.portrait
})
}
}
});
break;
case 'Y':
this.$http.request({
url: '/topic/cancelLike/'+e.topicId,
success: (res) => {
if (res.data.code == 200) {
this.hidenTool()
var like=e.like=='N' ? e.like='Y' : e.like='N'
this.content[this.chooseUserIndex].like=like
var thisObj=this.$fc.arrFindkey({arr:this.content[this.chooseUserIndex].likeList,key:'userId',val:this.userInfo.userId})
this.content[this.chooseUserIndex].likeList.splice(thisObj.index,1)
}
}
});
break;
default:
break;
}
},
sendMsg(e) {
if (!e) {
return;
}
var replyId=''
var replyType=''
if(this.type=='msg'){
replyId=this.content[this.chooseUserIndex].topicId
replyType='1'
}
if(this.type=='reply'){
replyId=this.replydata.replyId
replyType='2'
}
this.$http.request({
url: '/topic/reply',
method: 'POST',
data:JSON.stringify({
replyId: replyId,
replyType: replyType,
content: e
}),
success: (res) => {
if (res.data.code == 200) {
this.hidenTool()
this.content[this.chooseUserIndex].replyList.push(res.data.data)
}
}
});
this.msg=''
},
hidenTool() {//隐藏选项和输入框
this.showToolIndex = null;
this.toggleMsgBox = false;
}
}
};
</script>
<style scoped lang="scss">
.zfb-tk-send-tool {
background: #f7f7f7;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
}
.zfb-tk-send-tool-c {
padding: 16rpx 12rpx;
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
border: 1px #ddd solid;
border-left: none;
border-right: none;
}
.zfb-tk-send-tool-c .zfb-tk-send-tool-btn {
transition: color 0.5s;
}
.zfb-tk-send-tool .zfb-tk-send-tool-input {
padding:0 24rpx;
box-sizing: border-box !important;
width: 100%;
background: #fff;
}
.zfb-tk-send-tool-text {
white-space: nowrap;
padding: 10rpx 24rpx;
border-radius: 12rpx;
border: 1px #ddd solid;
background: #f7f7f7;
color: #ddd;
}
.friendsCircle-content-item{
display: flex;flex-direction: row;
padding:24rpx 34rpx;
border-bottom: 1px #F2F2F2 solid;
}
.friendsCircle-content-avatar{
border-radius: 10rpx;
width: 90rpx;
height: 90rpx;
min-width: 90rpx;
}
.friendsCircle-content-right{
display: flex;flex-direction: column;
padding-left: 18rpx;
flex: 1;
box-sizing: border-box;
}
.friendsCircle-content-nickName{
font-size: 32rpx;
color: #5F698C;
margin-bottom: 10rpx;
}
.friendsCircle-content-text{
font-size: 32rpx;
color: #333;
}
.friendsCircle-content-imgs{
display: flex;
flex-direction: row;
flex-wrap: wrap;
flex: 1;
margin-top: 24rpx;
}
.friendsCircle-content-img{
max-width: 100%;
}
.friendsCircle-content-img.fci1{
height: 550rpx;
max-width: 500rpx;
margin-right: 5px;
}
.friendsCircle-content-img.fci2{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci2:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci3{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci3:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci4{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci4:nth-child(2n){
margin-right: 185rpx;
}
.friendsCircle-content-img.fci5{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci5:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci6{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci6:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci7{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci7:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci8{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci8:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-img.fci9{
width: 185rpx;
height: 185rpx;
margin-right: 5rpx;
margin-bottom: 5rpx;
}
.friendsCircle-content-img.fci9:nth-child(3n){
margin-right: 0;
}
.friendsCircle-content-location{
font-size: 24rpx;
color: #5F698C;
margin-top: 12rpx;
}
.friendsCircle-content-time{
font-size: 24rpx;
color: #666666;
margin-right: auto;
}
.friendsCircle-content-tools{
margin-top: 24rpx;
display: flex;flex-direction: row;align-items: center;
margin-bottom: 14rpx;
}
.friendsCircle-content-tool{
padding:4rpx 16rpx;
border-radius: 10rpx;
background-color: #F8F8FA;
color: #63678C;
display: flex;flex-direction: row;
position: relative;
}
.tabon .friendsCircle-content-tool-absolute{
display: block;
}
.friendsCircle-content-tool-absolute{
position: absolute;
right: 100%;
top: 50%;
transform: translateY(-50%);
display: none;
}
.friendsCircle-popup{
margin-right: 24rpx;
border-radius: 12rpx;
background-color: rgba(0,0,0,0.7);
height: 75rpx;
display: flex;flex-direction: row;align-items: center;
padding:0 24rpx;
}
.friendsCircle-popup-item{
color: #fff;
padding:0 25rpx;
height: 100%;
display: flex;flex-direction: row;align-items: center;
}
.friendsCircle-popup-item:nth-child(1):active .friendsCircle-popup-icon{
font-size: 56rpx;
}
.friendsCircle-popup-icon{
font-size: 46rpx;
}
.friendsCircle-popup-text{
white-space: nowrap;
}
.friendsCircle-like{
border-radius: 8rpx;
padding: 12rpx;
background-color: #F7F7F7;
display: flex;flex-direction: row;align-items: center;flex-wrap: wrap;
margin-bottom: 1px;
}
.friendsCircle-like-icon,.friendsCircle-like-text,.friendsCircle-comment-text{
color: #5F698C !important;
font-size: 28rpx;
}
.friendsCircle-comment-feedback .friendsCircle-comment-text{
margin:0 6rpx;
}
.friendsCircle-comment-text::after{
content: ":";
color: #333;
}
.friendsCircle-like-text{
margin-left: 4rpx;
margin-right: 10rpx;
}
.friendsCircle-like-text::after{
content: ",";
}
.friendsCircle-like-text:nth-last-child(1)::after{
content: "";
}
.friendsCircle-comment{
background-color: #F7F7F7;
border-radius: 8rpx;
}
.friendsCircle-comment-item{
line-height: 40rpx;
font-size: 28rpx;
padding: 12rpx;
white-space: pre-wrap;
display: flex;flex-direction: row;flex-wrap: wrap;
}
.friendsCircle-comment-feedback{
display: inline-block;
}
.friendsCircle-content-video{
width: auto;
height: 550rpx;
max-width: 400rpx;
position: relative;
margin-top: 24rpx;
}
.friendsCircle-content-video-img{
width: 100%;
height: 100%;
}
.friendsCircle-content-video-icon{
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 80rpx !important;
height: 80rpx !important;
transform: translate(-50%,-50%);
}
.friendsCircle-comment-content{
}
.friendsCircle-comment-content text{
word-break: break-all;
}
.zfb-tk-send-tool-input-box{
overflow: auto;
width: 100%;
margin: 0 12rpx;
min-height: 75rpx;
background-color: #fff;
border-radius: 24rpx;
padding-top: 18rpx;
max-height: 225rpx;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,225 @@
<template>
<view class="friends-circle-person-item" v-for="(v, i) in content" :key="i">
<view class="friends-circle-person-item-date"><!-- -->
<text class="day">{{returnDate(v.createTime,'d')}}</text>
<text class="month">{{returnDate(v.createTime,'m')}}</text>
<view class="year">{{returnDate(v.createTime,'y')}}</view>
<view class="year" v-if="v.location.name">{{v.location.name}}</view>
</view>
<view class="friends-circle-person-view" @click="clickItem(v)">
<view class="friends-circle-person-content" v-if="v.topicType=='IMAGE'&&v.files.length>0">
<view class="friends-circle-person-imgs">
<image v-for="(item, ii) in v.files.slice(0,4)" :key="ii" :class="'fci' + v.files.slice(0,4).length" :src="item.url" mode="aspectFill"></image>
</view>
<view class="friends-circle-person-imgs-text">
<text>{{v.text}}</text>
<view class="friends-circle-person-imgs-total">{{v.files.length}}</view>
</view>
</view>
<view class="friends-circle-person-content" v-if="v.topicType=='VIDEO'&&v.files.length>0">
<view class="friendsCircle-content-video">
<image class="friendsCircle-content-video-icon" src="../../static/img/bf.png" mode="aspectFill"></image>
<image class="friendsCircle-content-video-img" :src="v.files[0].url" mode="aspectFill"></image>
</view>
<view class="friends-circle-person-imgs-text">
<text>{{v.text}}</text>
</view>
</view>
<view v-if="v.topicType=='TEXT'" class="friends-circle-person-content">
<view class="friends-circle-person-text">
<text>{{v.text}}</text>
</view>
</view>
</view>
</view>
<view class="friends-circle-nodata" v-if="content.length<=0">
<view class="friends-circle-nodata-line"></view>
<view class="friends-circle-nodata-text">未发布朋友圈</view>
</view>
</template>
<script>
export default {
// 朋友圈单人列表
emits:['clickItem'],
data() {
return {
};
},
props:{
content:{
type:Array,
default:[]
}
},
methods: {
returnDate(e,format){
return this.$fc.getNewDate(format,true,e)
},
clickItem(e){
this.$emit('clickItem',e)
}
}
};
</script>
<style scoped lang="scss">
.friends-circle-person-item{
display: flex;flex-direction: row;
margin-bottom: 60rpx;
width: 100%;
}
.friends-circle-person-view{
flex: 1;
}
.friends-circle-person-item-date{
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-right: 40rpx;
width: 115rpx;
min-width: 115rpx;
line-height: 60rpx;
text-align: right;
}
.friends-circle-person-item-date .day{
flex: 1;
font-size: 43rpx;
}
.friends-circle-person-item-date .month{
font-size: 26rpx;
flex: 1;
}
.friends-circle-person-item-date .year{
width: 100%;
min-width: 100%;
font-size: 24rpx;
color: #999;
line-height: initial;
}
.friends-circle-person-content{
flex: 1;
display: flex;flex-direction: row;
}
.friends-circle-person-text{
padding: 12rpx;
background-color: #eee;
border-radius: 4rpx;
}
.friends-circle-person-text text{
font-size: 28rpx;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.friends-circle-person-imgs{
width: 140rpx;
min-width: 140rpx;
height: 140rpx;
margin-right: 14rpx;
}
.friends-circle-person-imgs image{float: left;}
.friends-circle-person-imgs .fci1{
width: 100%;
height: 100%;
}
.friends-circle-person-imgs .fci2{
margin-right: 2%;
width: 49%;
height: 100%;
}
.friends-circle-person-imgs .fci2:nth-child(2n){
margin-right: 0;
}
.friends-circle-person-imgs .fci3{
margin-right: 2%;
width: 49%;
height: 48%;
}
.friends-circle-person-imgs .fci3:nth-child(1){
height: 98%;
}
.friends-circle-person-imgs .fci3:nth-child(2){
margin-bottom: 2%;
}
.friends-circle-person-imgs .fci3:nth-child(2),.friends-circle-person-imgs .fci3:nth-child(3){
margin-right: 0;
}
.friends-circle-person-imgs .fci4{
margin-right: 2%;
width: 49%;
height: 49%;
margin-bottom: 2%;
}
.friends-circle-person-imgs .fci4:nth-child(2n){
margin-right: 0;
}
.friends-circle-person-imgs-text{
display: flex;
flex-direction: column;
justify-content: space-between;
}
.friends-circle-person-imgs-text text{
line-height: 1.2;
font-size: 28rpx;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
.friends-circle-person-imgs-total{
color: #999;
font-size: 24rpx;
}
.friends-circle-nodata{
display: flex;flex-direction: column;
}
.friends-circle-nodata-line{
margin: 50rpx;
margin-bottom: 30rpx;
height: 1px;
position: relative;
background-color: #eee;
}
.friends-circle-nodata-line::after{
content: " ";
width: 13rpx;
height: 13rpx;
border-radius: 50%;
background-color: #eee;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.friends-circle-nodata-text{
font-size: 24rpx;
text-align: center;
color: #999;
}
.friendsCircle-content-video{
width: 140rpx;
height: 140rpx;
position: relative;
margin-right: 14rpx;
}
.friendsCircle-content-video-img{
width: 100%;
height: 100%;
}
.friendsCircle-content-video-icon{
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 60rpx !important;
height: 60rpx !important;
transform: translate(-50%,-50%);
}
</style>

View File

@@ -0,0 +1,327 @@
<template>
<view class="friendsCircle-top" :class="{ 'friendsCircle-top-opend': CircleTop, 'friendsCircle-top-type-img': cover.type == 'img' }" :key="ikey">
<view style="width: 100%;height: 100%;" v-if="cover.type == 'img'">
<image class="friendsCircle-top-img" @click="changeCircleTop" :src="cover.url" mode="aspectFill"></image>
<view class="friendsCircle-top-post" @click="changePoster" v-if="showChangePoster">
<view class="wxfont tupian"></view>
<text class="text">换封面</text>
</view>
<view class="friendsCircle-top-information" @click="userClick" v-if="userInfo">
<view class="friendsCircle-top-information-user">
<view class="friendsCircle-top-information-nikeName">{{userInfo.nickName}}</view>
<image class="friendsCircle-top-information-avatar" :src="userInfo.portrait" mode="aspectFill"></image>
</view>
<view class="friendsCircle-top-information-signature">{{userInfo.intro}}</view>
</view>
</view>
<video v-else class="friendsCircle-top-video" id="videodemo" ref="videodemo" :key="vkey" :autoplay="false" :src="cover.url" :controls="false" :loop="true" :show-center-play-btn="false" object-fit="cover" muted>
<cover-view class="friendsCircle-top-video-model-img-box"><cover-image class="friendsCircle-top-video-model-img" :src="cover.screenShot"></cover-image></cover-view>
<cover-view class="friendsCircle-top-video-model" @click="changeCircleTop"></cover-view>
<cover-view class="friendsCircle-top-post" @click="changePoster" v-if="showChangePoster">
<cover-image @click="changePoster" class="friendsCircle-top-post-img" src="../../static/img/f01.png"></cover-image>
<cover-view @click="changePoster" class="text">换封面</cover-view>
</cover-view>
<cover-view class="friendsCircle-top-information" @click="userClick" v-if="userInfo">
<cover-view class="friendsCircle-top-information-user">
<cover-view class="friendsCircle-top-information-nikeName">{{userInfo.nickName}}</cover-view>
<cover-image class="friendsCircle-top-information-avatar" :src="userInfo.portrait" mode="aspectFill"></cover-image>
</cover-view>
<cover-view class="friendsCircle-top-information-signature">{{userInfo.intro}}</cover-view>
</cover-view>
</video>
</view>
</template>
<script>
export default {
emits:['userClick'],
data() {
return {
CircleTop: false,
vkey: 0,
ikey:0,
videodemo: null
};
},
props:{
cover:{
type:Object,
default(){
return{
type:'img',
name:'',
url:''
}
}
},
userInfo:{
type:[Object,String]
},
showChangePoster:{
type:[Boolean],
default:true
},
},
watch:{
cover(v){
this.ikey++
this.vkey++;
}
},
methods: {
userClick(){//前往个人朋友圈
this.$emit('userClick',this.cover,this.userInfo)
},
changeCircleTop() {//顶部预览
this.CircleTop = !this.CircleTop;
if(this.type=='img'){
return
}
this.videodemo = uni.createVideoContext('videodemo', this);
this.$nextTick(() => {
setTimeout(() => {
this.vkey++;
this.$nextTick(() => {
if (this.CircleTop) {
this.videodemo.seek(0);
this.videodemo.play();
} else {
this.videodemo.pause();
this.videodemo.seek(0);
}
});
}, 105);
});
},
updateCover(formdata){
this.$http.request({
url: '/topic/editCover',
method: 'POST',
data:JSON.stringify({
cover:JSON.stringify(formdata)
}),
success: (res) => {
if (res.data.code == 200) {
this.$store.dispatch('get_UserInfo')
this.changeCircleTop()
}
}
});
},
changePoster() {//修改封面
uni.showActionSheet({
// itemList: ['图片封面', '视频封面'],
itemList: ['图片封面'],
success: (res) => {
switch (res.tapIndex){
case 0:
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album','camera'], //从相册选择
success: (res) => {
uni.showLoading({
title:'上传中'
})
this.$http.uploadFile({
url: '/file/upload',
filePath: res.tempFilePaths[0],
name: 'file',
fileType: 'image',
success: (res) => {
var data=JSON.parse(res.data)
if (data.code == 200) {
var formdata={
type:'img',
name:data.data.fileName,
url:data.data.fullPath
}
this.updateCover(formdata)
}
}
})
}
});
break;
case 1:
uni.chooseVideo({
sourceType: ['camera', 'album'],
success: (res) => {
uni.showLoading({
title:'上传中'
})
this.$http.uploadFile({
url: '/file/uploadVideo',
filePath: res.tempFilePath,
name: 'file',
fileType: 'video',
success: (res) => {
var data=JSON.parse(res.data)
if (data.code == 200) {
var formdata={
type:'video',
name:data.data.fileName,
url:data.data.fullPath,
screenShot:data.data.screenShot
}
this.updateCover(formdata)
}
}
})
}
});
break;
default:
break;
}
},
fail: function (res) {
console.log(res.errMsg);
}
});
},
},
};
</script>
<style scoped lang="scss">
.friendsCircle-top{
transition: all 0.1s;
width: 750rpx;
height: 590rpx;
position: relative;
background-image: linear-gradient( 135deg, #9708CC 10%, #43CBFF 100%);
}
.friendsCircle-top-type-img{
transition: all 0.3s;
}
.friendsCircle-top-img{
width: 100%;
height: 100%;
}
.friendsCircle-top-opend{
height: 80vh;
}
.friendsCircle-top-post-img{
width: 40rpx;
height: 40rpx;
margin: 0 auto;
}
.friendsCircle-top-post{
text-align: center;
z-index:99;
position: absolute;
bottom: 24rpx;
right: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
display: none;
}
.friendsCircle-top-post .tupian{
color: #f5f5f5;
font-size: 38rpx;
}
.friendsCircle-top-post .text{
font-size: 18rpx;
color: #f5f5f5;
}
.friendsCircle-top-video-model-img-box{
position: absolute;
width: 100%;
height: 80vh;
bottom: 0;
left: 0;
z-index: 2;
}
.friendsCircle-top-video-model-img{
width: 100%;
height: 100%;
}
.friendsCircle-top-video-model{
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 2;
// background-color: rgba(255,0,0,0.3);
}
.friendsCircle-top-opend .friendsCircle-top-post{
display: block;
}
.friendsCircle-top-video{
width: 100%;
height: 100%;
}
.friendsCircle-top-opend .friendsCircle-top-video-model-img-box{
display: none;
}
.friendsCircle-top-opend .friendsCircle-top-video{
height: 80vh;
}
.friendsCircle-top-information{
}
.friendsCircle-top-information-nikeName{
font-size: 32rpx;
color: #fff;
margin-top: 20rpx;
margin-right: 14rpx;
font-weight: bold;
text-shadow: 0px 0px 5px rgba(0,0,0,0.9);
}
.friendsCircle-top-information-avatar{
width: 120rpx;
min-width: 120rpx;
height: 120rpx;
border-radius: 12rpx;
}
.friendsCircle-top-information{
z-index: 99;
display: flex;flex-direction: row;flex-wrap: wrap;
position: absolute;
bottom: -45rpx;
right: 24rpx;
display: flex;flex-direction: column;
}
.friendsCircle-top-information-signature{
position: absolute;
right: 0;
bottom: -45rpx;
width: 100%;
font-size: 24rpx;
color: #666;
text-align: right;
}
.friendsCircle-top-information-user{
display: flex;
flex-direction: row;
// border-radius: 12rpx;
// background-color: #f00;
}
.friendsCircle-top-opend .friendsCircle-top-information{
display: none;
}
.friendsCircle-top-video .friendsCircle-top-information{
bottom: -65rpx;
}
.friendsCircle-top-video .friendsCircle-top-information-user{
display: flex;
flex-direction: row;
}
.friendsCircle-top-video .friendsCircle-top-information-avatar{
float: left;
}
.friendsCircle-top-video .friendsCircle-top-information-nikeName{
float: left;
}
.friendsCircle-top-video .friendsCircle-top-information-signature{
position: inherit;
float: left;
margin-top: 10rpx;
}
</style>

View File

@@ -0,0 +1,145 @@
<template>
<view class="xw-tool-list">
<view class="xw-tool-list-content" v-if="type=='list'">
<template v-for="(item, i) in list" :key="i">
<view class="xw-tool-item" @click="onClick(item,i)" @longpress="onlongpress(item,i)" v-if="item.title">
<image v-if="item.icon" class="xw-tool-img" :src="item.icon" mode="aspectFill"></image>
<view class="xw-tool-text">
<uni-badge v-if="item.type == 'dottext'" text="1" is-dot absolute="rightTop" size="normal">
<text>{{ item.title }}</text>
</uni-badge>
<text v-else>{{ item.title }}</text>
</view>
<view class="xw-tool-else">
<view v-for="(v, index) in item.else" :key="index">
<uni-badge v-if="v.type == 'dotimg'" class="xw-tool-badge" text="1" is-dot absolute="rightTop" size="normal"><image :src="v.content" mode="aspectFill"></image></uni-badge>
<image v-if="v.type == 'img'" :src="v.content" mode="aspectFill"></image>
<uni-badge v-if="v.type == 'dottext'" class="xw-tool-badge" text="1" is-dot absolute="rightTop" size="normal"><view class="text">{{ v.content }}</view></uni-badge>
<view class="text" v-if="v.type == 'text'">{{ v.content }}</view>
</view>
</view>
<uni-icons v-if="!item.hideRight" class="xw-tool-right" type="right" size="16" color="#b5b5b5"></uni-icons>
</view>
</template>
</view>
<view class="xw-tool-list-content" v-if="type=='btns'">
<view class="xw-tool-btn-item" v-for="(item, i) in list" :key="i" @click="onClick(item,i)" @longpress="onlongpress(item,i)">
<view class="xw-tool-btn-icon wxfont " :class="item.icon"></view>
<view class="xw-tool-btn-text">{{item.title}}</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'toolListWx', //微信功能列表
data() {
return {};
},
emits: ['itemClick','onlongpress'],
props: {
list: {
type: Array
},
type: {
type: String,
default: 'list' //list列表icon为图片btns按钮组icon为字体
}
},
methods: {
onlongpress(e,i){
this.$emit('onlongpress', e,i);
},
onClick(e,i) {
this.$emit('itemClick', e,i);
if (e.path=='#') {
return;
}
if (!e.path&&!e.hideRight) {
uni.showToast({
title: '未开通',
icon: 'none'
});
return;
}
uni.navigateTo({
url: e.path
});
}
}
};
</script>
<style scoped>
.xw-tool-list {
display: flex;
flex-direction: column;
background-color: #ffffff;
margin-bottom: 18rpx;
}
.xw-tool-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 26rpx 24rpx;
border-bottom: 1px #eee solid;
}
.xw-tool-item:nth-last-child(1) {
border: none;
}
.xw-tool-img {
width: 52rpx;
height: 52rpx;
margin-right: 24rpx;
}
.xw-tool-text {
white-space: nowrap;
margin-right: auto;
}
.xw-tool-badge {
}
.xw-tool-else {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
}
.xw-tool-else image {
width: 60rpx;
height: 60rpx;
border-radius: 6rpx;
}
.xw-tool-else .text {
color: #666;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
max-width: 520rpx;
}
.xw-tool-else image,
.xw-tool-else .text {
margin-left: 12rpx;
}
.xw-tool-right {
margin-left: 12rpx;
}
.xw-tool-btn-item{
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 34rpx 44rpx;
border-bottom: 1px #eee solid;
font-weight: bold;
color: #5F698C;
}
.xw-tool-btn-icon{
margin-right: 14rpx;
}
</style>

View File

@@ -0,0 +1,156 @@
<template>
<view @touchmove.stop.prevent="moveHandle('touchmove')" @click="moveHandle('click')" v-if="show">
<view class="top-right-tool-wx" :animation="animationData" :style="{ top: height + 'px' }">
<view class="top-right-tool-wx-icon"></view>
<view class="top-right-tool-wx-list">
<view class="top-right-tool-wx-list-item" @click="groupChat">
<view class="top-right-tool-wx-list-item-icon"><view class="wxfont xiaoxi"></view></view>
<view class="text">发起群聊</view>
</view>
<view class="top-right-tool-wx-list-item" @click="goSearchFriends">
<view class="top-right-tool-wx-list-item-icon"><view class="wxfont jiahaoyou"></view></view>
<view class="text">添加朋友</view>
</view>
<view class="top-right-tool-wx-list-item" @click="saoyisao">
<view class="top-right-tool-wx-list-item-icon"><view class="wxfont saoyisao"></view></view>
<view class="text">扫一扫</view>
</view>
</view>
</view>
<view class="top-right-tool-wx-model"></view>
</view>
</template>
<script>
export default {
data() {
return {
height: 0 ,//距离顶部高度
show:false,
animationData: {}
};
},
props: {
list: {
type: Array,
default() {
return [{}];
}
}
},
onShow() {
},
mounted() {
this.getstatusBarHeight();
var animation = uni.createAnimation({
duration: 300,
timingFunction: 'linear'
});
this.animation = animation;
},
methods: {
groupChat(){
uni.navigateTo({
url:'../../wx/group/createGroup'
})
},
goSearchFriends(){
uni.navigateTo({
url:'../../wx/search-friends/index'
})
},
saoyisao(){
this.$fc.saoyisao()
},
showAnimation() {
this.animation.opacity(1).step();
this.animationData = this.animation.export();
},
hideAnimation() {
this.animation.opacity(0).step();
this.animationData = this.animation.export();
},
moveHandle(e) {
this.hiddenTab()
},
showTab(){
this.show=true
setTimeout(()=>{
this.showAnimation()
},30)
},
hiddenTab(){
this.show=false
this.hideAnimation()
},
getstatusBarHeight() {
var SystemInfo = uni.getSystemInfoSync();
// #ifdef H5
this.height = SystemInfo.safeArea.top + SystemInfo.windowTop;
// #endif
}
}
};
</script>
<style scoped>
.top-right-tool-wx {
width: 300rpx;
position: fixed;
z-index: 9999;
top: -10px;
right: 16rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
opacity: 0;
}
.top-right-tool-wx-model{
background-color: rgba(0,0,0,0);
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 9998;
}
.top-right-tool-wx-icon {
width: 0px;
height: 0px;
border: 10px solid transparent;
border-bottom-color: #4C4C4C;
margin-left: auto;
margin-right: 20rpx;
}
.top-right-tool-wx-list {
width: 100%;
background-color: #4C4C4C;
border-radius: 10rpx;
}
.top-right-tool-wx-list-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 0rpx 36rpx;
padding-right: 0;
}
.top-right-tool-wx-list-item-icon {
width: 38rpx;
height: 38rpx;
margin-right: 28rpx;
color: #fff;
}
.top-right-tool-wx-list-item-icon .wxfont {
font-size: 40rpx;
}
.top-right-tool-wx-list-item .text {
color: #fff;
font-size: 32rpx;
border-bottom: 1px #535353 solid;
padding: 30rpx 0rpx;
flex: 1;
}
.top-right-tool-wx-list-item:nth-last-child(1) .text{
border: none;
}
</style>

View File

@@ -0,0 +1,105 @@
<template>
<view>
<view v-if="loaded || list.itemIndex < 15" class="xw-book-wrapper">
<view v-if="list.items && list.items.length > 0" class="xw-book-sort">{{ list.key }}</view>
</view>
<view v-if="(loaded || list.itemIndex < 15) && list.items && list.items.length > 0" class="xw-book-userlist">
<view v-for="(item, index) in list.items" :key="index">
<view class="xw-book-user" @click="onClick(idx, index)">
<view v-if="showSelect" class="xw-book-user-checked">
<uni-icons :type="item.checked ? 'checkbox-filled' : 'circle'" :color="item.checked ? '#09C160' : '#C0C0C0'" size="28" />
</view>
<image class="xw-book-user-avatar" :src="item.data.avatar" mode="aspectFill"></image>
<view class="xw-book-user-name">{{item.data.name}}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'UniIndexedListWX',
emits: ['itemClick'],
props: {
loaded: {
type: Boolean,
default: false
},
idx: {
type: Number,
default: 0
},
list: {
type: Object,
default () {
return {}
}
},
showSelect: {
type: Boolean,
default: false
}
},
methods: {
onClick(idx, index) {
this.$emit("itemClick", {
idx,
index
})
}
}
}
</script>
<style lang="scss" scoped>
.xw-book-wrapper {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
}
.xw-book-sort {
padding: 0 24rpx;
flex: 1;
line-height: 60rpx;
background-color: #fafafa;
font-size: 26rpx;
}
.xw-book-userlist {
padding: 0 24rpx;
padding-right: 0;
border-radius: 24rpx;
box-shadow: 0px 0px 10rpx rgba(0, 0, 0, 0.05);
}
.xw-book-user {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
}
.xw-book-user-avatar {
width: 90rpx;
height: 90rpx;
border-radius: 10rpx;
}
.xw-book-user-name {
font-size: 32rpx;
margin-left: 26rpx;
height: 90rpx;
line-height: 90rpx;
padding-bottom: 13rpx;
padding-top: 13rpx;
border-bottom: 1px #f8f8f8 solid;
max-width: 560rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.xw-book-user-checked{
margin-right: 20rpx;
}
</style>

View File

@@ -0,0 +1,456 @@
<template>
<view class="uni-indexed-list" ref="list" id="list">
<!-- #ifdef APP-NVUE -->
<list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false">
<cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx">
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y>
<view class="xw-book-add" v-if="showAdd">
<view class="xw-book-add-item" v-for="(v,i) in addlist" :key="i" @click="gopath(v)">
<uni-badge v-if="v.num" :text="v.num+''" absolute="rightTop">
<view class="xw-book-add-icon">
<view class="wxfont jiahaoyou"></view>
</view>
</uni-badge>
<view v-else class="xw-book-add-icon" :style="{background:v.color}">
<view class="wxfont jiahaoyou"></view>
</view>
<view class="xw-book-add-item-text">{{v.title}}</view>
</view>
</view>
<view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx">
<!-- #endif -->
<indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect"
@itemClick="onClick"></indexed-list-item>
<!-- #ifndef APP-NVUE -->
</view>
</scroll-view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
</cell>
</list>
<!-- #endif -->
<view class="uni-indexed-list__menu" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
@touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove"
@mouseleave.stop="mouseleave">
<view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu--active' : ''">
<text class="uni-indexed-list__menu-text"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text>
</view>
</view>
<view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
<text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text>
</view>
</view>
</template>
<script>
import indexedListItem from './uni-indexed-list-item.vue'
// #ifdef APP-NVUE
const dom = weex.requireModule('dom');
// #endif
// #ifdef APP-PLUS
function throttle(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function touchMove(e) {
let pageY = e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
// #ifndef APP-NVUE
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #endif
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
this.touchmoveIndex = index
// #endif
}
}
const throttleTouchMove = throttle(touchMove, 40)
// #endif
/**
* IndexedList 索引列表 微信版
* @description 用于展示索引列表
* @tutorial https://ext.dcloud.net.cn/plugin?id=375
* @property {Boolean} showSelect = [true|false] 展示模式
* @value true 展示模式
* @value false 选择模式
* @property {Object} options 索引列表需要的数据对象
* @event {Function} click 点击列表事件 ,返回当前选择项的事件对象
* @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
*/
export default {
name: 'UniIndexedListWX',
components: {
indexedListItem
},
emits: ['click'],
props: {
options: {
type: Array,
default () {
return []
}
},
showSelect: {
type: Boolean,
default: false
},
showAdd: {
type: Boolean,
default: true
}
},
data() {
return {
lists: [],
winHeight: 0,
itemHeight: 0,
winOffsetY: 0,
touchmove: false,
touchmoveIndex: -1,
scrollViewId: '',
touchmovable: true,
loaded: false,
isPC: false
}
},
computed:{
friendApply() {
return this.$store.state.friendApply
},
addlist(){
return [{
title:'新的朋友',
path:'../../wx/search-friends/index',
num:this.$store.state.friendApply.count ? this.$store.state.friendApply.count : '',
icon:'jiahaoyou',
color:'#FB9E3E'
},{
title:'群聊',
path:'../../wx/groupInfo/grouplist',
icon:'qunl',
num:'',
color:'#0ABF62'
}]
}
},
watch: {
options: {
handler: function() {
this.setList()
},
deep: true
}
},
mounted() {
// #ifdef H5
this.isPC = this.IsPC()
// #endif
setTimeout(() => {
this.setList()
}, 50)
setTimeout(() => {
this.loaded = true
}, 300);
},
methods: {
gopath(e){
uni.navigateTo({
url:e.path
})
},
setList() {
let index = 0;
this.lists = []
this.options.forEach((value, index) => {
if (value.data.length === 0) {
return
}
let indexBefore = index
let items = value.data.map(item => {
let obj = {}
obj['key'] = value.letter
obj['data'] = item
obj['itemIndex'] = index
index++
obj.checked = item.checked ? item.checked : false
return obj
})
this.lists.push({
title: value.letter,
key: value.letter,
items: items,
itemIndex: indexBefore
})
})
// #ifndef APP-NVUE
uni.createSelectorQuery()
.in(this)
.select('#list')
.boundingClientRect()
.exec(ret => {
this.winOffsetY = ret[0].top
this.winHeight = ret[0].height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['list'], (res) => {
this.winOffsetY = res.size.top
this.winHeight = res.size.height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
},
touchStart(e) {
this.touchmove = true
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
// #endif
}
},
touchMove(e) {
// #ifndef APP-PLUS
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
}
// #endif
// #ifdef APP-PLUS
throttleTouchMove.call(this, e)
// #endif
},
touchEnd() {
this.touchmove = false
// this.touchmoveIndex = -1
},
/**
* 兼容 PC @tian
*/
mousedown(e) {
if (!this.isPC) return
this.touchStart(e)
},
mousemove(e) {
if (!this.isPC) return
this.touchMove(e)
},
mouseleave(e) {
if (!this.isPC) return
this.touchEnd(e)
},
// #ifdef H5
IsPC() {
var userAgentInfo = navigator.userAgent;
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (let v = 0; v < Agents.length - 1; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
},
// #endif
onClick(e) {
let {
idx,
index
} = e
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
let select = []
if (this.showSelect) {
this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
this.lists.forEach((value, idx) => {
value.items.forEach((item, index) => {
if (item.checked) {
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
select.push(obj)
}
})
})
}
this.$emit('click', {
item: obj,
select: select
})
}
}
}
</script>
<style lang="scss" scoped>
.uni-indexed-list {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
}
.uni-indexed-list__scroll {
flex: 1;
height: 100%;
}
.uni-indexed-list__menu {
width: 24px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-indexed-list__menu-item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
align-items: center;
justify-content: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-indexed-list__menu-text {
font-size: 12px;
text-align: center;
color: #aaa;
}
.uni-indexed-list__menu--active {
// background-color: rgb(200, 200, 200);
}
.uni-indexed-list__menu--active {}
.uni-indexed-list__menu-text--active {
border-radius: 16px;
width: 16px;
height: 16px;
line-height: 16px;
// background-color: #007aff;
// color: #fff;
}
.uni-indexed-list__alert-wrapper {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.uni-indexed-list__alert {
width: 80px;
height: 80px;
border-radius: 80px;
text-align: center;
line-height: 80px;
font-size: 35px;
color: #fff;
background-color: rgba(0, 0, 0, 0.5);
}
.h5-buchang{
height: 140rpx;
display: none;
}
.uni-scroll-view-content view:nth-last-child(1) .h5-buchang{
display: block;
}
.xw-book-add{
width: 100%;
display: flex;
flex-direction: column;
padding:0 24rpx;
box-sizing: border-box;
// height: 120rpx;
}
.xw-book-add-item{
display: flex;flex-direction: row;align-items: center;
// height: 90rpx;
// line-height: 90rpx;
padding-bottom: 16rpx;
padding-top: 16rpx;
// border-bottom: 1px #f8f8f8 solid;
}
.xw-book-add-icon{
background: #FB9E3E;
border-radius: 10rpx;
width: 90rpx;
height: 90rpx;
display: flex;flex-direction: row;align-items: center;justify-content: center;
}
.xw-book-add-icon .wxfont{
color: #fff;
font-size: 58rpx;
}
.xw-book-add-item-text{
flex: 1;
font-size: 32rpx;
margin-left: 26rpx;
// height: 90rpx;
// line-height: 90rpx;
// border-bottom: 1px #f8f8f8 solid;
}
</style>

View File

@@ -0,0 +1,173 @@
<template>
<view @touchmove.stop.prevent="moveHandle('touchmove')" @click="moveHandle('click')" v-if="show">
<view class="openTool-wx" :animation="animationData">
<view class="openTool-wx-list" v-if="data">
<view class="openTool-wx-list-item" @click="zhiding">
<view class="text" v-if="data.top=='Y'">取消置顶</view>
<view class="text" v-if="data.top=='N'">置顶该聊天</view>
</view>
<view class="openTool-wx-list-item" @click="shanchu">
<view class="text">删除该聊天</view>
</view>
</view>
</view>
<view class="openTool-wx-model"></view>
</view>
</template>
<script>
export default {
data() {
return {
show:false,
animationData: {}
};
},
props: {
data:{
type:[Object,String]
},
itemKey:{
type:[Object,String,Number]
},
list: {
type: Array,
default() {
return [{}];
}
}
},
onShow() {
},
computed: {
chatList() {
return this.$store.state.chatlist
}
},
mounted() {
var animation = uni.createAnimation({
duration: 300,
timingFunction: 'linear'
});
this.animation = animation;
},
methods: {
zhiding(){
var data=JSON.parse(JSON.stringify(this.data))
var yn = data.top=='N' ? 'Y' : 'N';
data.top=yn
this.$store.dispatch('updateChatListInfoById', {
userId: this.data.userId,
data: data
})
this.$store.dispatch('getChatList')
if(data.windowType=='GROUP'){
var formData = {
groupId: this.data.userId,
top: yn
};
this.$http.request({
url: '/group/editTop',
method: 'POST',
data: JSON.stringify(formData),
success: res => {
if (res.data.code == 200) {
}
}
});
}
if(data.windowType=='SINGLE'){
var formData = {
userId: this.data.userId,
top: yn
};
this.$http.request({
url: '/friend/top',
method: 'POST',
data: JSON.stringify(formData),
success: res => {
if (res.data.code == 200) {
}
}
});
}
},
shanchu(){
delete this.chatList[this.data.userId]
this.$store.dispatch('updateChatListInfoById',{
userId: this.data.userId,
data: {}
});
this.$store.dispatch('updateChatById', {
userId: this.data.userId,
data: []
});
},
showAnimation() {
this.animation.opacity(1).step();
this.animationData = this.animation.export();
},
hideAnimation() {
this.animation.opacity(0).step();
this.animationData = this.animation.export();
},
moveHandle(e) {
this.hiddenTab()
},
showTab(){
this.show=true
setTimeout(()=>{
this.showAnimation()
},30)
},
hiddenTab(){
this.show=false
this.hideAnimation()
},
}
};
</script>
<style scoped>
.openTool-wx {
display: flex;
flex-direction: column;
flex-wrap: wrap;
opacity: 0;
position: relative;
z-index: 2;
}
.openTool-wx-model{
background-color: rgba(0,0,0,0);
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 1;
}
.openTool-wx-list {
width: 100%;
box-sizing: border-box;
background-color: #fff;
box-shadow: 0px 0px 10px rgba(0,0,0,0.3);
border-radius: 10rpx;
display: flex;
flex-direction: column;
}
.openTool-wx-list-item {
padding: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.openTool-wx-list-item .text {
color: #333;
font-size: 28rpx;
flex: 1;
}
.openTool-wx-list-item:nth-last-child(1) .text{
border: none;
}
</style>

View File

@@ -0,0 +1,583 @@
<template>
<!-- #ifdef APP-NVUE -->
<cell>
<!-- #endif -->
<view :hover-class="!clickable && !link ? '' : 'uni-list-chat--hover'" :style="{background:color}" class="uni-list-chat" @longpress="longpressItem($event,itemKey,item)">
<openTool class="openTool" :ref="'toolx'+itemKey" :data="item" :itemKey="itemKey"></openTool>
<view :class="{ 'uni-list--border': border, 'uni-list-chat--first': isFirstChild }"></view>
<view class="uni-list-chat__container" @click="onClick">
<view class="uni-list-chat__header-warp">
<view v-if="item.windowType=='SINGLE'" class="uni-list-chat__header" :class="{ 'header--circle': avatarCircle }">
<image class="uni-list-chat__header-image" :src="item.portrait" mode="aspectFill"></image>
</view>
<!-- 头像组 -->
<view v-if="item.windowType=='GROUP'" class="uni-list-chat__header">
<view v-for="(item, index) in avatarList" :key="index" class="uni-list-chat__header-box" :class="computedAvatar"
:style="{ width: imageWidth + 'px', height: imageWidth + 'px' }">
<image class="uni-list-chat__header-image" :style="{ width: imageWidth + 'px', height: imageWidth + 'px' }" :src="item.url"
mode="aspectFill"></image>
</view>
</view>
</view>
<view v-if="badgeText && badgePositon === 'left'" class="uni-list-chat__badge uni-list-chat__badge-pos" :class="[isSingle]">
<text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text>
</view>
<view class="uni-list-chat__content">
<view class="uni-list-chat__content-main">
<text class="uni-list-chat__content-title uni-ellipsis">{{ title }}</text>
<text class="uni-list-chat__content-note uni-ellipsis">{{ note }}</text>
</view>
<view class="uni-list-chat__content-extra">
<slot>
<text class="uni-list-chat__content-extra-text">{{ time }}</text>
<view v-if="badgeText && badgePositon === 'right'" class="uni-list-chat__badge" :class="[isSingle, badgePositon === 'right' ? 'uni-list-chat--right' : '']">
<text class="uni-list-chat__badge-text">{{ badgeText === 'dot' ? '' : badgeText }}</text>
</view>
</slot>
</view>
</view>
</view>
</view>
<!-- #ifdef APP-NVUE -->
</cell>
<!-- #endif -->
</template>
<script>
import openTool from './openTool.vue'
// 头像大小
const avatarWidth = 45;
/**
* ListChat 聊天列表
* @description 聊天列表,用于创建聊天类列表
* @tutorial https://ext.dcloud.net.cn/plugin?id=24
* @property {String} title 标题
* @property {String} note 描述
* @property {Boolean} clickable = [true|false] 是否开启点击反馈默认为false
* @property {String} badgeText 数字角标内容
* @property {String} badgePositon = [left|right] 角标位置,默认为 right
* @property {String} link = [falsenavigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈默认为false
* @value false 不开启
* @value navigateTo 同 uni.navigateTo()
* @value redirectTo 同 uni.redirectTo()
* @value reLaunch 同 uni.reLaunch()
* @value switchTab 同 uni.switchTab()
* @property {String | PageURIString} to 跳转目标页面
* @property {String} time 右侧时间显示
* @property {Boolean} avatarCircle = [true|false] 是否显示圆形头像默认为false
* @property {String} avatar 头像地址avatarCircle 不填时生效
* @property {Array} avatarList 头像组,格式为 [{url:''}]
* @event {Function} click 点击 uniListChat 触发事件
*/
export default {
components:{
openTool
},
name: 'UniListChatWx',
emits:['click','longpressItem'],
props: {
color: {
type: String,
default: '#fff'
},
title: {
type: String,
default: ''
},
note: {
type: String,
default: ''
},
clickable: {
type: Boolean,
default: false
},
link: {
type: [Boolean, String],
default: false
},
to: {
type: String,
default: ''
},
badgeText: {
type: [String, Number],
default: ''
},
badgePositon: {
type: String,
default: 'right'
},
time: {
type: String,
default: ''
},
avatarCircle: {
type: Boolean,
default: false
},
avatar: {
type: String,
default: ''
},
// avatarList: {
// type: Array,
// default () {
// return [];
// }
// },
item: {
type: Object,
default: {}
},
itemKey: {
type: Number
},
longTapItemKey: {
type: [Number,String],
default:''
}
},
// inject: ['list'],
computed: {
isSingle() {
if (this.badgeText === 'dot') {
return 'uni-badge--dot';
} else {
const badgeText = this.badgeText.toString();
if (badgeText.length > 1) {
return 'uni-badge--complex';
} else {
return 'uni-badge--single';
}
}
},
avatarList(){
return this.returnAvatar(this.item.portrait)
},
computedAvatar() {
if (this.avatarList.length > 4) {
this.imageWidth = avatarWidth * 0.31;
return 'avatarItem--3';
} else if (this.avatarList.length > 1) {
this.imageWidth = avatarWidth * 0.47;
return 'avatarItem--2';
} else {
this.imageWidth = avatarWidth;
return 'avatarItem--1';
}
}
},
data() {
return {
isFirstChild: false,
border: true,
// avatarList: 3,
imageWidth: 50
};
},
mounted() {
this.list = this.getForm()
if (this.list) {
if (!this.list.firstChildAppend) {
this.list.firstChildAppend = true;
this.isFirstChild = true;
}
this.border = this.list.border;
}
},
methods: {
returnAvatar(text){
var data=JSON.parse(text)
var avatars=[]
for(var i=0;i<data.length;i++){
avatars.push({
url:data[i]
})
}
return avatars
},
longpressItem(e,i,v) {//长按回调
this.$emit('longpressItem',e,i,v)
if(this.itemKey==this.longTapItemKey){
this.$refs['toolx'+this.itemKey].showTab();
}
},
/**
* 获取父元素实例
*/
getForm(name = 'uniList') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
onClick() {
if (this.to !== '') {
this.openPage();
return;
}
if (this.clickable || this.link) {
this.$emit('click', {
data: {}
});
}
},
openPage() {
if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].indexOf(this.link) !== -1) {
this.pageApi(this.link);
} else {
this.pageApi('navigateTo');
}
},
pageApi(api) {
uni[api]({
url: this.to,
success: res => {
this.$emit('click', {
data: res
});
},
fail: err => {
this.$emit('click', {
data: err
});
console.error(err.errMsg);
}
});
}
}
};
</script>
<style lang="scss" scoped>
$uni-font-size-lg:16px;
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
$background-color: #fff;
$divide-line-color: #e5e5e5;
$avatar-width: 45px;
$avatar-border-radius: 5px;
$avatar-border-color: #eee;
$avatar-border-width: 1px;
$title-size: 16px;
$title-color: #3b4144;
$title-weight: normal;
$note-size: 12px;
$note-color: #999;
$note-weight: normal;
$right-text-size: 12px;
$right-text-color: #999;
$right-text-weight: normal;
$badge-left: 0px;
$badge-top: 0px;
$dot-width: 10px;
$dot-height: 10px;
$badge-size: 18px;
$badge-font: 12px;
$badge-color: #fff;
$badge-background-color: #ff5a5f;
$badge-space: 6px;
$hover: #f5f5f5;
.openTool{
width: 100%;
position: absolute;
top: 50%;
display: flex;flex-direction: row;justify-content: center;
}
.uni-list-chat {
font-size: $uni-font-size-lg;
position: relative;
flex-direction: column;
justify-content: space-between;
background-color: $background-color;
position: relative;
}
// .uni-list-chat--disabled {
// opacity: 0.3;
// }
.uni-list-chat--hover {
background-color: $hover;
}
.uni-list--border {
position: relative;
margin-left: $uni-spacing-row-lg;
/* #ifdef APP-PLUS */
border-top-color: $divide-line-color;
border-top-style: solid;
border-top-width: 0.5px;
/* #endif */
}
/* #ifndef APP-NVUE */
.uni-list--border:after {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $divide-line-color;
}
.uni-list-item--first:after {
height: 0px;
}
/* #endif */
.uni-list-chat--first {
border-top-width: 0px;
}
.uni-ellipsis {
/* #ifndef APP-NVUE */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
}
.uni-ellipsis-2 {
/* #ifndef APP-NVUE */
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
/* #endif */
/* #ifdef APP-NVUE */
lines: 2;
/* #endif */
}
.uni-list-chat__container {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
padding: $uni-spacing-row-base $uni-spacing-row-lg;
position: relative;
overflow: hidden;
}
.uni-list-chat__header-warp {
position: relative;
}
.uni-list-chat__header {
/* #ifndef APP-NVUE */
display: flex;
align-content: center;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: wrap-reverse;
/* #ifdef APP-NVUE */
width: 50px;
height: 50px;
/* #endif */
/* #ifndef APP-NVUE */
width: $avatar-width;
height: $avatar-width;
/* #endif */
border-radius: $avatar-border-radius;
border-color: $avatar-border-color;
border-width: $avatar-border-width;
border-style: solid;
overflow: hidden;
}
.uni-list-chat__header-box {
/* #ifndef APP-PLUS */
box-sizing: border-box;
display: flex;
width: $avatar-width;
height: $avatar-width;
/* #endif */
/* #ifdef APP-NVUE */
width: 50px;
height: 50px;
/* #endif */
overflow: hidden;
border-radius: 2px;
}
.uni-list-chat__header-image {
margin: 1px;
/* #ifdef APP-NVUE */
width: 50px;
height: 50px;
/* #endif */
/* #ifndef APP-NVUE */
width: $avatar-width;
height: $avatar-width;
/* #endif */
}
/* #ifndef APP-NVUE */
.uni-list-chat__header-image {
display: block;
width: 100%;
height: 100%;
}
.avatarItem--1 {
width: 100%;
height: 100%;
}
.avatarItem--2 {
width: 47%;
height: 47%;
}
.avatarItem--3 {
width: 32%;
height: 32%;
}
/* #endif */
.header--circle {
border-radius: 50%;
}
.uni-list-chat__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
overflow: hidden;
padding: 2px 0;
}
.uni-list-chat__content-main {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: space-between;
padding-left: $uni-spacing-row-base;
flex: 1;
overflow: hidden;
}
.uni-list-chat__content-title {
font-size: $title-size;
color: $title-color;
font-weight: $title-weight;
overflow: hidden;
}
.uni-list-chat__content-note {
margin-top: 3px;
color: $note-color;
font-size: $note-size;
font-weight: $title-weight;
overflow: hidden;
}
.uni-list-chat__content-extra {
/* #ifndef APP-NVUE */
flex-shrink: 0;
display: flex;
/* #endif */
flex-direction: column;
justify-content: space-between;
align-items: flex-end;
margin-left: 5px;
}
.uni-list-chat__content-extra-text {
color: $right-text-color;
font-size: $right-text-size;
font-weight: $right-text-weight;
overflow: hidden;
}
.uni-list-chat__badge-pos {
position: absolute;
/* #ifdef APP-NVUE */
left: 55px;
top: 3px;
/* #endif */
/* #ifndef APP-NVUE */
left: calc(#{$avatar-width} + 10px - #{$badge-space} + #{$badge-left});
top: calc(#{$uni-spacing-row-base}/ 2 + 1px + #{$badge-top});
/* #endif */
}
.uni-list-chat__badge {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
border-radius: 100px;
background-color: $badge-background-color;
}
.uni-list-chat__badge-text {
color: $badge-color;
font-size: $badge-font;
}
.uni-badge--single {
/* #ifndef APP-NVUE */
// left: calc(#{$avatar-width} + 7px + #{$badge-left});
/* #endif */
width: $badge-size;
height: $badge-size;
}
.uni-badge--complex {
/* #ifdef APP-NVUE */
left: 50px;
/* #endif */
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
height: $badge-size;
padding: 0 $badge-space;
}
.uni-badge--dot {
/* #ifdef APP-NVUE */
left: 60px;
top: 6px;
/* #endif */
/* #ifndef APP-NVUE */
left: calc(#{$avatar-width} + 15px - #{$dot-width}/ 2 + 1px + #{$badge-left});
/* #endif */
width: $dot-width;
height: $dot-height;
padding: 0;
}
.uni-list-chat--right {
/* #ifdef APP-NVUE */
left: 0;
/* #endif */
}
</style>

View File

@@ -0,0 +1,108 @@
<template>
<!-- #ifndef APP-NVUE -->
<view class="uni-list uni-border-top-bottom">
<view v-if="border" class="uni-list--border-top"></view>
<slot />
<view v-if="border" class="uni-list--border-bottom"></view>
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<list class="uni-list" :class="{ 'uni-list--border': border }" :enableBackToTop="enableBackToTop" loadmoreoffset="15"><slot /></list>
<!-- #endif -->
</template>
<script>
/**
* List 列表
* @description 列表组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=24
* @property {String} border = [true|false] 标题
*/
export default {
name: 'uniListWx',
'mp-weixin': {
options: {
multipleSlots: false
}
},
props: {
enableBackToTop: {
type: [Boolean, String],
default: false
},
scrollY: {
type: [Boolean, String],
default: false
},
border: {
type: Boolean,
default: true
}
},
// provide() {
// return {
// list: this
// };
// },
created() {
this.firstChildAppend = false;
},
methods: {
loadMore(e) {
this.$emit('scrolltolower');
}
}
};
</script>
<style lang="scss" scoped>
$uni-bg-color:#ffffff;
$uni-border-color:#e5e5e5;
.uni-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
background-color: $uni-bg-color;
position: relative;
flex-direction: column;
}
.uni-list--border {
position: relative;
/* #ifdef APP-NVUE */
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 0.5px;
border-bottom-color: $uni-border-color;
border-bottom-style: solid;
border-bottom-width: 0.5px;
/* #endif */
z-index: -1;
}
/* #ifndef APP-NVUE */
.uni-list--border-top {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $uni-border-color;
z-index: 1;
}
.uni-list--border-bottom {
position: absolute;
bottom: 0;
right: 0;
left: 0;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
background-color: $uni-border-color;
}
/* #endif */
</style>

View File

@@ -0,0 +1,140 @@
<template>
<view class="uni-section">
<view class="uni-section-header" nvue>
<view v-if="type" class="uni-section__head">
<view :class="type" class="uni-section__head-tag"/>
</view>
<view class="uni-section__content">
<text :class="{'distraction':!subTitle}" :style="{color:color}" class="uni-section__content-title">{{ title }}</text>
<text v-if="subTitle" class="uni-section__content-sub">{{ subTitle }}</text>
</view>
</view>
<view :style="{padding: padding ? '10px' : ''}">
<slot/>
</view>
</view>
</template>
<script>
/**
* Section 标题栏
* @description 标题栏
* @property {String} type = [line|circle] 标题装饰类型
* @value line 竖线
* @value circle 圆形
* @property {String} title 主标题
* @property {String} subTitle 副标题
*/
export default {
name: 'UniSection',
emits:['click'],
props: {
type: {
type: String,
default: ''
},
title: {
type: String,
default: ''
},
color:{
type: String,
default: '#333'
},
subTitle: {
type: String,
default: ''
},
padding: {
type: Boolean,
default: false
}
},
data() {
return {}
},
watch: {
title(newVal) {
if (uni.report && newVal !== '') {
uni.report('title', newVal)
}
}
},
methods: {
onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped>
$uni-primary: #2979ff !default;
.uni-section {
background-color: #fff;
// overflow: hidden;
margin-top: 10px;
}
.uni-section-header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 12px 10px;
// height: 50px;
font-weight: normal;
}
.uni-section__head {
flex-direction: row;
justify-content: center;
align-items: center;
margin-right: 10px;
}
.line {
height: 12px;
background-color: $uni-primary;
border-radius: 10px;
width: 4px;
}
.circle {
width: 8px;
height: 8px;
border-top-right-radius: 50px;
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
border-bottom-right-radius: 50px;
background-color: $uni-primary;
}
.uni-section__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
flex: 1;
color: #333;
}
.uni-section__content-title {
font-size: 14px;
color: $uni-primary;
}
.distraction {
flex-direction: row;
align-items: center;
}
.uni-section__content-sub {
font-size: 12px;
color: #999;
line-height: 16px;
margin-top: 2px;
}
</style>

View File

@@ -0,0 +1,20 @@
<template>
<zmm-watermark :opacity="0.05" :watermark="watermark"></zmm-watermark>
</template>
<script>
export default {
name: 'watermark',
data() {
return {};
},
computed: {
watermark() {
return this.$store.state.watermark;
}
}
};
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,187 @@
<template>
<view class="zmm-picker-region" :style="{'cursor':disabled ? 'move' : 'default'}">
<picker
:disabled="disabled"
mode="multiSelector"
@columnchange="Citycolumnchange($event)"
@change="pickerchange"
:value="Index"
:range="pickerArr"
range-key="label"
@tap="iconopen"
@cancel="opend = false"
>
<view class="zmm-picker-region-uni-input" :style="{'color':disabled ? '#666' : '#333'}" :class="{ 'zmm-picker-region-placeholder': modelValue == '' }">
<text :style="{ 'text-align': textAlign }" v-if="modelValue == ''">{{ placeholder }}</text>
<text :style="{ 'text-align': textAlign }" v-else>{{ modelValue }}</text>
</view>
</picker>
</view>
</template>
<script>
import citydata from '@/common/city.js';
export default {
emits: ['update:modelValue','change'],
data() {
return {
opend: false,
Index: [0,0,0],
citydata:citydata.data
};
},
behaviors: ['uni://form-field'], //必须要写不然微信小程序收不到值
props: {
rangeLeave:{//联动级别
type:Number,
default:2
},
textAlign: {
type: String,
default: 'center'
},
disabled: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: '请点击选择'
},
modelValue: {
type: [String]
}
},
mounted() {
},
computed:{
pickerArr(){
var arr=[]
function findarr(arrx){
var arr=[]
for (var i = 0; i < arrx.length; i++) {
var item = arrx[i]
arr.push({
label:item.label,
value:item.value,
position:item.position
})
}
return arr
}
for (var i = 0; i < this.rangeLeave; i++) {
arr.push([])
if(i==0){//一级数据
arr[i]=findarr(this.citydata)
}
if(i==1){//二级数据
if(this.citydata[this.Index[0]]){
arr[i]=findarr(this.citydata[this.Index[0]]['children'])
}else{
arr[i]=[]
}
}
if(i==2){//三级数据
if(this.citydata[this.Index[0]]&&this.citydata[this.Index[0]]['children'][this.Index[1]]){
arr[i]=findarr(this.citydata[this.Index[0]]['children'][this.Index[1]]['children'])
}else{
arr[i]=[]
}
}
}
return arr
}
},
watch: {
modelValue:{
deep: true,
immediate:true,
handler(val) {
this.iniIndex()
}
}
},
methods: {
iniIndex(){
var arr=this.modelValue.split(',')
function findLabelIndex(arr,label){
if(!label){
return 0
}
for (var i = 0; i < arr.length; i++) {
if(arr[i].label==label){
return i
break;
}
}
return 0
}
for (var i = 0; i < arr.length; i++) {
this.Index[i]=findLabelIndex(this.pickerArr[i],arr[i])
}
},
iconopen() {
if (this.disabled) {
return;
}
this.opend = true;
},
pickerchange(e) {
var text=[]
if(this.rangeLeave==1){
text.push(this.pickerArr[0][e.detail.value[0]].label)
}
if(this.rangeLeave==2){
text.push(this.pickerArr[0][e.detail.value[0]].label)
text.push(this.pickerArr[1][e.detail.value[1]].label)
}
if(this.rangeLeave==3){
text.push(this.pickerArr[0][e.detail.value[0]].label)
text.push(this.pickerArr[1][e.detail.value[1]].label)
text.push(this.pickerArr[2][e.detail.value[2]].label)
}
this.opend = !this.opend;
this.$emit('update:modelValue', text.toString());
this.$emit('change', text.toString())
},
Citycolumnchange(e) {
if (e.detail.column == 0&&this.rangeLeave>1) {//第一行发生变化重置第二列索引
this.Index.splice(1, 1, 0);
}
if (e.detail.column == 1&&this.rangeLeave>2) {
this.Index.splice(2, 1, 0);
}
this.Index[e.detail.column] = e.detail.value;//设置index
this.$forceUpdate();
},
}
};
</script>
<style>
.zmm-picker-region {
}
.zmm-picker-region-uni-input {
height: 90rpx;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background-color: #fff;
}
.zmm-picker-region-uni-input text {
flex: 1;
font-size: 28rpx;
padding:0 12rpx;
}
.zmm-picker-region-placeholder {
width: 100%;
}
.zmm-picker-region-placeholder text{
color: #666;
}
</style>

View File

@@ -0,0 +1,105 @@
<template>
<view>
<radio-group class="zmm-radio-group" @change="radioChange">
<label class="zmm-radio-group-label" v-for="(item,index) in ranges" :key="index" :class="{'checked':index === current}">
<view class="zmm-radio-group-radio">
<radio style="transform:scale(0.9)" :value="item.value" :checked="index === current" />
</view>
<view class="zmm-radio-group-label-text">{{ item.label }}</view>
<uni-icons class="zmm-radio-group-label-icon" type="checkmarkempty" color="#09C160" size="20"></uni-icons>
</label>
</radio-group>
</view>
</template>
<script>
export default {
emits: ['update:modelValue','change'],
data() {
return {
current: '',
val: '',
ranges: []
};
},
props: {
modelValue: {
type: [String]
},
range: {
//数组label value
type: Array,
default: []
}
},
watch: {
range(v) {
this.init();
},
modelValue(val) {
this.init();
},
},
created() {
this.init();
},
methods: {
init() {
this.ranges = JSON.parse(JSON.stringify(this.range));
this.getval(this.modelValue);
},
getval(e) {
if (!e) {
return;
}
var val = e;
var items = this.ranges;
for (var i = 0; i < items.length; ++i) {
const item = items[i];
if (item.value==val) {
this.current=i
break;
}
}
},
radioChange(e) {
this.getval(e.detail.value);
this.$emit('update:modelValue', e.detail.value)
this.$emit('change', e.detail.value)
}
}
};
</script>
<style>
.zmm-radio-group {
display: flex;
flex-direction: column;
}
.zmm-radio-group-radio{
display: none;
}
.zmm-radio-group-label.checked{
}
.zmm-radio-group-label-icon{display: none;}
.zmm-radio-group-label.checked .zmm-radio-group-label-icon{
display: block;
}
.zmm-radio-group-label {
height: 90rpx;
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
background-color: #fff;
border-bottom: 1px #eee solid;
padding: 24rpx;
}
.zmm-radio-group-label:nth-last-child(1){
margin-right: 0;
}
.zmm-radio-group-label-text{
margin-left: 10rpx;
font-size: 28rpx;
margin-right: auto;
}
</style>

View File

@@ -0,0 +1,277 @@
<template>
<view v-if="show">
<view class="uni-padding-wrap">
<block v-if="!recording && !playing && !hasRecord">
<view class="page-body-time">
<text class="time-big">{{formatedRecordTime}}</text>
</view>
<view class="page-body-buttons">
<view class="page-body-button" @click="startRecord">
<image src="@/static/record.png"></image>
</view>
</view>
</block>
<block v-if="recording === true">
<view class="page-body-time">
<text class="time-big">{{formatedRecordTime}}</text>
</view>
<view class="page-body-buttons">
<view class="page-body-button" @click="stopRecord">
<view class="button-stop-record"></view>
</view>
</view>
</block>
<block v-if="hasRecord === true && playing === false">
<view class="page-body-time">
<text class="time-big">{{formatedPlayTime}}</text>
<text class="time-small">{{formatedRecordTime}}</text>
</view>
<view class="page-body-buttons">
<view class="page-body-button" @click="playVoice">
<image src="@/static/play.png"></image>
</view>
<view class="page-body-button" @click="clear">
<image src="@/static/trash.png"></image>
</view>
</view>
</block>
<block v-if="hasRecord === true && playing === true">
<view class="page-body-time">
<text class="time-big">{{formatedPlayTime}}</text>
<text class="time-small">{{formatedRecordTime}}</text>
</view>
<view class="page-body-buttons">
<view class="page-body-button" @click="stopVoice">
<image src="@/static/stop.png"></image>
</view>
<view class="page-body-button" @click="clear">
<image src="@/static/trash.png"></image>
</view>
</view>
</block>
</view>
</view>
</template>
<script>
// #ifdef APP-PLUS
import permision from "@/common/permission.js"
// #endif
var playTimeInterval = null;
var recordTimeInterval = null;
var recorderManager = null;
var music = null;
export default {
props:{
show:{
type:Boolean,
default:true
}
},
emits:['recorderStop'],
data() {
return {
recording: false, //录音中
playing: false, //播放中
hasRecord: false, //是否有了一个
tempFilePath: '',
recordTime: 0,
playTime: 0,
formatedRecordTime: '00:00:00', //录音的总时间
formatedPlayTime: '00:00:00' //播放录音的当前时间
}
},
beforeDestroy: function() {
this.clear();
},
mounted() {
music = uni.createInnerAudioContext();
music.onEnded(() => {
clearInterval(playTimeInterval)
var playTime = 0
// console.log('play voice finished')
this.playing = false;
this.formatedPlayTime = this.formatTime(playTime);
this.playTime = playTime;
});
recorderManager = uni.getRecorderManager();
recorderManager.onStart(() => {
// console.log('recorder start');
this.recording = true;
recordTimeInterval = setInterval(() => {
this.recordTime += 1;
this.formatedRecordTime = this.formatTime(this.recordTime);
}, 1000)
});
recorderManager.onStop((res) => {
// console.log('on stop');
music.src = res.tempFilePath;
this.hasRecord = true;
this.recording = false;
this.$emit('recorderStop',{
formatedRecordTime:this.formatedRecordTime,
recordTime:this.recordTime<1 ? this.recordTime+1 : this.recordTime,
recordFilePath:res.tempFilePath
})
this.clear()
});
recorderManager.onError(() => {
console.log('recorder onError');
});
},
methods: {
formatTime(time) {
if (typeof time !== 'number' || time < 0) {
return time
}
var hour = parseInt(time / 3600)
time = time % 3600
var minute = parseInt(time / 60)
time = time % 60
var second = time
return ([hour, minute, second]).map(function(n) {
n = n.toString()
return n[1] ? n : '0' + n
}).join(':')
},
async startRecord() { //开始录音
// #ifdef APP-PLUS
let status = await this.checkPermission();
if (status !== 1) {
return;
}
// #endif
// TODO ios 在没有请求过权限之前无法得知是否有相关权限,这种状态下需要直接调用录音,但没有状态或回调判断用户拒绝
recorderManager.start({
duration:600000,
sampleRate:44100,
format:'mp3'
});
},
stopRecord() { //停止录音
recorderManager.stop();
},
playVoice() {
// console.log('play voice');
this.playing = true;
playTimeInterval = setInterval(() => {
this.playTime += 1;
this.formatedPlayTime = this.formatTime(this.playTime);
}, 1000)
music.play();
},
stopVoice() {
clearInterval(playTimeInterval)
this.playing = false;
this.formatedPlayTime = this.formatTime(0);
this.playTime = 0;
music.stop();
},
end() {
music.stop();
recorderManager.stop();
clearInterval(recordTimeInterval)
clearInterval(playTimeInterval);
this.recording = false, this.playing = false, this.hasRecord = false;
this.playTime = 0, this.recordTime = 0;
this.formatedRecordTime = "00:00:00", this.formatedRecordTime = "00:00:00";
},
clear() {
this.end();
}
// #ifdef APP-PLUS
,
async checkPermission() {
let status = permision.isIOS ? await permision.requestIOS('record') :
await permision.requestAndroid('android.permission.RECORD_AUDIO');
if (status === null || status === 1) {
status = 1;
} else if (status === 2) {
uni.showModal({
content: "系统麦克风已关闭",
confirmText: "确定",
showCancel: false,
success: function(res) {
}
})
} else {
uni.showModal({
content: "需要麦克风权限",
confirmText: "设置",
success: function(res) {
if (res.confirm) {
permision.gotoAppSetting();
}
}
})
}
return status;
}
// #endif
}
}
</script>
<style scoped>
.uni-padding-wrap{}
image {
width: 130rpx;
height: 130rpx;
}
.page-body-wrapper {
justify-content: space-between;
flex-grow: 1;
margin-bottom: 300rpx;
}
.page-body-time {
display: flex;
flex-direction: column;
align-items: center;
}
.time-big {
font-size: 26rpx;
margin: 20rpx;
}
.time-small {
font-size: 26rpx;
}
.page-body-buttons{
display: flex;
justify-content: space-around;
}
.page-body-button {
/* width: 250rpx; */
text-align: center;
}
.button-stop-record {
box-sizing: border-box;
width: 130rpx;
height: 130rpx;
border: 20rpx solid #fff;
background-color: #4CD964;
border-radius: 50%;
animation: colors 1s linear infinite;
}
@keyframes colors {
0% {
opacity: 1;
}
50% {
opacity: .7;
}
100% {
opacity: 1;
}
}
</style>

View File

@@ -0,0 +1,158 @@
<template>
<view>
<image class="zmm-upload-avatar" :src="modelValue" :style="{ width: imgSize + 'rpx', height: imgSize + 'rpx', 'border-radius': imgRadius + 'rpx' }" mode="aspectFill" @click="selectImage"></image>
</view>
</template>
<script>
import http from '@/common/request.js';
export default {
name: 'zmm-upload-avatar',
emits: ['update:modelValue','change'],
props: {
modelValue: {
type: [String]
},
imgSize: {
//图片大小
type: Number,
default: 120
},
imgRadius: {
//图片圆角
type: Number,
default: 12
},
disabled: {
type: Boolean,
default: false
},
action: {
//后台上传接口
type: String,
default: http.baseUrl + '/file/upload'
},
formData: {
//上传所附带数据
type: Object
},
header: {
//自定义头
type: Object,
default() {
return {
Authorization: uni.getStorageSync('Authorization'),
device: uni.getStorageSync('device'),
version: uni.getStorageSync('version')
};
}
},
fileKey: {
//后端接受的filekey
type: String,
default: 'file'
}
},
data() {
return {
isDestroyed: false,
showUploadProgress: true
};
},
mounted: function() {},
destroyed: function() {
this.isDestroyed = true;
},
computed: {},
methods: {
selectImage: function() {
var _self = this;
uni.chooseImage({
count: 1,
sourceType: ['album', 'camera'],
success: function(e) {
var imagePathArr = e.tempFilePaths;
//检查服务器地址是否设置,设置即表示图片要上传到服务器
if (_self.action) {
uni.showToast({
title: '上传中',
icon: 'none',
mask: false
});
var remoteIndexStart = 1 - imagePathArr.length;
var promiseWorkList = [];
var keyname = _self.fileKey ? _self.fileKey : 'upload-images';
var completeImages = 0;
for (let i = 0; i < imagePathArr.length; i++) {
promiseWorkList.push(
new Promise((resolve, reject) => {
let remoteUrlIndex = remoteIndexStart + i;
uni.uploadFile({
url: _self.action,
fileType: 'image',
header: _self.header,
formData: _self.formData,
filePath: imagePathArr[i],
name: keyname,
success: function(res) {
if (res.statusCode === 200) {
if (_self.isDestroyed) {
return;
}
completeImages++;
if (_self.showUploadProgress) {
uni.showToast({
title: '上传中',
icon: 'none',
mask: false,
duration: 500
});
}
// console.log('success to upload image: ' + res.data)
resolve(res.data);
} else {
console.log('fail to upload image:' + res.data);
reject('fail to upload image:' + remoteUrlIndex);
}
},
fail: function(res) {
uni.showToast({
title: '上传失败请检查网络',
icon: 'none'
});
console.log('fail to upload image:' + res);
reject('fail to upload image:' + remoteUrlIndex);
}
});
})
);
}
Promise.all(promiseWorkList).then(result => {
if (_self.isDestroyed) {
return;
}
for (let i = 0; i < result.length; i++) {
var resItem = JSON.parse(result[i]);
_self.$emit('update:modelValue', resItem.data.fullPath);
_self.$emit('change', resItem.data.fullPath);
}
});
} else {
_self.$emit('update:modelValue', '../../static/img/avatar/avatar2.jpg');
_self.$emit('change', '../../static/img/avatar/avatar2.jpg');
}
}
});
}
}
};
</script>
<style scoped>
.zmm-upload-avatar {
}
</style>

View File

@@ -0,0 +1,319 @@
<template>
<view @touchmove.stop.prevent="moveHandle" class="moveWrap">
<movable-area class="movarea" ref="areaBox" id="areaBox" :style="{height:imgSize*rowNum +200+ 'rpx'}">
<view class="imgBox" :style="{height:imgSize*rowNum + 'rpx'}">
<view :id="'img' + idx" class="imgItem" v-for="(item, idx) in imgList" :key="idx"
:style="{transition:addJump?' all 0.5s':'',opacity:idx===selectIdx?'0':'1', width: imgSize + 'rpx', height: imgSize + 'rpx', borderRadius:imgRadius+'rpx',padding:imgPadding+'rpx',left:(hoverImgIdx==='img'+idx?curHoverBoxMove.x+'rpx':''),top:(hoverImgIdx==='img'+idx?curHoverBoxMove.y+'rpx':'')}">
<view class="imgItem-img" @tap="itemclick(item,idx)" @touchstart="tstr(idx, $event)" @touchmove="tsmo" @touchend="toend">
<image v-if="item.videoUrl" class="imgItem-play" src="../../static/img/bf.png" mode="aspectFill"></image>
<image :style="{borderRadius:imgRadius+'rpx' }" :ref="'img' + idx" :src="item.url" mode="aspectFill"></image>
</view>
</view>
<slot></slot>
</view>
<movable-view v-if="moveSrc" :animation="false" class="moveV" :x="x" :y="y" direction="all"
@change="onChange"
:style="{ width: imgSize + 'rpx', height: imgSize + 'rpx',padding:imgPadding+'rpx' }">
<image v-if="moveSrc.videoUrl" class="imgItem-play" src="../../static/img/bf.png" mode="aspectFill"></image>
<image :style="{borderRadius:imgRadius+'rpx' }" :src="moveSrc.url" mode="aspectFill"></image>
</movable-view>
</movable-area>
<view v-if="showDelete">
<view class="delete" :class="{'deleteType':deleteType}">
<text v-if="deleteType">松开删除</text>
<text v-else>拖动到此处删除</text>
</view>
</view>
</view>
</template>
<script>
export default {
emits:['itemclick','update:imgList','deleteImage','change','moveEndList'],
props: {
//图片列表
imgList: {
type: Array,
default: function() {
return [];
}
},
//图片大小
imgSize: {
type: Number
},
//图片间距
imgPadding: {
type: Number
},
//图片行数
rowNum: {
type: Number
},
//图片圆角
imgRadius: {
type: Number
}
},
components: {},
data() {
return {
showDelete:false,
touchobj:null,
deleteType:false,
addJump:false,
areaBoxInfo: {},
x: 0,
y: 0,
selectIdx: null,
moveSrc: null,
areaBoxTop: 0,
hoverImgIdx: '',
inBoxXY: {},
curHoverBoxMove: {
x: 0,
y: 0
}
};
},
watch: {
hoverImgIdx(e) {
let idx = this.selectIdx
let hoverIdx = parseInt(e.split('img')[1]);
if (this.imgList[idx]) {
let selectRow = this.imgList[idx].y / uni.upx2px(this.imgSize)
let selectColum= this.imgList[idx].x / uni.upx2px(this.imgSize)
let hoverRow = this.imgList[hoverIdx].y / uni.upx2px(this.imgSize)
let hoverColum= this.imgList[hoverIdx].x / uni.upx2px(this.imgSize)
let left = -(this.imgSize * (hoverColum - selectColum))
let top= -(this.imgSize * (hoverRow - selectRow))
this.curHoverBoxMove = {
x: left,
y: top,
}
}
},
// imgList(e){
// console.log('变化了item')
// this.$nextTick(()=>{
// this.setImgXy()
// })
// }
imgList: {
deep: true,//深度监听可见听对象中的元素变化例:obj.id
immediate:false,//在进入页面时第一次绑定值不会立刻执行监听只有数据发生改变才会执行handler中的操作
handler(val) {//watch事件
// console.log('变化了item')
this.$nextTick(()=>{
this.setImgXy()
})
}
}
},
methods: {
itemclick(e,i){
this.$emit('itemclick',e,i)
},
moveHandle() {
return;
},
deleteImage: function(e) {
var index = e
var deletedImage = this.imgList[index]
this.imgList.splice(index, 1)
this.$emit('deleteImage', deletedImage)
},
onChange(e) {
var boxHeight=this.imgSize * this.rowNum
var deleteHeight=(boxHeight-80)/2
if(e.detail.y>deleteHeight){
this.deleteType=true
}else{
this.deleteType=false
}
this.showDelete=true
this.$emit('change',e)
},
tstr(e, s) {
this.touchobj=this.imgList[e]
this.addJump=true
this.getDomInfo('areaBox', info => {
this.areaBoxInfo = info;
//获取拖拽区域的上边距和下边距
let areaBoxTop = this.areaBoxInfo.top;
let areaBoxLeft = this.areaBoxInfo.left;
// 设置可移动方块的初始位置为当前所选中图片的位置坐标
this.x = this.imgList[e].x;
this.y = this.imgList[e].y;
//显示可移动方块
this.moveSrc = this.imgList[e];
//保存当前所选择的图片索引
this.selectIdx = e;
var x = s.changedTouches[0].clientX - areaBoxLeft;
var y = s.changedTouches[0].clientY - areaBoxTop;
// 保存鼠标在图片内的坐标
this.inBoxXY = {
x: x - this.imgList[e].x,
y: y - this.imgList[e].y,
}
});
},
tsmo(e) {
let areaBoxTop = this.areaBoxInfo.top;
let areaBoxLeft = this.areaBoxInfo.left;
let imgSize = this.imgSize;
//重置为以拖拽盒子左上角为坐标原点
var x = e.changedTouches[0].clientX - areaBoxLeft;
var y = e.changedTouches[0].clientY - areaBoxTop;
this.x = x - this.inBoxXY.x;
this.y = y - this.inBoxXY.y;
this.imgList.forEach((item, idx) => {
if (x > item.x && x < item.x + imgSize && y > item.y && y < item.y + imgSize) {
this.hoverImgIdx = 'img' + idx
}
});
},
toend(e) {
this.addJump=false
if(this.deleteType){
this.deleteImage(this.selectIdx)
}else{
// 移动结束隐藏可移动方块
let beforeIdx = this.selectIdx;
let afterIdx = parseInt(this.hoverImgIdx.split('img')[1]);
if (this.hoverImgIdx !== '' && beforeIdx !== afterIdx) {
this.imgList[beforeIdx] = this.imgList[afterIdx];
this.imgList[afterIdx] = this.moveSrc;
this.$emit('moveEndList', this.imgList);
}
}
this.moveSrc = '';
this.hoverImgIdx = ''
this.selectIdx = null
this.deleteType=false
this.showDelete=false
this.$nextTick(()=>{
this.setImgXy()
})
},
getDomInfo(id, callBack) {
const query = uni.createSelectorQuery().in(this);
query.select('#' + id)
.boundingClientRect()
.exec(function(res) {
callBack(res[0]);
});
},
setImgXy(){//设置每个图片的基础xy轴
this.getDomInfo('areaBox', info => {
this.areaBoxInfo = info;
// 设置区域内所有图片的左上角坐标
this.imgList.forEach((item, idx) => {
this.getDomInfo('img' + idx, res => {
item.x = res.left - info.left;
});
this.getDomInfo('img' + idx, res => {
item.y = res.top - info.top;
});
});
});
}
},
mounted() {
this.setImgXy()
}
};
</script>
<style lang="scss" scoped>
.imgItem-play{
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 80rpx !important;
height: 80rpx !important;
transform: translate(-50%,-50%);
}
.movarea {
width: 100%;
height: 320rpx;
display: flex;
flex-direction: row;
}
.imgBox {
position: relative;
z-index: 1;
width: 100%;
height: 320rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
.imgItem {
position: relative;
box-sizing: border-box;
left: 0;
top: 0;
box-sizing: border-box;
image {
width: 100%;
height: 100%;
// transition: all 0.2s;
vertical-align: top;
}
}
}
.moveV {
opacity: 0.6;
z-index: 0;
box-sizing: border-box;
image {
width: 100%;
height: 100%;
}
}
.select {
opacity: 0;
}
.zmm-upload-image-deleteIcon {
right: 0rpx;
top: 0rpx;
position: absolute;
background-color: rgba(0, 0, 0, 0.3);
width: 36rpx;
height: 36rpx;
text-align: center;
border-radius: 50%;
color: white;
font-size: 30rpx;
z-index: 2;
display: flex;flex-direction: row;align-items: center;justify-content: center;
}
.delete{
position: absolute;
bottom: 0rpx;
left: 0;
width: 100%;
height: 80rpx;
line-height: 80rpx;
background: #C04A42;
text-align: center;
transition: all 0.3s;
}
.delete text{
color: #fff;
}
.deleteType{
transform: scale(1.1);
}
.moveWrap{
position: relative;
}
.imgItem-img{
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,431 @@
<template>
<view class="zmm-upload-image" v-if="show">
<zmm-upload-image-drag :key="key" :rowNum="rows" :imgRadius="imgRadius" :imgSize="imgSize" :imgPadding="imgPadding" :imgList="fileList" @deleteImage="deleteImage" @itemclick="itemclick">
<view v-if="showAdd && fileList.length < limit" @tap="chooseTap" :style="{ width: imgSize + 'rpx', height: imgSize + 'rpx',padding:imgPadding+'rpx' }" class="zmm-upload-image-item-slot">
<view class="zmm-upload-image-item-slotIcon">
<uni-icons type="plusempty" color="#6E6E6E" size="24" />
</view>
</view>
</zmm-upload-image-drag>
</view>
</template>
<script>
import zmmUploadImageDrag from './zmm-upload-image-drag.vue';
import http from '@/common/request.js';
export default {
components: {
zmmUploadImageDrag
},
name: 'zmm-upload-image',
emits: ['update:modelValue', 'deleteImage','allComplete','oneComplete'],
props: {
modelValue: {
type: [Array]
},
chooseType:{
type:String,
default:'chooseImage'//chooseImage图片 chooseVideo视频 chooseMedia图片或视频
},
imgSize: {
//图片大小
type: Number,
default: 214
},
imgPadding: {
//图片间距
type: Number,
default: 6
},
imgRadius: {
//图片圆角
type: Number,
default: 4
},
fileList: {
//图片数据
type: Array,
default(){
return []
}
},
rowNumber: {
//一行多少个图片
type: Number,
default: 3
},
showAdd: {
//增加按钮
type: Boolean,
default: true
},
show: {
//是否显示
type: Boolean,
default: true
},
fileAction: {
//后台文件上传接口
type: String,
default: http.baseUrl + '/file/upload'
},
videoAction: {
//后台上传接口
type: String,
default: http.baseUrl + '/file/uploadVideo'
},
formData: {
//上传所附带数据
type: Object
},
header: {
//自定义头
type: Object,
default() {
return {
Authorization: uni.getStorageSync('Authorization'),
device: uni.getStorageSync('device'),
version: uni.getStorageSync('version')
};
}
},
limit: {
//图片数量
type: Number,
default: 9
},
fileKey: {
//后端接受的filekey
type: String,
default: 'file'
}
},
data() {
return {
key:0,
isDestroyed: false,
showUploadProgress:true
};
},
watch: {
modelValue(val) {
console.log(val);
},
fileList(e){
// console.log('变化了list')
this.key++
},
rows(e){
this.key++
}
// fileList: {
// deep: true,//深度监听可见听对象中的元素变化例:obj.id
// immediate:false,//在进入页面时第一次绑定值不会立刻执行监听只有数据发生改变才会执行handler中的操作
// handler(val) {//watch事件
// console.log('变化了list')
// this.key++
// }
// }
},
mounted: function() {},
destroyed: function() {
this.isDestroyed = true;
},
computed: {
rows() {
var buzhu=this.showAdd && this.fileList.length < this.limit ? 1 : 0;
var rows=Math.ceil((this.fileList.length+buzhu) / this.rowNumber);
return rows
}
},
methods: {
itemclick(e,i){
if(!e.videoUrl){
var imgs=[]
for (var i = 0; i < this.fileList.length; i++) {
var item=this.fileList[i]
if(!item.videoUrl){
imgs.push(item.url)
}
}
this.previewImage(imgs,i)
}else{
this.$fc.plusDownload({onlinePath:e.videoUrl}).then(res=>{
this.$fc.plusOpenFile({filePath:res})
})
}
},
chooseTap(){
switch (this.chooseType){
case 'chooseImage':
this.selectImage()
break;
case 'chooseVideo':
this.selectVideo()
break;
case 'chooseMedia':
uni.showActionSheet({
itemList: ['图片', '视频'],
success: (res) => {
switch (res.tapIndex){
case 0:
this.selectImage()
break;
case 1:
this.selectVideo()
break;
default:
break;
}
}
});
break;
default:
break;
}
},
selectImage: function() {
var _self = this;
if (!_self.fileList) {
_self.fileList = [];
}
uni.chooseImage({
count: _self.limit ? _self.limit - _self.fileList.length : 999,
sourceType: ['album', 'camera'],
sizeType:['original','compressed'],
success: function(e) {
var imagePathArr = e.tempFilePaths;
//如果设置了limit限制在web上count参数无效这里做判断控制选择的数量是否合要求
//在非微信小程序里,虽然可以选多张,但选择的结果会被截掉
//在app里会自动做选择数量的限制
if (_self.limit) {
var availableImageNumber = _self.limit - _self.fileList.length;
if (availableImageNumber < imagePathArr.length) {
uni.showToast({
title: '图片总数限制为' + _self.limit + '张,当前还可以选' + availableImageNumber + '张',
icon: 'none',
mask: false,
duration: 2000
});
return;
}
}
//检查服务器地址是否设置,设置即表示图片要上传到服务器
if (_self.fileAction) {
uni.showToast({
title: '上传进度0/' + imagePathArr.length,
icon: 'none',
mask: false
});
var remoteIndexStart = _self.fileList.length - imagePathArr.length;
var promiseWorkList = [];
var keyname = _self.fileKey ? _self.fileKey : 'upload-images';
var completeImages = 0;
for (let i = 0; i < imagePathArr.length; i++) {
promiseWorkList.push(
new Promise((resolve, reject) => {
let remoteUrlIndex = remoteIndexStart + i;
uni.uploadFile({
url: _self.fileAction,
fileType: 'image',
header: _self.header,
formData: _self.formData,
filePath: imagePathArr[i],
name: keyname,
success: function(res) {
if (res.statusCode === 200) {
if (_self.isDestroyed) {
return;
}
completeImages++;
if (_self.showUploadProgress) {
uni.showToast({
title: '上传进度:' + completeImages + '/' + imagePathArr.length,
icon: 'none',
mask: false,
duration: 500
});
}
// console.log('success to upload image: ' + res.data)
var resItem=JSON.parse(res.data)
_self.$emit('oneComplete', {
name: resItem.data.fileName,
url: resItem.data.fullPath,
type:'IMAGE'
},_self.chooseType);
resolve(res.data);
} else {
console.log('fail to upload image:' + res.data);
reject('fail to upload image:' + remoteUrlIndex);
}
},
fail: function(res) {
uni.showToast({
title: '上传失败请检查网络',
icon: 'none'
});
console.log('fail to upload image:' + res);
reject('fail to upload image:' + remoteUrlIndex);
}
});
})
);
}
Promise.all(promiseWorkList).then(result => {
if (_self.isDestroyed) {
return;
}
var fresult=[]
for (let i = 0; i < result.length; i++) {
var resItem=JSON.parse(result[i])
_self.fileList.push({
name: resItem.data.fileName,
url: resItem.data.fullPath,
type:'IMAGE'
});
fresult.push({
name: resItem.data.fileName,
url: resItem.data.fullPath,
type:'IMAGE'
})
}
_self.$emit('allComplete', fresult,_self.chooseType);
});
} else {
var testImg='图片地址'
for (let i = 0; i < imagePathArr.length; i++) {
_self.fileList.push({
name: 'avatar'+i+'.jpg',
url: testImg
});
}
}
}
});
},
selectVideo: function() {
var _self = this;
if (!_self.fileList) {
_self.fileList = [];
}
uni.chooseVideo({
sourceType: ['album', 'camera'],
compressed:true,
success: function(e) {
if (_self.showUploadProgress) {
uni.showLoading({
title:'上传中...'
})
}
uploadVideo(e).then(res=>{
uni.hideLoading()
_self.fileList.push(res);
_self.$emit('allComplete', res ,_self.chooseType);
})
function uploadVideo(e){
return new Promise((resolve, reject) => {
uni.uploadFile({
url: _self.videoAction,
fileType: 'video',
header: _self.header,
formData: _self.formData,
filePath: e.tempFilePath,
name: _self.fileKey,
success: function(res) {
if (res.statusCode === 200) {
if (_self.isDestroyed) {
return;
}
var resItem=JSON.parse(res.data)
var fresult={
name: resItem.data.fileName,
videoUrl: resItem.data.fullPath,
url: resItem.data.screenShot,
type:'VIDEO'
}
_self.$emit('oneComplete', {
name: resItem.data.fileName,
videoUrl: resItem.data.fullPath,
url: resItem.data.screenShot,
type:'VIDEO'
},_self.chooseType);
resolve(fresult)
} else {
reject(res)
}
},
fail: function(res) {
uni.showToast({
title: '上传失败请检查网络',
icon: 'none'
});
reject(res)
}
});
})
}
}
});
},
deleteImage: function(e) {
this.$emit('deleteImage', e);
},
previewImage: function(arr,index) {
uni.previewImage({
current: index,
indicator: 'number',
loop: true,
urls: arr
});
},
}
};
</script>
<style scoped>
.zmm-upload-image {
width: 100%;
position: relative;
/* display: flex;flex-direction: row;justify-content: center; */
}
.zmm-upload-image-list {
/* width: 714rpx; */
display: flex;
flex-wrap: wrap;
flex-direction: row;
}
.zmm-upload-image-item {
width: 214rpx;
height: 214rpx;
margin: 12rpx;
box-sizing: border-box;
position: relative;
border-radius: 4rpx;
}
.zmm-upload-image-item-img {
width: 100%;
height: 100%;
border-radius: 4rpx;
}
.zmm-upload-image-item-slot{
box-sizing: border-box;
}
.zmm-upload-image-item-slotIcon {
box-sizing: border-box;
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
background-color: #f7f7f7;
}
</style>

View File

@@ -0,0 +1,568 @@
<template name="zy-search">
<view class="good-search-page" :class="Theme">
<view class="good-search-head">
<input :class="{'clearvalue':clearValuebtn}" :maxlength="maxLength" :style="{'line-height': inputHeight+'px','height': inputHeight+'px'}" :focus="isFocus" type="text" confirm-type="search" @confirm="searchStart()" :placeholder="inputPlaceholder" :confirm-hold="true" v-model="searchText" />
<!-- <view v-if="searchText.length>0&&clearValuebtn" class="good-search-icon clearvalue-icon iconfont iconshanchu1" @click="clearvalue()"></view> -->
<view class="good-search-icon search-icon2" @click="goback()">取消</view>
<view class="good-search-icon search-icon iconfont iconsousuo-copy" @click="searchStart()"></view>
<scroll-view class="autocomplay" :style="{'top': inputHeight+5+'px'}" v-if="autocomplaystate" scroll-y>
<view class="autocomplay-item" v-for="(item,index) in autocomplayarr" :key="index" @click="tagsClick(item.value)">
<rich-text :nodes="item.richtxt"></rich-text>
</view>
<view style="color: #999;text-align: center;justify-content: center; line-height: 70rpx;" v-if="autocomplayarr.length<1">没有相关信息</view>
</scroll-view>
</view>
<view class="good-search-body">
<view class="search-history" v-if="hList.length > 0">
<view class="header">
<text>历史记录</text>
<view class="good-search-icon delete-icon iconfont iconshanchu" @click="delhistory()"></view>
</view>
<view class="list">
<view v-for="(item,index) in hList" :key="index" @click="tagsClick(item)">{{item}}</view>
</view>
</view>
<view class="search-showhot" v-if="hotList.length>0">
<view class="header">
<text>猜你想搜的</text>
</view>
<view class="list">
<view v-for="(item,index) in hotList" :key="index" @click="tagsClick(item)">{{item}}</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
clearValuebtn: { //是否显示清空按钮
type: Boolean,
default: true
},
isFocus: { //是否自动获取焦点
type: Boolean,
default: false
},
Theme: { //选择主题class
type: String,
default: 'good-search-circle'
},
maxLength: { //字符最大长度
type: [String, Number],
default: '140'
},
inputHeight: { //搜索框高度单位px
type: [String, Number],
default: '35'
},
inputPlaceholder: { //搜索框默认提示
type: String,
default: '请输入关键词搜索'
},
autoComplaylist: { //自动联想数据
type: Array,
default () {
return []
}
},
historyNum: { //历史记录保存数量
type: Number,
default: 6
},
hotList: { //推荐列表数据
type: Array,
default () {
return []
}
},
speechEngine: { //语音引擎=>讯飞:iFly,百度:'baidu'
type: String,
default: 'iFly'
},
Punctuation: { //是否开启语音识别标点符号
type: Boolean,
default: false
},
callbackTime: { //input回调缓冲时间(不返回500毫秒以内输入的数据防止每输入一个值就会触发一次)
type: Number,
default: 500
},
},
data() {
return {
speechengine: this.speechEngine,
punctuation: this.Punctuation,
historynum: this.historyNum,
callbacktime: this.callbackTime,
autocomplaylist: this.getcomplaylist(this.autoComplaylist),
autocomplayarr: [],
autocomplaystate: false,
searchText: '',
hList: uni.getStorageSync('search_cache'),
};
},
watch: {
historyNum: function(val) {
this.historynum = val;
},
speechEngine: function(val) {
this.engine = val;
},
hotList: function(val) {
this.hotList = val;
},
autoComplaylist: function(val) {
this.autocomplaylist = this.getcomplaylist(val);
if (!this.searchText || this.searchText == '') {
this.autocomplaystate = false
return
}
this.autocomplayarr = this.replacekeyword(this.autocomplaylist, this.searchText)
this.autocomplaystate = this.autocomplayarr.length > 0 ? true : false;
},
searchText: function(val) {
let _this = this;
var searchWords = val.replace(/^ +| +$/g, '');
if (!searchWords || searchWords == '') {
this.autocomplaystate = false
return
}
if (this.calbacktime) {
clearTimeout(_this.calbacktime)
}
this.calbacktime = setTimeout(function() {
_this.inputChange(searchWords)
}, _this.callbackTime);
this.autocomplayarr = this.replacekeyword(this.autocomplaylist, searchWords)
this.autocomplaystate = this.autocomplayarr.length > 0 ? true : false;
},
},
methods: {
goback(){
uni.navigateBack({
delta:1
})
},
clearvalue() { //删除input值
let _this = this;
setTimeout(function() { //增加延时解决键盘收回时导致的@input事件
_this.searchText = ''
}, 20);
this.searchText = ''
},
getcomplaylist(arr) { //初始化自动联想数组
var data = []
for (var i = 0; i < arr.length; i++) {
data.push({
richtxt: arr[i],
value: arr[i]
})
}
return data
},
replacekeyword(arr, searchWords) { //返回符合关键词的高亮数组
var data = []
for (var i = 0; i < arr.length; i++) {
if (arr[i].richtxt.search(searchWords) != -1) {
data.push({
richtxt: arr[i].richtxt.replace(searchWords, "<span style='color: #333;font-weight:bold'>" + searchWords +
"</span>"),
value: arr[i].value
})
}
}
return data
},
tagsClick(item) { //标签点击事件
let _this = this;
setTimeout(function() { //增加延时解决键盘收回时导致的@input事件
_this.searchText = item
_this.searchStart()
}, 20);
this.$emit('tagsClick', item)
},
inputChange(e) { //input回调
this.$emit('inputChange', e)
},
notSupport() { //不支持提醒
uni.showToast({
title: '该平台暂不支持',
icon: 'none',
duration: 1000
});
},
searchStart() { //触发搜索
let _this = this;
if (_this.searchText == '') {
uni.showToast({
title: '请输入关键字',
icon: 'none',
duration: 1000
});
return false;
}
// else {
// if(this.autocomplayarr.length<1){
// this.autocomplaystate=true
// return
// }
// uni.getStorage({
// key: 'search_cache',
// success(res) {
// let list = res.data;
// if (list.length >= _this.historynum) {
// for (let item of list) {
// if (item == _this.searchText) {
// return false;
// }
// }
// list.pop();
// list.unshift(_this.searchText);
// } else {
// for (let item of list) {
// if (item == _this.searchText) {
// return false;
// }
// }
// list.unshift(_this.searchText);
// }
// _this.hList = list;
// uni.setStorage({
// key: 'search_cache',
// data: _this.hList
// });
// },
// fail() {
// _this.hList = [];
// _this.hList.push(_this.searchText);
// uni.setStorage({
// key: 'search_cache',
// data: _this.hList
// });
// }
// })
// }
this.$emit('clickSearch', _this.searchText)
},
delhistory() { //清空历史记录
this.hList = [];
uni.setStorage({
key: 'search_cache',
data: []
});
this.$emit('delhHistory')
},
startRecognize() { //语音输入
let _this = this;
let options = {};
options.engine = _this.speechengine;
options.punctuation = _this.punctuation; // 是否需要标点符号
options.timeout = 1000;
plus.speech.startRecognize(options, function(s) {
_this.searchText = _this.searchText + s;
});
}
}
}
</script>
<style scoped>
@font-face {
font-family: 'iconfont';
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAARsAAsAAAAACTAAAAQeAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDMgqFOIR/ATYCJAMUCwwABCAFhG0HXhshCBEVnP3IfiTGtg+yYk9cckRpaoLn+Ty/c9976WTSnw8NxB3gNgOIO3pXjJjRVmgD9Ab4PpuONYw5YTol8dSz/I/7dt/vpPE4jVIIiZD8//ctbT0e2HotiyTD+1qXxepTJ4rjgALadwoja4DGBsTHZGxxVg7MnJ/5HAI4FKQ80rBxy47YGGidADJs8MDe2B0bZoEj2MbAxFGDrMLCVovVA2Bl8fPyhapig8LS0Na2Axr1o66P31LPSsyiW0ZDfjgPYJ0CDZQHDMiYSd8wdJUrj8YJfs9QF3CwUSgfP9sv7bdMJCBU3ZDBkT5QN/OPp0ECkC4J7Lp5Az6JCAr8bBEE/NKyZX1L2NA60zI4Byq8AakDdnGd+mjadOnkCgUCQZe2JBWQrrOPLDlENBrCKQ7NOhx0lx6dfcRxJB73ZLxDIpGOsVjY60kZ8igzOXw9LgUBGelc6m3cuuVAZsRx5GvKIpG66zZnkSW0fl+aqQyrs2/PC4MqGm3k3o57OnjwGM/GYh13Xl76sMCgA5cKaldtu1A8yVt6VE6qgY0tiXyypWtSP/W45FhtsVi3dUuywx3Tq47MzuWFN8w+ujQr5G1cenhWZsfQenHrt0Nual5cNWf/2RnJgwa45BXXcaVvHjDIwtCj5mC0pbqoUevXj0rVykS01XygQmPGhkWi1q5CD1V4zJiwAgLTK6ixKmnjng+VFzQ3pz5/PqlPaRCoT+KaU6ZLu+LnFx6adKNhoZbr9a1bap1EeNbrdQ2mn6ZhlWmvN9uLq2TsP7U4b5W8FB5bv7F076IbRQtPLDExo9Ibadiy2+o5q1t1pdHrcsP37+hG3bvu21zld1OnSNq6uuWtV79fm/JtF9YYl+plfM3wiugNm1M99VW1huenTU6hsK5/2fz9ay5L1EmqairU5E15Nq0kzL1F8ywu6YspBjsP5znMJAAS1011/YKXvq7rYabb2/VcX9P+QgCB5CWns3um1PxmBw0AT9ymU0Q0kYEv+0/oCeR/Hjmzz4RKslvG701kftwixF9Dp4KSAyQQSufFYQxZ7EYQbLIaUOSgAGhsitMNuzxYBKkOAWzqgEM5PdXpIBlYa8TkBMryFiCk8gFFMpdBk8qXbtg/sMjmDwFSZQ0O4WVcGaSktKcGFplHCXyGVtCklNOfyuYb1mFgtq0suC+0rk1Ckeb9YoUT2kPscLEuvVegLI1wFa/hMBBoSx0Knzbe60uWqakPpYLGxYkGLMR4SAK4GdASyERlo73T4Ps3qBYMGLtAGYi/IMtpJwcKUnIGvdJNLMpLme1EtRLPU/oqxSIj4AoPGmSOAHp6UQcJXkqzQ0a7kGmDFFeXnt4wvsdjgAPNtkSJFiOWBEi7b3fydkLpGqZRkJ7PYym4QEmj2U8UJtGEQ+EtFgsAAA==') format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconcombinedshapecopy:before {
content: "\e616";
}
.iconsousuo-copy:before {
content: "\e600";
}
.iconshanchu:before {
content: "\e64c";
}
.iconshanchu1:before {
content: "\e628";
}
page {
display: flex;
flex-direction: column;
box-sizing: border-box;
background-color: #F5F5F5;
}
view {
display: flex;
font-size: 28upx;
line-height: inherit
}
.good-search-page {
box-sizing: border-box;
width: 750rpx;
padding: 30rpx;
display: flex;
flex-direction: column;
}
.good-search-head {
position: relative;
}
.good-search-head input {
box-sizing: border-box;
width: 100%;
background-color: #F7F7F7;
font-size: 28rpx;
border-radius: 50rpx;
padding: 0 73rpx;
padding-left: 34rpx;
padding-right: 160rpx;
}
.good-search-head input.clearvalue{
}
.good-search-icon {
color: #888;
justify-content: center;
align-items: center;
font-size: 38rpx;
}
.good-search-head .good-search-icon {
width: 73rpx;
height: 100%;
position: absolute;
z-index: 2;
top: 0rpx;
}
.good-search-head .voice-icon {
left: 0;
}
.good-search-head .search-icon {
width: 63rpx;
right: 97rpx;
}
.good-search-head .search-icon2 {
font-size: 28rpx;
right: 0;
width: 73rpx;
padding-right: 24rpx;
}
.good-search-head .clearvalue-icon {
width: auto;
right: 146rpx;
color: #ccc;
}
.good-search-head .autocomplay {
left: 0;
top: 80rpx;
border-radius: 20rpx;
height: 70vh;
background: #f7f7f7;
position: absolute;
z-index: 9;
}
.good-search-head .autocomplay .autocomplay-item {
padding: 0 24rpx;
line-height: 70rpx;
height: 70rpx;
border-bottom: 1px #eee solid;
color: #999;
}
.good-search-body {
display: flex;
flex-direction: column;
}
.good-search-page .search-history {
display: flex;
flex-direction: column;
}
.good-search-page .search-showhot {
display: flex;
flex-direction: column;
}
/* 主题1 */
.good-search-rect {}
.good-search-rect .good-search-head input {
border-radius: 0;
}
.good-search-rect .good-search-head input.clearvalue{
}
.good-search-rect .good-search-head .voice-icon {}
.good-search-rect .good-search-head .search-icon {}
.good-search-rect .good-search-head .clearvalue-icon {}
.good-search-rect .header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 24rpx 0;
padding: 0 12rpx;
}
.good-search-rect .header text {
color: #666;
font-weight: bold;
font-size: 32rpx;
margin-right: auto;
}
.good-search-rect .list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.good-search-rect .list view {
display: block;
width: 49%;
color: #8A8A8A;
font-size: 28rpx;
box-sizing: border-box;
text-align: center;
padding: 20rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
background-color: #F7F7F7;
margin: 0.5%;
}
.good-search-rect .search-showhot {}
/* 主题2 */
.good-search-circle {}
.good-search-circle .good-search-head input {
border-radius: 50rpx;
}
.good-search-circle .good-search-head input.clearvalue{
}
.good-search-circle .good-search-head .voice-icon {}
.good-search-circle .good-search-head .search-icon {}
.good-search-circle .good-search-head .clearvalue-icon {}
.good-search-circle .header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 24rpx 0;
padding: 0 12rpx;
}
.good-search-circle .header text {
color: #666;
font-weight: bold;
font-size: 32rpx;
margin-right: auto;
}
.good-search-circle .list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.good-search-circle .list view {
display: block;
padding: 8rpx 18rpx;
margin: 12rpx;
margin-top: 0;
font-size: 28rpx;
color: #8A8A8A;
background-color: #F7F7F7;
box-sizing: border-box;
text-align: center;
border-radius: 20rpx;
}
.good-search-circle .search-showhot {}
/* 主题3 */
.good-search-tb {}
.good-search-tb .good-search-head input {
border-radius: 0;
}
.good-search-tb .good-search-head input.clearvalue{
padding-right: 133rpx;
}
.good-search-tb .good-search-head .voice-icon {}
.good-search-tb .good-search-head .search-icon {
background: #FF9A33;
color: #fff;
}
.good-search-tb .good-search-head .clearvalue-icon {
width: 60rpx;
}
.good-search-tb .header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 24rpx 0;
padding: 0 12rpx;
}
.good-search-tb .header text {
color: #666;
font-weight: bold;
font-size: 32rpx;
margin-right: auto;
}
.good-search-tb .list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.good-search-tb .list view {
display: block;
padding: 8rpx 30rpx;
margin: 12rpx;
margin-top: 0;
font-size: 28rpx;
color: #8A8A8A;
background-color: #F7F7F7;
box-sizing: border-box;
text-align: center;
border-radius: 20rpx;
}
.good-search-tb .search-showhot {}
</style>