diff --git a/README.md b/README.md index 743372c..c35961d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ 1. 支持单人、多人音视频通话(基于原生webrtc实现,需要ssl证书) 1. uniapp端兼容app、h5、微信小程序,可与web端同时在线,并保持消息同步 1. 后端采用springboot+netty实现,网页端使用vue,移动端使用uniapp -1. 服务器支持集群化部署,每个im-server仅处理自身连接用户的消息 +1. 服务器支持集群化部署,具有良好的横向扩展能力 详细文档:https://www.yuque.com/u1475064/mufu2a @@ -21,7 +21,7 @@ - 后台管理端上线,后台管理代码仓库地址:https://gitee.com/bluexsx/box-im-admin - 框架和组件版本全面升级: jdk17、springboot3.3、node18等 -- 部分界面,功能、性能优化 +- 部分界面,功能、性能优化1 #### 在线体验 @@ -60,9 +60,9 @@ https://www.yuque.com/u1475064/imk5n2/qtezcg32q1d0dr29#SbvXq | im-uniapp | uniapp页面,可打包成app、h5、微信小程序 | #### 消息推送方案 +当消息的发送者和接收者连的不是同一个server时,消息是无法直接推送的,所以我们设计出了能够支持跨节点推送的方案: ![输入图片说明](%E6%88%AA%E5%9B%BE/%E6%B6%88%E6%81%AF%E6%8E%A8%E9%80%81%E9%9B%86%E7%BE%A4%E5%8C%96.jpg) -- 当消息的发送者和接收者连的不是同一个server时,消息是无法直接推送的,所以我们需要设计出能够支持跨节点推送的方案 - 利用了redis的list数据实现消息推送,其中key为im:unread:${serverid},每个key的数据可以看做一个queue,每个im-server根据自身的id只消费属于自己的queue - redis记录了每个用户的websocket连接的是哪个im-server,当用户发送消息时,im-platform将根据所连接的im-server的id,决定将消息推向哪个queue @@ -102,24 +102,25 @@ https://www.yuque.com/u1475064/mufu2a/vn5u10ephxh9sau8 #### 界面截图 私聊: -![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E7%A7%81%E8%81%8A.png) +![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E7%A7%81%E8%81%8A.jpg) 群聊: -![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E7%BE%A4%E8%81%8A.png) +![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E7%BE%A4%E8%81%8A.jpg) 群通话: -![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E7%BE%A4%E8%A7%86%E9%A2%91.png) +![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E5%A4%9A%E4%BA%BA%E9%80%9A%E8%AF%9D.jpg) 好友列表: -![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E5%A5%BD%E5%8F%8B%E5%88%97%E8%A1%A8.png) +![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E5%A5%BD%E5%8F%8B.jpg) 群列表: -![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E7%BE%A4%E5%88%97%E8%A1%A8.png) +![输入图片说明](%E6%88%AA%E5%9B%BE/web/%E7%BE%A4%E5%88%97%E8%A1%A8.jpg) 移动端APP: -![输入图片说明](%E6%88%AA%E5%9B%BE/app/1.jpg) +![输入图片说明](%E6%88%AA%E5%9B%BE/app/1.png) + -![输入图片说明](%E6%88%AA%E5%9B%BE/app/2.jpg) +![输入图片说明](%E6%88%AA%E5%9B%BE/app/2.png) #### 加入交流群 群1: 741174521(已满) diff --git a/im-common/src/main/java/com/bx/imcommon/enums/IMCmdType.java b/im-common/src/main/java/com/bx/imcommon/enums/IMCmdType.java index a6731ec..74793ec 100644 --- a/im-common/src/main/java/com/bx/imcommon/enums/IMCmdType.java +++ b/im-common/src/main/java/com/bx/imcommon/enums/IMCmdType.java @@ -6,9 +6,9 @@ import lombok.AllArgsConstructor; public enum IMCmdType { /** - * 登陆 + * 登录 */ - LOGIN(0, "登陆"), + LOGIN(0, "登录"), /** * 心跳 */ diff --git a/im-platform/src/main/java/com/bx/implatform/annotation/RepeatSubmit.java b/im-platform/src/main/java/com/bx/implatform/annotation/RepeatSubmit.java new file mode 100644 index 0000000..7bd6704 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/annotation/RepeatSubmit.java @@ -0,0 +1,31 @@ +package com.bx.implatform.annotation; + +import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; + +/** + * 防止表单重复提交注解 + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit { + + + /** + * 间隔时间,小于此时间视为重复提交 + */ + int interval() default 5000; + + /** + * 间隔时间单位 + */ + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; + + /** + * 提示消息 + */ + String message() default "请勿提交重复请求"; + +} diff --git a/im-platform/src/main/java/com/bx/implatform/aspect/RepeatSubmitAspect.java b/im-platform/src/main/java/com/bx/implatform/aspect/RepeatSubmitAspect.java new file mode 100644 index 0000000..cdd566f --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/aspect/RepeatSubmitAspect.java @@ -0,0 +1,100 @@ +package com.bx.implatform.aspect; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import com.alibaba.fastjson.JSON; +import com.bx.implatform.annotation.RepeatSubmit; +import com.bx.implatform.contant.RedisKey; +import com.bx.implatform.exception.GlobalException; +import com.bx.implatform.session.SessionContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Collection; +import java.util.Map; +import java.util.StringJoiner; + +/** + * @author: blue + * @date: 2024-12-08 + * @version: 1.0 + */ + +@Aspect +@Component +@AllArgsConstructor +public class RepeatSubmitAspect { + + private final RedisTemplate redisTemplate; + + @Before("@annotation(repeatSubmit)") + public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable { + // 如果注解不为0 则使用注解数值 + long interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval()); + HttpServletRequest request = + ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); + String url = request.getRequestURL().toString(); + Long userId = SessionContext.getSession().getUserId(); + String reqParams = argsArrayToString(point.getArgs()); + String md5 = SecureUtil.md5(StrUtil.join(":", userId, url, reqParams)); + // 唯一标识 + String key = String.join(":",RedisKey.IM_REPEAT_SUBMIT,md5) ; + if(redisTemplate.hasKey(key)){ + throw new GlobalException(repeatSubmit.message()); + } + redisTemplate.opsForValue().set(key,1,repeatSubmit.interval(),repeatSubmit.timeUnit()); + } + + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) { + StringJoiner params = new StringJoiner(" "); + if (ArrayUtil.isEmpty(paramsArray)) { + return params.toString(); + } + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + params.add(JSON.toJSONString(o)); + } + } + return params.toString(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection)o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map)o; + for (Object value : map.values()) { + return value instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse; + } +} diff --git a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java index 17ddd9f..3162ea4 100644 --- a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java +++ b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java @@ -47,9 +47,10 @@ public final class RedisKey { * 缓存群聊成员id */ public static final String IM_CACHE_GROUP_MEMBER_ID = "im:cache:group_member_ids"; + /** - * 分布式锁前缀 + * 重复提交 */ - public static final String IM_LOCK_RTC_GROUP = "im:lock:rtc:group"; + public static final String IM_REPEAT_SUBMIT = "im:repeat:submit"; } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java index e66be74..c16f692 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java @@ -1,5 +1,6 @@ package com.bx.implatform.controller; +import com.bx.implatform.annotation.RepeatSubmit; import com.bx.implatform.entity.Friend; import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; @@ -39,6 +40,7 @@ public class FriendController { } + @RepeatSubmit @PostMapping("/add") @Operation(summary = "添加好友", description = "双方建立好友关系") public Result addFriend(@NotNull(message = "好友id不可为空") @RequestParam Long friendId) { diff --git a/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java b/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java index 7f29c23..dc0557b 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java @@ -1,5 +1,6 @@ package com.bx.implatform.controller; +import com.bx.implatform.annotation.RepeatSubmit; import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.GroupService; @@ -23,6 +24,7 @@ public class GroupController { private final GroupService groupService; + @RepeatSubmit @Operation(summary = "创建群聊", description = "创建群聊") @PostMapping("/create") public Result createGroup(@Valid @RequestBody GroupVO vo) { diff --git a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java index e889e40..0f6e242 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java @@ -1,5 +1,6 @@ package com.bx.implatform.controller; +import com.bx.implatform.annotation.RepeatSubmit; import com.bx.implatform.dto.LoginDTO; import com.bx.implatform.dto.ModifyPwdDTO; import com.bx.implatform.dto.RegisterDTO; @@ -13,7 +14,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -@Tag(name = "注册登陆") +@Tag(name = "注册登录") @RestController @RequiredArgsConstructor public class LoginController { @@ -21,7 +22,7 @@ public class LoginController { private final UserService userService; @PostMapping("/login") - @Operation(summary = "用户登陆", description = "用户登陆") + @Operation(summary = "用户登录", description = "用户登录") public Result login(@Valid @RequestBody LoginDTO dto) { LoginVO vo = userService.login(dto); return ResultUtils.success(vo); diff --git a/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java b/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java index 7b8d308..8cc0bf1 100644 --- a/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java +++ b/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java @@ -32,7 +32,7 @@ public class AuthInterceptor implements HandlerInterceptor { //从 http 请求头中取出 token String token = request.getHeader("accessToken"); if (StrUtil.isEmpty(token)) { - log.error("未登陆,url:{}", request.getRequestURI()); + log.error("未登录,url:{}", request.getRequestURI()); throw new GlobalException(ResultCode.NO_LOGIN); } String strJson = JwtUtil.getInfo(token); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java index 72fdc7b..bf42251 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java @@ -81,7 +81,7 @@ public class UserServiceImpl extends ServiceImpl implements Us public LoginVO refreshToken(String refreshToken) { //验证 token if (!JwtUtil.checkSign(refreshToken, jwtProperties.getRefreshTokenSecret())) { - throw new GlobalException("您的登陆信息已过期,请重新登陆"); + throw new GlobalException("您的登录信息已过期,请重新登录"); } String strJson = JwtUtil.getInfo(refreshToken); Long userId = JwtUtil.getUserId(refreshToken); diff --git a/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java b/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java index ae8345b..f54b17d 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java @@ -15,12 +15,12 @@ public class UserVO { private Long id; @NotEmpty(message = "用户名不能为空") - @Length(max = 64, message = "用户名不能大于64字符") + @Length(max = 20, message = "用户名不能大于20字符") @Schema(description = "用户名") private String userName; @NotEmpty(message = "用户昵称不能为空") - @Length(max = 64, message = "昵称不能大于64字符") + @Length(max = 20, message = "昵称不能大于20字符") @Schema(description = "用户昵称") private String nickName; @@ -30,7 +30,7 @@ public class UserVO { @Schema(description = "用户类型 1:普通用户 2:审核账户") private Integer type; - @Length(max = 1024, message = "个性签名不能大于1024个字符") + @Length(max = 128, message = "个性签名不能大于128个字符") @Schema(description = "个性签名") private String signature; diff --git a/im-server/src/main/java/com/bx/imserver/netty/processor/LoginProcessor.java b/im-server/src/main/java/com/bx/imserver/netty/processor/LoginProcessor.java index c0258d3..6068a45 100644 --- a/im-server/src/main/java/com/bx/imserver/netty/processor/LoginProcessor.java +++ b/im-server/src/main/java/com/bx/imserver/netty/processor/LoginProcessor.java @@ -49,7 +49,7 @@ public class LoginProcessor extends AbstractMessageProcessor { // 不允许多地登录,强制下线 IMSendInfo sendInfo = new IMSendInfo<>(); sendInfo.setCmd(IMCmdType.FORCE_LOGUT.code()); - sendInfo.setData("您已在其他地方登陆,将被强制下线"); + sendInfo.setData("您已在其他地方登录,将被强制下线"); context.channel().writeAndFlush(sendInfo); log.info("异地登录,强制下线,userId:{}", userId); } diff --git a/im-uniapp/App.vue b/im-uniapp/App.vue index f92ea61..9b290f3 100644 --- a/im-uniapp/App.vue +++ b/im-uniapp/App.vue @@ -47,7 +47,7 @@ export default { if (cmd == 2) { // 异地登录,强制下线 uni.showModal({ - content: '您已在其他地方登陆,将被强制下线', + content: '您已在其他地方登录,将被强制下线', showCancel: false, }) this.exit(); @@ -356,26 +356,32 @@ export default { url: '/user/self', method: 'GET' }) + }, + closeSplashscreen(delay) { + // #ifdef APP-PLUS + // 关闭开机动画 + setTimeout(() => { + console.log("plus.navigator.closeSplashscreen()") + plus.navigator.closeSplashscreen() + }, delay) + // #endif } }, onLaunch() { this.$mountStore(); + // 延迟1s,避免用户看到页面跳转 + this.closeSplashscreen(1000); // 登录状态校验 let loginInfo = uni.getStorageSync("loginInfo") this.refreshToken(loginInfo).then(() => { // 初始化 this.init(); - // 跳转到聊天页面 - uni.switchTab({ - url: "/pages/chat/chat" - }) + this.closeSplashscreen(0); }).catch(() => { // 跳转到登录页 - // #ifdef H5 uni.navigateTo({ url: "/pages/login/login" }) - // #endif }) } } diff --git a/im-uniapp/common/emotion.js b/im-uniapp/common/emotion.js index 7e567e3..7e301e0 100644 --- a/im-uniapp/common/emotion.js +++ b/im-uniapp/common/emotion.js @@ -7,29 +7,21 @@ const emoTextList = ['憨笑', '媚眼', '开心', '坏笑', '可怜', '爱心', ]; - -let transform = (content) => { - return content.replace(/\#[\u4E00-\u9FA5]{1,3}\;/gi, textToImg); +let transform = (content, extClass) => { + return content.replace(/\#[\u4E00-\u9FA5]{1,3}\;/gi, (emoText)=>{ + // 将匹配结果替换表情图片 + let word = emoText.replace(/\#|\;/gi, ''); + let idx = emoTextList.indexOf(word); + if (idx == -1) { + return emoText; + } + let path = textToPath(emoText); + let img = ``; + return img; + }); } -// 将匹配结果替换表情图片 -let textToImg = (emoText) => { - let word = emoText.replace(/\#|\;/gi, ''); - let idx = emoTextList.indexOf(word); - if (idx == -1) { - return emoText; - } - let path = textToPath(emoText); - // #ifdef MP - // 微信小程序不能有前面的'/' - path = path.slice(1); - // #endif - let img = ``; - return img; -} - let textToPath = (emoText) => { let word = emoText.replace(/\#|\;/gi, ''); @@ -42,6 +34,5 @@ let textToPath = (emoText) => { export default { emoTextList, transform, - textToImg, textToPath } \ No newline at end of file diff --git a/im-uniapp/common/recorder-app.js b/im-uniapp/common/recorder-app.js index e525ef4..0c8ff83 100644 --- a/im-uniapp/common/recorder-app.js +++ b/im-uniapp/common/recorder-app.js @@ -62,7 +62,6 @@ let upload = () => { export { start, - pause, close, upload } \ No newline at end of file diff --git a/im-uniapp/common/recorder-h5.js b/im-uniapp/common/recorder-h5.js index fdf828d..1425dd3 100644 --- a/im-uniapp/common/recorder-h5.js +++ b/im-uniapp/common/recorder-h5.js @@ -1,60 +1,73 @@ -import Recorder from 'js-audio-recorder'; import UNI_APP from '@/.env.js'; let rc = null; +let duration = 0; +let chunks = []; +let stream = null; let start = () => { - if (rc != null) { - close(); - } - rc = new Recorder(); - return rc.start(); -} + return navigator.mediaDevices.getUserMedia({ audio: true }).then(audioStream => { + const startTime = new Date().getTime(); + chunks = []; + stream = audioStream; + rc = new MediaRecorder(stream) + rc.ondataavailable = (e) => { + console.log("ondataavailable") + chunks.push(e.data) + } + rc.onstop = () => { + duration = (new Date().getTime() - startTime) / 1000; + console.log("时长:", duration) + } + rc.start() + }) -let pause = () => { - rc.pause(); } let close = () => { - rc.destroy(); - rc = null; + stream.getTracks().forEach((track) => { + track.stop() + }) + rc.stop() } + let upload = () => { return new Promise((resolve, reject) => { - const wavBlob = rc.getWAVBlob(); - const newbolb = new Blob([wavBlob], { type: 'audio/wav' }) - const name = new Date().getDate() + '.wav'; - const file = new File([newbolb], name) - uni.uploadFile({ - url: UNI_APP.BASE_URL + '/file/upload', - header: { - accessToken: uni.getStorageSync("loginInfo").accessToken - }, - file: file, - name: 'file', - success: (res) => { - let r = JSON.parse(res.data); - if (r.code != 200) { - console.log(res) - reject(r.message); - } else { - const data = { - duration: parseInt(rc.duration), - url: r.data + setTimeout(() => { + const newbolb = new Blob(chunks, { 'type': 'audio/mpeg' }); + const name = new Date().getDate() + '.mp3'; + const file = new File([newbolb], name) + console.log("upload") + uni.uploadFile({ + url: UNI_APP.BASE_URL + '/file/upload', + header: { + accessToken: uni.getStorageSync("loginInfo").accessToken + }, + file: file, + name: 'file', + success: (res) => { + let r = JSON.parse(res.data); + if (r.code != 200) { + console.log(res) + reject(r.message); + } else { + const data = { + duration: parseInt(duration), + url: r.data + } + resolve(data); } - resolve(data); + }, + fail: (e) => { + reject(e); } - }, - fail: (e) => { - reject(e); - } - }) + }) + }, 100) }) } export { start, - pause, close, upload } \ No newline at end of file diff --git a/im-uniapp/components/chat-item/chat-item.vue b/im-uniapp/components/chat-item/chat-item.vue index 47131c8..ed985f6 100644 --- a/im-uniapp/components/chat-item/chat-item.vue +++ b/im-uniapp/components/chat-item/chat-item.vue @@ -16,7 +16,7 @@ {{ atText }} {{ chat.sendNickName + ': ' }} - + diff --git a/im-uniapp/components/chat-message-item/chat-message-item.vue b/im-uniapp/components/chat-message-item/chat-message-item.vue index 1c2fd88..71ab643 100644 --- a/im-uniapp/components/chat-message-item/chat-message-item.vue +++ b/im-uniapp/components/chat-message-item/chat-message-item.vue @@ -17,13 +17,13 @@ - + - @@ -256,6 +256,7 @@ export default { color: $im-text-color-lighter; font-size: $im-font-size-smaller; line-height: $im-font-size-smaller; + height: $im-font-size-smaller; } .chat-msg-bottom { @@ -305,6 +306,7 @@ export default { .send-image { min-width: 200rpx; max-width: 420rpx; + height: 350rpx; cursor: pointer; border-radius: 4px; } diff --git a/im-uniapp/components/chat-record/chat-record.vue b/im-uniapp/components/chat-record/chat-record.vue index 659d95f..bde3454 100644 --- a/im-uniapp/components/chat-record/chat-record.vue +++ b/im-uniapp/components/chat-record/chat-record.vue @@ -71,13 +71,12 @@ export default { }, onEndRecord() { this.recording = false; - // 停止录音 - this.$rc.pause(); // 停止计时 this.StopTimer(); + // 停止录音 + this.$rc.close(); // 触屏位置是否移动到了取消区域 if (this.moveToCancel) { - this.$rc.close(); console.log("录音取消") return; } @@ -87,7 +86,6 @@ export default { title: "说话时间太短", icon: 'none' }) - this.$rc.close(); return; } this.$rc.upload().then((data) => { diff --git a/im-uniapp/components/file-upload/file-upload.vue b/im-uniapp/components/file-upload/file-upload.vue index f204243..c364ffd 100644 --- a/im-uniapp/components/file-upload/file-upload.vue +++ b/im-uniapp/components/file-upload/file-upload.vue @@ -43,6 +43,12 @@ export default { } }, methods: { + show() { + this.$refs.lsjUpload.show(); + }, + hide() { + this.$refs.lsjUpload.hide(); + }, onUploadEnd(item) { let file = this.fileMap.get(item.path); if (item.type == 'fail') { diff --git a/im-uniapp/im.scss b/im-uniapp/im.scss index 1f2856c..f6d4dd6 100644 --- a/im-uniapp/im.scss +++ b/im-uniapp/im.scss @@ -28,6 +28,39 @@ uni-button[size='mini'] { font-size: $im-font-size-smaller !important; } +// #ifdef MP-WEIXIN +// wx小程序只有button,没有uni-botton +button { + font-size: $im-font-size !important; +} + +button[type='primary'] { + color: #fff !important; + background-color: $im-color-primary !important; +} + +button[type='primary'][plain] { + color: $im-color-primary !important; + border: 1px solid $im-color-primary; + background-color: transparent; +} + +button[type='warn'] { + color: #fff !important; + background-color: $im-color-danger !important; +} + +button[type='warn'][plain] { + color: $im-color-danger !important; + border: 1px solid $im-color-danger !important; + background-color: transparent !important; +} + +button[size='mini'] { + font-size: $im-font-size-smaller !important; +} +// #endif + .button-hover[type='primary'] { color: #fff !important; background-color: $im-color-primary-dark-1 !important; @@ -138,4 +171,22 @@ uni-button[size='mini'] { uni-button + uni-button { margin-top: 20rpx; } +} + +.emoji-large { + width: 64rpx; + height: 64rpx; + vertical-align: bottom; +} + +.emoji-normal { + width: 54rpx; + height: 54rpx; + vertical-align: bottom; +} + +.emoji-small { + width: 36rpx; + height: 36rpx; + vertical-align: bottom; } \ No newline at end of file diff --git a/im-uniapp/manifest.json b/im-uniapp/manifest.json index eb44f57..cfbc8be 100644 --- a/im-uniapp/manifest.json +++ b/im-uniapp/manifest.json @@ -1,32 +1,35 @@ { - "name": "盒子IM", - "appid": "__UNI__69DD57A", - "description": "", - "versionName": "3.1.0", - "versionCode": 3100, - "transformPx": false, + "name" : "盒子IM", + "appid" : "__UNI__69DD57A", + "description" : "", + "versionName" : "3.1.0", + "versionCode" : 3100, + "transformPx" : false, /* 5+App特有相关 */ - "app-plus": { - "usingComponents": true, - "nvueStyleCompiler": "uni-app", - "compilerVersion": 3, - "splashscreen": { - "alwaysShowBeforeRender": true, - "waiting": true, - "autoclose": true, - "delay": 0 + "app-plus" : { + "usingComponents" : true, + "nvueStyleCompiler" : "uni-app", + "compilerVersion" : 3, + "splashscreen" : { + "alwaysShowBeforeRender" : false, + "waiting" : false, + "autoclose" : false, + "delay" : 0 }, /* 模块配置 */ - "modules": { - "Camera": {}, - "Record": {}, - "Bluetooth": {} + "modules" : { + "Camera" : {}, + "Record" : {}, + "Bluetooth" : {} + }, + "softinput" : { + "mode" : "adjustResize" }, /* 应用发布信息 */ - "distribute": { + "distribute" : { /* android打包配置 */ - "android": { - "permissions": [ + "android" : { + "permissions" : [ "", "", "", @@ -45,90 +48,87 @@ "", "" ], - "abiFilters": [ - "armeabi-v7a", - "arm64-v8a", - "x86" - ], - "minSdkVersion": 21 + "abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ], + "minSdkVersion" : 21 }, /* ios打包配置 */ - "ios": { - "dSYMs": false, - "privacyDescription": { - "NSMicrophoneUsageDescription": "", - "NSCameraUsageDescription": "" + "ios" : { + "dSYMs" : false, + "privacyDescription" : { + "NSMicrophoneUsageDescription" : "", + "NSCameraUsageDescription" : "" }, - "idfa": false + "idfa" : false }, /* SDK配置 */ - "sdkConfigs": { - "ad": {}, - "speech": {} + "sdkConfigs" : { + "ad" : {}, + "speech" : {} }, - "icons": { - "android": { - "xhdpi": "unpackage/res/icons/96x96.png", - "hdpi": "unpackage/res/icons/72x72.png", - "xxhdpi": "unpackage/res/icons/144x144.png", - "xxxhdpi": "unpackage/res/icons/192x192.png" + "icons" : { + "android" : { + "xhdpi" : "unpackage/res/icons/96x96.png", + "hdpi" : "unpackage/res/icons/72x72.png", + "xxhdpi" : "unpackage/res/icons/144x144.png", + "xxxhdpi" : "unpackage/res/icons/192x192.png" }, - "ios": { - "appstore": "unpackage/res/icons/1024x1024.png", - "ipad": { - "app": "unpackage/res/icons/76x76.png", - "app@2x": "unpackage/res/icons/152x152.png", - "notification": "unpackage/res/icons/20x20.png", - "notification@2x": "unpackage/res/icons/40x40.png", - "proapp@2x": "unpackage/res/icons/167x167.png", - "settings": "unpackage/res/icons/29x29.png", - "settings@2x": "unpackage/res/icons/58x58.png", - "spotlight": "unpackage/res/icons/40x40.png", - "spotlight@2x": "unpackage/res/icons/80x80.png" + "ios" : { + "appstore" : "unpackage/res/icons/1024x1024.png", + "ipad" : { + "app" : "unpackage/res/icons/76x76.png", + "app@2x" : "unpackage/res/icons/152x152.png", + "notification" : "unpackage/res/icons/20x20.png", + "notification@2x" : "unpackage/res/icons/40x40.png", + "proapp@2x" : "unpackage/res/icons/167x167.png", + "settings" : "unpackage/res/icons/29x29.png", + "settings@2x" : "unpackage/res/icons/58x58.png", + "spotlight" : "unpackage/res/icons/40x40.png", + "spotlight@2x" : "unpackage/res/icons/80x80.png" }, - "iphone": { - "app@2x": "unpackage/res/icons/120x120.png", - "app@3x": "unpackage/res/icons/180x180.png", - "notification@2x": "unpackage/res/icons/40x40.png", - "notification@3x": "unpackage/res/icons/60x60.png", - "settings@2x": "unpackage/res/icons/58x58.png", - "settings@3x": "unpackage/res/icons/87x87.png", - "spotlight@2x": "unpackage/res/icons/80x80.png", - "spotlight@3x": "unpackage/res/icons/120x120.png" + "iphone" : { + "app@2x" : "unpackage/res/icons/120x120.png", + "app@3x" : "unpackage/res/icons/180x180.png", + "notification@2x" : "unpackage/res/icons/40x40.png", + "notification@3x" : "unpackage/res/icons/60x60.png", + "settings@2x" : "unpackage/res/icons/58x58.png", + "settings@3x" : "unpackage/res/icons/87x87.png", + "spotlight@2x" : "unpackage/res/icons/80x80.png", + "spotlight@3x" : "unpackage/res/icons/120x120.png" } } } } }, /* 快应用特有相关 */ - "quickapp": {}, + "quickapp" : {}, /* 小程序特有相关 */ - "mp-weixin": { - "appid": "wxda94f40bfad0262c", - "libVersion": "latest", - "setting": { - "urlCheck": false + "mp-weixin" : { + "appid" : "wxda94f40bfad0262c", + "libVersion" : "latest", + "setting" : { + "urlCheck" : false }, - "usingComponents": true + "usingComponents" : true }, - "mp-alipay": { - "usingComponents": true + "mp-alipay" : { + "usingComponents" : true }, - "mp-baidu": { - "usingComponents": true + "mp-baidu" : { + "usingComponents" : true }, - "mp-toutiao": { - "usingComponents": true + "mp-toutiao" : { + "usingComponents" : true }, - "uniStatistics": { - "enable": false + "uniStatistics" : { + "enable" : false }, - "vueVersion": "3", - "h5": { - "title": "盒子IM", - "router": { - "base": "/h5/" + "vueVersion" : "3", + "h5" : { + "title" : "盒子IM", + "router" : { + "base" : "/h5/" } } } -/* ios打包配置 */ /* SDK配置 */ \ No newline at end of file +/* ios打包配置 *//* SDK配置 */ + diff --git a/im-uniapp/package.json b/im-uniapp/package.json index 2ada879..3e8bf34 100644 --- a/im-uniapp/package.json +++ b/im-uniapp/package.json @@ -4,7 +4,6 @@ "scripts": {} }, "dependencies": { - "js-audio-recorder": "^1.0.7", "pinyin-pro": "^3.23.1", "vconsole": "^3.15.1" } diff --git a/im-uniapp/pages.json b/im-uniapp/pages.json index 6a499e6..dd86682 100644 --- a/im-uniapp/pages.json +++ b/im-uniapp/pages.json @@ -7,16 +7,14 @@ "^u-([^-].*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue" } }, - "pages": [ - { + "pages": [{ + "path": "pages/chat/chat" + }, { "path": "pages/login/login" }, { "path": "pages/register/register" }, - { - "path": "pages/chat/chat" - }, { "path": "pages/friend/friend" }, @@ -30,7 +28,14 @@ "path": "pages/common/user-info" }, { - "path": "pages/chat/chat-box" + "path": "pages/chat/chat-box", + "style": { + "navigationStyle": "custom", + "app-plus": { + // adjustPan窗体高度不变,但窗体上推、adjustResize屏幕高度=webview窗体高度+软键盘高度 + "softinputMode": "adjustResize" + } + } }, { "path": "pages/chat/chat-private-video" @@ -71,8 +76,7 @@ "selectedColor": "#587ff0", "borderStyle": "black", "backgroundColor": "#ffffff", - "list": [ - { + "list": [{ "pagePath": "pages/chat/chat", "iconPath": "static/tarbar/chat.png", "selectedIconPath": "static/tarbar/chat_active.png", diff --git a/im-uniapp/pages/chat/chat-box.vue b/im-uniapp/pages/chat/chat-box.vue index add8199..0f2f015 100644 --- a/im-uniapp/pages/chat/chat-box.vue +++ b/im-uniapp/pages/chat/chat-box.vue @@ -1,49 +1,54 @@ diff --git a/im-uniapp/pages/register/register.vue b/im-uniapp/pages/register/register.vue index 0e8f7b8..4e4eb32 100644 --- a/im-uniapp/pages/register/register.vue +++ b/im-uniapp/pages/register/register.vue @@ -1,7 +1,7 @@ @@ -111,7 +111,7 @@ export default { } -