first commit

This commit is contained in:
2023-09-07 00:56:03 +08:00
commit c0ca154d31
718 changed files with 56107 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
<template>
<image :src="src ? src : '/static/images/userpic.png'" mode="widthFix" :style="getStyle" :class="type" @click="clickEvent"></image>
</template>
<script>
export default {
props: {
size:{
type:[String,Number],
default:90
},
src: {
type: String,
default: ""
},
type:{
type:String,
default:"rounded"
},
clickType:{
type:String,
default:"none"
}
},
computed: {
getStyle() {
return `width: ${this.size}rpx;height: ${this.size}rpx;`
}
},
methods: {
clickEvent() {
switch (this.clickType){
case 'navigate':
// console.log(this.clickType)
uni.navigateTo({
url: '/pages/mail/user-base/user-base'
});
break;
default:
this.$emit('click')
break;
}
}
},
}
</script>
<style>
</style>

View File

@@ -0,0 +1,28 @@
<template>
<text class="badge bg-danger text-white rounded-circle font-sm" :class="badgeClass" :style="badgeStyle">{{value}}</text>
</template>
<script>
export default {
props: {
badgeClass: {
type: String,
default: ""
},
badgeStyle:{
type:String,
default:""
},
value:{
type:[Number,String],
default:""
}
},
}
</script>
<style scoped>
.badge{
padding-left: 14rpx;padding-right: 14rpx;padding-bottom: 3rpx;padding-top: 3rpx;
}
</style>

View File

@@ -0,0 +1,159 @@
<template>
<div class="" @longpress="long">
<view v-if="showTime" class="flex align-center justify-center pb-4 pt-2">
<text class="font-sm text-light-muted">{{showTime}}</text>
</view>
<view v-if="item.isRemove" ref="isRemove" class="flex align-center justify-center pb-4 pt-1 chat-animate">
<text class="font-sm text-light-muted">你撤回了一条消息</text>
</view>
<!-- 气泡 -->
<view v-else class="flex align-start position-relative mb-3"
:class="!isself ? 'justify-start' : 'justify-end'">
<!-- 好友 -->
<template v-if="!isself">
<avater size="70" :src="item.avatar" clickType="navigate"></avater>
<text v-if="hasLabelClass" class="iconfont text-white font-md position-absolute chat-left-icon"
style="">&#xe609;</text>
</template>
<div style="max-width: 500rpx;" class="p-2 rounded " :class="labelClass" :style="labelStyle">
<!-- 文字 -->
<text v-if="item.type === 'text'" class="font-md ">{{item.data}}</text>
</div>
<!-- 本人 -->
<template v-if="isself">
<text v-if="hasLabelClass" class="iconfont text-chat-item font-md position-absolute chat-right-icon"
style="">&#xe640;</text>
<avater size="70" :src="item.avatar" clickType="navigate"></avater>
</template>
</view>
</div>
</template>
<script>
import avater from "@/components/ui/avater.vue"
import fImage from "@/components/ui/fImage.vue"
import $T from "@/common/lib/time.js"
import {
mapState,
mapActions
} from 'vuex'
// #ifdef APP-PLUS-NVUE
const animation = weex.requireModule('animation')
// #endif
export default {
components: {
avater,
fImage
},
props: {
item: Object,
index: Number,
pretime: [Number, String]
},
computed: {
// 是否为本人
isself() {
var lim = this.imsdk
var sdk = lim.im;
let uid = sdk.getUserId();
// console.log(uid)
return this.item.userId === uid;
},
// 显示的时间
showTime() {
return $T.getChatTime(this.item.createTime, this.pretime)
},
// 是否需要气泡样式
hasLabelClass() {
return this.item.type === 'text'
},
// 气泡的样式
labelClass() {
let label = this.hasLabelClass ? 'bg-chat-item mr-3' : 'mr-3'
return this.isself ? label : 'bg-white ml-3'
},
labelStyle(){
}
},
methods: {
long(e) {
let x = 0
let y = 0
// #ifdef APP-PLUS-NVUE
if (Array.isArray(e.changedTouches) && e.changedTouches.length > 0) {
x = e.changedTouches[0].screenX
y = e.changedTouches[0].screenY
}
// #endif
// #ifdef H5
x = e.changedTouches[0].pageX
y = e.changedTouches[0].pageY
// #endif
// #ifdef MP
x = e.detail.x
y = e.detail.y
// #endif
this.$emit('long', {
x,
y,
index: this.index
})
},
preview(url) {
this.$emit("preview", url)
},
loadImage(e) {
let w = e.detail.width
let h = e.detail.height
// 按照宽度来
let maxW = uni.upx2px(500)
//按照高度来
let maxH = uni.upx2px(350)
if (h <= maxH) {
this.w = w <= maxW ? w : maxW;
this.h = h;
return;
}
this.h = maxH
let w2 = maxH * (w / h)
this.w = w2 <= maxW ? w2 : maxW
}
},
destroyed() {
},
data() {
return {
w: 100,
h: 100
}
},
mounted() {
// 注册全局事件
}
}
</script>
<style scoped>
.chat-left-icon {
left: 80rpx;
}
.chat-right-icon {
right: 80rpx;
}
.chat-animate {
/* #ifndef APP-PLUS-NVUE */
opacity: 0;
/* #endif */
}
</style>

View File

@@ -0,0 +1,62 @@
<template>
<popup ref="confirm" center maskColor transformOrigin="center center">
<view class="bg-white rounded" style="width: 600rpx;">
<view class="p-4 flex flex-column">
<text class="font-md font-weight-bold mb-3">{{title}}</text>
<slot></slot>
</view>
<!-- 底部 -->
<view style="height: 100rpx;" class="border-top flex align-stretch">
<view class="flex-1 border-right flex align-center justify-center" @click="cancel">
<text class="font-md text-muted">取消</text>
</view>
<view class="flex-1 flex align-center justify-center"
@click="confirm">
<text class="font-md main-text-color">确定</text>
</view>
</view>
</view>
</popup>
</template>
<script>
import popup from '@/components/ui/popup.vue';
export default {
components: {
popup
},
props: {
title: {
type: String,
default: "提示"
},
},
data() {
return {
callback: false
}
},
methods: {
// 显示
show(callback = false){
this.callback = callback
this.$refs.confirm.show()
},
// 取消
cancel() {
this.$refs.confirm.hide()
},
// 确定
confirm(){
if(typeof this.callback === 'function'){
this.callback(()=>{
this.cancel()
})
}
}
},
}
</script>
<style>
</style>

View File

@@ -0,0 +1,26 @@
<template>
<view :style="getStyle"></view>
</template>
<script>
export default {
props: {
color:{
type:String,
default:"#EFEDE9"
}
},
computed: {
getStyle(){
return `height: 18rpx;background-color: ${this.color}rpx`
}
},
methods: {
},
}
</script>
<style>
</style>

View File

@@ -0,0 +1,69 @@
<template>
<image :src="src" lazy-load :style="imageStyle" :class="imageClass" @tap="$emit('click')"
@longpress="$emit('longpress')" @load="loadImage" class="bg-hover-light"></image>
</template>
<script>
export default {
props: {
src: {
type: String,
default: ""
},
imageClass: {
type: String,
default: ""
},
maxWidth: {
type: Number,
default: 500 // rpx
},
maxHeight: {
type: Number,
default: 350 // rpx
}
},
data() {
return {
h: 100,
w: 100
}
},
computed: {
imageStyle() {
return `width:${this.w}px;height:${this.h}px;`
}
},
methods: {
// 加载图片
loadImage(e) {
let w = e.detail.width
let h = e.detail.height
// // 最大宽度 px
let maxW = uni.upx2px(this.maxWidth)
// 最大高度
let maxH = uni.upx2px(this.maxHeight)
if (h <= maxH) {
// 用原来的宽高
this.w = w <= maxW ? w : maxW
this.h = h
this.$emit('load', {
w: this.w,
h: this.h
})
return;
}
this.h = maxH
let w2 = maxH * (w / h)
this.w = w2 <= maxW ? w2 : maxW
this.$emit('load', {
w: this.w,
h: this.h
})
},
},
}
</script>
<style>
</style>

View File

@@ -0,0 +1,21 @@
<template>
<view class="flex align-center justify-center"
hover-class="bg-hover-light" @click="$emit('click')"
style="height: 90rpx;width: 90rpx;">
<text class="iconfont font-md">{{icon}}</text>
</view>
</template>
<script>
export default {
props: {
icon: {
type: String,
default: ''
},
},
}
</script>
<style>
</style>

View File

@@ -0,0 +1,71 @@
<template>
<view class="bg-white flex align-stretch" hover-class="bg-light"
@click="$emit('click')">
<view class="flex align-center justify-center py-2 pl-3"
v-if="showLeftIcon">
<slot name="icon"></slot>
<image :src="cover" v-if="cover"
mode="widthFix" :style="coverStyle"></image>
</view>
<view class="flex-1 flex align-center justify-between pr-3 py-3 pl-3" :class="border ? 'border-bottom' : ''">
<slot>
<text class="font-md text-dark">{{title}}</text>
</slot>
<view class="flex align-center" v-if="showRight">
<slot name="right"></slot>
<!-- 右箭头 -->
<text v-if="showRightIcon" class="iconfont text-light-muted font-md">&#xe60c;</text>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
// 是否开启下边线
border:{
type:Boolean,
default:true
},
// 封面
cover: {
type: String,
default: ""
},
// 封面大小
coverSize:{
type: [String,Number],
default:75
},
// 标题
title:{
type:String,
default:""
},
// 显示右边
showRight:{
type:Boolean,
default:false
},
// 显示左边图标
showLeftIcon:{
type:Boolean,
default:true
},
// 是否显示箭头
showRightIcon:{
type:Boolean,
default:true
}
},
computed: {
coverStyle() {
return `width: ${this.coverSize}rpx;height: ${this.coverSize}rpx;`
}
},
}
</script>
<style>
</style>

View File

@@ -0,0 +1,34 @@
<template>
<view class="rounded mr-2 px-2 py-1"
@click="clickEvent"
:class="disabled ? 'bg-light border' : 'main-bg-color'">
<text class="font"
:class="disabled ? 'text-light-muted':'text-white'">{{name}}</text>
</view>
</template>
<script>
export default {
props: {
name: {
type: String,
default: ""
},
disabled:{
type:Boolean,
default:false
}
},
methods: {
clickEvent() {
console.log("clickEvent")
if(!this.disabled){
this.$emit('click')
}
}
},
}
</script>
<style>
</style>

View File

@@ -0,0 +1,76 @@
<template>
<view :class="item.istop ? 'bg-light' : 'bg-white'" hover-class="bg-hover-light">
<div class="flex align-stretch" @click="onClick" @longpress="long">
<view class="flex align-center justify-center position-relative" style="width: 145rpx;">
<avater :src="item.avatar" size="92"></avater>
<badge badgeClass="position-absolute" badgeStyle="top:15rpx;right:15rpx" v-if="item.noreadnum"
:value="item.noreadnum"></badge>
</view>
<view class="flex flex-column border-bottom flex-1 py-3 pr-3 border-light-secondary">
<view class="flex align-center justify-between mb-1">
<text class="font-md">{{item.nickname}}</text>
<text class="font-sm text-light-muted">{{item.updateTime|formatTime}}</text>
</view>
<text class="font-sm text-ellipsis text-light-muted">{{item.data}}</text>
</view>
</div>
</view>
</template>
<script>
import base from "@/common/mixin/base.js"
import avater from "@/components/ui/avater.vue"
import badge from "@/components/ui/badge.vue"
export default {
mixins:[base],
components: {
avater,
badge
},
props: {
item: Object,
index:Number
},
methods:{
onClick(){
uni.navigateTo({
url: '/pages/chat/chat/chat?params='+encodeURIComponent(JSON.stringify({
id:this.item.id
}))
});
},
long(e){
let x = 0
let y = 0
// #ifdef APP-PLUS-NVUE
if (Array.isArray(e.changedTouches) && e.changedTouches.length > 0) {
x = e.changedTouches[0].screenX
y = e.changedTouches[0].screenY
}
// #endif
// #ifdef H5
x = e.changedTouches[0].pageX
y = e.changedTouches[0].pageY
// #endif
// #ifdef MP
x = e.detail.x
y = e.detail.y
// #endif
console.log(this.index)
this.$emit('long',{
x,
y,
index:this.index
})
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,182 @@
<template>
<view>
<view :class="getClass">
<!-- 状态栏 -->
<view :style="'height:'+statusBarHeight+'px'"></view>
<!-- 导航 -->
<view class="w-100 flex align-center justify-between" style="height: 90rpx;">
<!-- 左边 -->
<view class="flex align-center">
<!-- 返回按钮 -->
<!-- #ifndef MP -->
<icon-button v-if="showBack" @click="back"
:icon="'\ue60d'"></icon-button>
<!-- #endif -->
<!-- 标题 -->
<slot>
<text v-if="title" class="font-md ml-3">{{getTitle}}</text>
</slot>
</view>
<!-- 右边 -->
<view class="flex align-center" v-if="showRight">
<slot name="right">
<icon-button @click="search" :icon="'\ue6e3'"></icon-button>
<icon-button @click="openExtend" :icon="'\ue682'"></icon-button>
</slot>
</view>
</view>
</view>
<!-- 占位 -->
<view v-if="fixed" :style="fixedStyle"></view>
<!-- 扩展菜单 -->
<popup v-if="showRight" ref="extend" :bodyWidth="320" :bodyHeight="525"
bodyBgColor="bg-dark" transformOrigin="right top">
<view class="flex flex-column"
style="width: 320rpx;height: 525rpx;">
<view class="flex-1 flex align-center"
hover-class="bg-hover-dark"
v-for="(item,index) in menus"
:key="index"
@click="clickEvent(item)">
<text class="iconfont pl-3 pr-2 font-md text-white">{{item.icon}}</text>
<text class="font-md text-white">{{item.name}}</text>
</view>
</view>
</popup>
</view>
</template>
<script>
import iconButton from "./icon-button.vue"
import popup from "./popup.vue"
import $H from '@/common/lib/request.js';
export default {
props: {
showBack:{
type:Boolean,
default:false
},
backEvent:{
type:Boolean,
default:true
},
title: {
type: [String,Boolean],
default:false
},
fixed:{
type:Boolean,
default:true
},
noreadnum:{
type:[Number,String],
default:0
},
bgColor:{
type:String,
default:"bg-light"
},
showRight:{
type:Boolean,
default:true
}
},
components:{
iconButton,
popup
},
data() {
return {
statusBarHeight:0,
navBarHeight:0,
menus:[
{
name:"发起群聊",
event:"navigateTo",
path:'',
icon:"\ue633"
},
{
name:"添加好友",
event:"navigateTo",
path:'',
icon:"\ue65d"
},
{
name:"扫一扫",
event:"scan",
icon:"\ue614"
},
{
name:"收付款",
event:"",
icon:"\ue66c"
},
{
name:"帮助与反馈",
event:"",
icon:"\ue66c"
}
],
}
},
mounted() {
// #ifdef APP-PLUS-NVUE
this.statusBarHeight = plus.navigator.getStatusbarHeight()
// #endif
this.navBarHeight = this.statusBarHeight + uni.upx2px(90)
},
computed: {
fixedStyle() {
return `height:${this.navBarHeight}px`
},
getTitle(){
let noreadnum = this.noreadnum > 0 ? '('+this.noreadnum+')' : ''
return this.title + noreadnum
},
getClass(){
let fixed = this.fixed?'fixed-top':''
return `${fixed} ${this.bgColor}`
}
},
methods: {
openExtend() {
this.$refs.extend.show(uni.upx2px(415),uni.upx2px(150))
},
// 返回
back(){
if(this.backEvent){
return uni.navigateBack({
delta: 1
});
}
this.$emit('back')
},
search(){
console.log("search")
},
clickEvent(e){
this.$refs.extend.hide()
switch (e.event){
case 'navigateTo':
uni.navigateTo({
url: e.path,
});
break;
default:
uni.showToast({
title: '自己发挥',
icon: 'none'
});
break;
}
}
},
}
</script>
<style>
</style>

View File

@@ -0,0 +1,157 @@
<template>
<div style="z-index:9999;overflow:hidden;" v-if="status">
<!-- 蒙版 -->
<view v-if="mask" class="position-fixed top-0 left-0 right-0 bottom-0 z-index" :style="getMaskColor" @click="hide"></view>
<!-- 弹出框内容 -->
<div ref="popup" class="position-fixed animated z-index" :class="getBodyClass" :style="getBodyStyle">
<slot></slot>
</div>
</div>
</template>
<script>
// #ifdef APP-PLUS-NVUE
const animation = weex.requireModule('animation')
// #endif
export default {
props: {
// 是否开启蒙版颜色
maskColor: {
type: Boolean,
default: false
},
// 是否开启蒙版
mask:{
type:Boolean,
default:true
},
// 是否居中
center:{
type:Boolean,
default:false
},
// 是否处于底部
bottom:{
type:Boolean,
default:false
},
// 弹出层内容宽度
bodyWidth:{
type:Number,
default:0
},
// 弹出层内容高度
bodyHeight:{
type:Number,
default:0
},
bodyBgColor:{
type:String,
default:"bg-white"
},
transformOrigin:{
type:String,
default:"left top"
},
// tabbar高度
tabbarHeight:{
type:Number,
default:0
}
},
data() {
return {
status: false,
x:-1,
y:1,
maxX:0,
maxY:0
}
},
mounted() {
try {
const res = uni.getSystemInfoSync();
this.maxX = res.windowWidth - uni.upx2px(this.bodyWidth)
this.maxY = res.windowHeight - uni.upx2px(this.bodyHeight) - uni.upx2px(this.tabbarHeight)
} catch (e) {
// error
}
},
computed: {
getMaskColor() {
let i = this.maskColor ? 0.5 : 0
return `background-color: rgba(0,0,0,${i});`
},
getBodyClass(){
if(this.center){
return 'left-0 right-0 bottom-0 top-0 flex align-center justify-center'
}
let bottom = this.bottom ? 'left-0 right-0 bottom-0' : 'rounded border'
return `${this.bodyBgColor} ${bottom}`
},
getBodyStyle(){
let left = this.x > -1 ? `left:${this.x}px;` : ''
let top = this.y > -1 ? `top:${this.y}px;` : ''
return left + top
}
},
methods:{
show(x = -1 ,y = -1){
if (this.status) {
return;
}
this.x = (x > this.maxX) ? this.maxX : x
this.y = (y > this.maxY) ? this.maxY : y
this.status = true
// #ifdef APP-PLUS-NVUE
this.$nextTick(()=>{
animation.transition(this.$refs.popup, {
styles: {
transform: 'scale(1,1)',
transformOrigin:this.transformOrigin,
opacity:1
},
duration: 100, //ms
timingFunction: 'ease',
}, function () {
// console.log('动画执行结束');
})
})
// #endif
},
hide(){
this.$emit('hide')
// #ifdef APP-PLUS-NVUE
animation.transition(this.$refs.popup, {
styles: {
transform: 'scale(0,0)',
transformOrigin:this.transformOrigin,
opacity:0
},
duration: 100, //ms
timingFunction: 'ease',
}, ()=> {
this.status = false
// console.log('动画执行结束');
})
// #endif
this.status = false
}
}
}
</script>
<style scoped>
.animated{
/* #ifdef APP-PLUS-NVUE */
/* transform: scale(0,0);
opacity: 0; */
/* #endif */
}
.z-index{
/* #ifndef APP-NVUE */
z-index: 9999;
/* #endif */
}
</style>