diff --git a/App.vue b/App.vue index e4986507..d9d18293 100644 --- a/App.vue +++ b/App.vue @@ -1,273 +1,270 @@ - - - + + + diff --git a/TUIKit/assets/styles/common.scss b/TUIKit/assets/styles/common.scss index 40ce2f71..90a0c8b5 100644 --- a/TUIKit/assets/styles/common.scss +++ b/TUIKit/assets/styles/common.scss @@ -1,60 +1,60 @@ -body, div, ul, ol, dt, dd, li, dl, h1, h2, h3, h4, p { - margin:0; - padding:0; - font-style:normal; - - /* font:12px/22px"\5B8B\4F53",Arial,Helvetica,sans-serif; */ -} - -ol, ul, li { - list-style:none; -} - -img { - border:0; - vertical-align:middle; - pointer-events:none; -} - -body{ - height: 100% important; - color:#000; - background:#FFF; -} - -.clear { - clear:both; - height:1px; - width:100%; - overflow:hidden; - margin-top:-1px; -} - -a { - color:#000; - text-decoration:none; - cursor: pointer; -} - -a:hover { - text-decoration:none; -} - -input, textarea { - user-select: auto; -} - -input:focus, input:active, textarea:focus, textarea:active { - outline: none; -} - -.chat-aside { - position: absolute; - top: 50px; - right: 0; - box-sizing: border-box; - width: 360px !important; - border-radius: 8px 0 0 8px; - z-index: 9999; - max-height: calc(100% - 50px); -} +body, div, ul, ol, dt, dd, li, dl, h1, h2, h3, h4, p { + margin:0; + padding:0; + font-style:normal; + + /* font:12px/22px"\5B8B\4F53",Arial,Helvetica,sans-serif; */ +} + +ol, ul, li { + list-style:none; +} + +img { + border:0; + vertical-align:middle; + pointer-events:none; +} + +body{ + height: 100% important; + color:#000; + background:#FFF; +} + +.clear { + clear:both; + height:1px; + width:100%; + overflow:hidden; + margin-top:-1px; +} + +a { + color:#000; + text-decoration:none; + cursor: pointer; +} + +a:hover { + text-decoration:none; +} + +input, textarea { + user-select: auto; +} + +input:focus, input:active, textarea:focus, textarea:active { + outline: none; +} + +.chat-aside { + position: absolute; + top: 50px; + right: 0; + box-sizing: border-box; + width: 360px !important; + border-radius: 8px 0 0 8px; + z-index: 9999; + max-height: calc(100% - 50px); +} diff --git a/TUIKit/components/TUIChat/message-input/index.vue b/TUIKit/components/TUIChat/message-input/index.vue index a32fbe28..d8846bc3 100644 --- a/TUIKit/components/TUIChat/message-input/index.vue +++ b/TUIKit/components/TUIChat/message-input/index.vue @@ -1,243 +1,243 @@ - - - - + + + + diff --git a/TUIKit/components/TUIChat/message-input/message-input-editor.vue b/TUIKit/components/TUIChat/message-input/message-input-editor.vue index dff1ac43..02825a3e 100644 --- a/TUIKit/components/TUIChat/message-input/message-input-editor.vue +++ b/TUIKit/components/TUIChat/message-input/message-input-editor.vue @@ -1,285 +1,285 @@ - - - - + + + + diff --git a/TUIKit/components/TUIContact/contact-info/index.vue b/TUIKit/components/TUIContact/contact-info/index.vue index ef02c343..0a9c5f56 100644 --- a/TUIKit/components/TUIContact/contact-info/index.vue +++ b/TUIKit/components/TUIContact/contact-info/index.vue @@ -1,430 +1,430 @@ - + + + + diff --git a/pages/search/search.vue b/pages/search/search.vue new file mode 100755 index 00000000..922c25da --- /dev/null +++ b/pages/search/search.vue @@ -0,0 +1,296 @@ + + + + + diff --git a/pages/search/searchList.vue b/pages/search/searchList.vue new file mode 100755 index 00000000..a5dc65c3 --- /dev/null +++ b/pages/search/searchList.vue @@ -0,0 +1,399 @@ + + + + + diff --git a/pages/search/searchVd.vue b/pages/search/searchVd.vue new file mode 100644 index 00000000..31e19a0b --- /dev/null +++ b/pages/search/searchVd.vue @@ -0,0 +1,243 @@ + + + + + diff --git a/pages/search/shop.vue b/pages/search/shop.vue new file mode 100644 index 00000000..e8ffaf02 --- /dev/null +++ b/pages/search/shop.vue @@ -0,0 +1,818 @@ + + + + + diff --git a/pages/search/tuangou.vue b/pages/search/tuangou.vue new file mode 100644 index 00000000..321280e0 --- /dev/null +++ b/pages/search/tuangou.vue @@ -0,0 +1,288 @@ + + + + + diff --git a/pages/search/user.vue b/pages/search/user.vue new file mode 100644 index 00000000..d21e7149 --- /dev/null +++ b/pages/search/user.vue @@ -0,0 +1,220 @@ + + + + + diff --git a/pages/tabbar/im/index.vue b/pages/tabbar/im/index.vue index 6f148931..480214d5 100644 --- a/pages/tabbar/im/index.vue +++ b/pages/tabbar/im/index.vue @@ -1,24 +1,27 @@ - - \ No newline at end of file + } + } + + if (me.curIndex == 0) { + me.playLocalStatus = true; + } + if (me.curIndex == 1) { + me.playFollowStatus = true; + } + if (me.curIndex == 2) { + me.playStatus = true; + } + }, + onHide() { + var me = this; + // 显示和隐藏,需要判断根据不同tab做暂停或者隐藏 + if (me.curIndex == 0) { + me.playLocalStatus = false; + me.isDraw_lo = false; + } + if (me.curIndex == 1) { + me.playFollowStatus = false; + me.isDraw_gz = false; + } + if (me.curIndex == 2) { + me.playStatus = false; + me.isDraw_tj = false; + } + }, + + // 当前页下拉刷新 + // onPullDownRefresh() { + // var me = this; + // // 下拉刷新判断,如果是不同tab,那么组件中刷新的请求也不同 + // if (me.curIndex == 0) { + // this.$refs.videoFollowComp.displayVideoPaging(1, true); + // } else if (me.curIndex == 1) { + // this.$refs.videoComp.displayVideoPaging(1, true); + // } + + // }, + methods: { + getLocation() { + uni.getLocation({ + type: 'wg84', + success: (res) => { + console.log(res); + var latitude = res.latitude; + var longitude = res.longitude; + var location = latitude + ',' + longitude; + var key = config.aMapKey; + uni.request({ + url: 'https://apis.map.qq.com/ws/geocoder/v1/', + method: 'GET', + data: { + location, + key + }, + success: (address) => { + console.log(address); + var ad_info = address.data.result.ad_info; + var cityCode = ad_info.adcode; // 城市编码cityCode + storage.setCityCode(cityCode); + this.cityCode = cityCode; + var address_name = ad_info.district || ad_info.city; + this.tabList.forEach((i, index) => { + if (index == 0) { + i.name = address_name; + // this.selectorQuery(); + var timer = setTimeout(() => { + this.selectorQuery(); + clearTimeout(timer); + }, 300); + } + }); + } + }); + }, + fail: (err) => { + console.log(err); + uni.showToast({ + icon: 'none', + title: '获取位置信息失败' + }); + // uni.showModal({ + // title:'提示', + // content:'获取位置信息失败', + // showCancel:false, + // success: (res) => { + // if(res.confirm){ + // } + // } + // }) + } + }); + }, + + // 前往搜索页面 + goSearch() { + uni.navigateTo({ + url: '/pages/search/search' + }); + }, + // ----------头部区域点击tabs + ontabtap(e) { + console.log(e); + let index = e.target.dataset.current || e.currentTarget.dataset.current; + this.isTap = true; + var currentSize = this.tabListSize[index]; + + this.updateIndicator(currentSize.left, currentSize.width); + this._touchTabIndex = index; + this.switchTab(index); + }, + // + selectorQuery() { + var dm = uni.createSelectorQuery().in(this); + dm.select('#head') + .boundingClientRect() + .exec((rect) => { + this._headHeight = rect[0].height; + }); + + // 查询 tabbar 宽度 + dm.in(this) + .select('#tab-bar') + .boundingClientRect() + .exec((rect) => { + this.tabbarWidth = rect[0].width; + console.log(this.tabbarWidth); + }); + // 查询 tabview 宽度 + dm.in(this) + .select('#tab-bar-view') + .boundingClientRect() + .exec((rect) => { + this.swiperWidth = rect[0].width; + console.log(this.swiperWidth); + }); + + // 因 nvue 暂不支持 class 查询 + // var queryTabSize = uni.createSelectorQuery().in(this); + var queryTabSize = dm; + for (var i = 0; i < this.tabList.length; i++) { + queryTabSize.select('#' + this.tabList[i].id).boundingClientRect(); + } + queryTabSize.exec((rects) => { + rects.forEach((rect) => { + rect.left = rect.left - 40; //修正 left 值,减去 padding-left: 40px + this.tabListSize[rect.dataset.id] = rect; + }); + console.log(this.tabListSize[this.tabIndex]); + this.updateIndicator(this.tabListSize[this.tabIndex].left, this.tabListSize[this.tabIndex].width); + this.switchTab(this.tabIndex); + }); + }, + + onswiperscroll(e) { + this.isDraw_gz = false; + this.isDraw_lo = false; + this.isDraw_tj = false; + // if (this.playStatus == true) { + // this.playStatus = this._lastTabIndex == 2 ? false : true; + // } + + // this.playFollowStatus = this._lastTabIndex == 1 ? false : true; + // this.playLocalStatus = this._lastTabIndex == 0 ? false : true; + + var offsetX = e.detail.dx; + var preloadIndex = this._lastTabIndex; + if (offsetX > TAB_PRELOAD_OFFSET) { + preloadIndex++; + } else if (offsetX < -TAB_PRELOAD_OFFSET) { + preloadIndex--; + } + let prop = this.pageList[preloadIndex]; + let obj = this.$refs[prop]; + if (!obj) return; + // 点击切换 + if (this.isTap) { + return; + } + // 切换失败 + if (preloadIndex === this._lastTabIndex || preloadIndex < 0 || preloadIndex > this.pageList.length - 1) { + // 是否有视频数据 + if (obj.playerList.length === 0) { + // this.loadTabData(preloadIndex); + this.isDraw_gz = false; + this.isDraw_lo = false; + this.isDraw_tj = false; + } else { + this.isDraw_gz = preloadIndex == 1 ? true : false; + this.isDraw_lo = preloadIndex == 0 ? true : false; + this.isDraw_tj = preloadIndex == 2 ? true : false; + } + return; + } + + // console.log(this.tabListSize) + var percentage = Math.abs(this.swiperWidth / offsetX); + var currentSize = this.tabListSize[this._lastTabIndex]; + var preloadSize = this.tabListSize[preloadIndex]; + var lineL = currentSize.left + (preloadSize.left - currentSize.left) / percentage; + var lineW = currentSize.width + (preloadSize.width - currentSize.width) / percentage; + this.updateIndicator(lineL, lineW); + }, + animationfinish(e) { + let index = e.detail.current; + console.log('当前索引' + index); + console.log('之前的索引' + this._lastTabIndex); + this._lastTabIndex = index; + if (this._touchTabIndex === index) { + this.isTap = false; + } + this.switchTab(index); + if (!this.tabListSize[index]) { + return; + } else { + // this.playStatus = index == 2 ? false : true; + // this.playFollowStatus = index == 1 ? true : false; + // this.playLocalStatus = index == 0 ? true : false; + this.isDraw_gz = index == 1 ? true : false; + this.isDraw_lo = index == 0 ? true : false; + this.isDraw_tj = index == 2 ? true : false; + + this.updateIndicator(this.tabListSize[index].left, this.tabListSize[index].width); + } + }, + updateIndicator(left, width) { + // console.log(left) + this.indicatorLineLeft = left; + this.indicatorLineWidth = width; + }, + + switchTab(index) { + if (this.tabIndex === index) { + return; + } + // console.dir(this.$refs) + let props = this.pageList[index]; + console.log(props); + console.log(index); + let obj = this.$refs[props]; + // if (obj.playerList.length === 0) { + // var info = storage.getVlogUserInfo() || null + // if(info!=null&&index==0){ + // obj.displayVideoPaging(this.page + 1, true); + // } + // } + + obj.setScrollRef(this._headHeight); + + console.log(this.cacheTab); + // 缓存 tabId + if (obj.playerList.length > MAX_CACHE_DATA) { + let isExist = this.cacheTab.indexOf(this.tabIndex); + if (isExist < 0) { + this.cacheTab.push(this.tabIndex); + } + } + this.tabIndex = index; + this.curIndex = index; + this.scrollTabTo(index); + this.scrollInto = this.tabList[index].id; + + // 释放 tabId + if (this.cacheTab.length > MAX_CACHE_PAGE) { + let cacheIndex = this.cacheTab[0]; + this.clearTabData(cacheIndex); + this.cacheTab.splice(0, 1); + } + // const el = this.$refs['tabitem' + index][0]; + // animation.transition(el, { + // duration: 3000, //ms + // timingFunction: 'ease', + // delay: 1000 //ms + // }); + }, + scrollTabTo(index) { + console.log(index); + const el = this.$refs['tabitem' + index][0]; + let offset = 0; + // TODO fix ios offset + if (index > 0) { + offset = this.tabbarWidth / 2 - this.tabListSize[index].width / 2; + if (this.tabListSize[index].right < this.tabbarWidth / 2) { + offset = this.tabListSize[0].width; + } + } + dom.scrollToElement(el, { + offset: -offset + }); + }, + + //---------------------------- + + // 左滑右滑选项卡改变选中状态 + changeTopTab: function (e) { + var current = e.detail.current; + this.curIndex = current; + this.tabIndex = current; + let timer = setTimeout(() => { + this.playLocalStatus = this.curIndex === 0 ? true : false; + this.playFollowStatus = this.curIndex === 1 ? true : false; + this.playStatus = this.curIndex === 2 ? true : false; + clearTimeout(timer); + }, 10); + }, + + // 下拉刷新,改变head的字样显示 + showLoading() { + this.isLoading = true; + }, + hideLoading() { + this.isLoading = false; + }, + letFollowVideoPause() { + this.playFollowStatus = false; + } + } +}; + + + diff --git a/static/images/playvd.png b/static/images/playvd.png new file mode 100644 index 00000000..4dba79b4 Binary files /dev/null and b/static/images/playvd.png differ diff --git a/uni_modules/TencentCloud-Push/changelog.md b/uni_modules/TencentCloud-Push/changelog.md new file mode 100644 index 00000000..c5e53f6a --- /dev/null +++ b/uni_modules/TencentCloud-Push/changelog.md @@ -0,0 +1,29 @@ +## 1.2.0(2025-03-31) +- 适配出海手机支持 FCM 推送。 +## 1.1.0(2024-12-11) +- 大幅减小插件包体积,优化产品体验。 +- 兼容 HBuilderX 4.36 的 Breaking changes。如果您需要 vivo/荣耀 的厂商推送,请参考 [文档](https://cloud.tencent.com/document/product/269/103522),正确配置 `manifestPlaceholders.json` 和 `mcn-services.json`。 + +## 1.0.0(2024-11-29) +- 优化和 [TencentCloud-TUICallKit 插件](https://ext.dcloud.net.cn/plugin?id=9035) 融合时的产品体验。 +- 新增点击通知栏事件 NOTIFICATION_CLICKED,支持获取推送扩展信息。 +- 在线通道支持自定义铃音功能。 + +## 0.5.1(2024-11-07) +- 优化和 [@tencentcloud/chat-uikit-uniapp](https://cloud.tencent.com/document/product/269/64507) 融合时的产品体验。 +- 优化和 [TencentCloud-TUICallKit 插件](https://ext.dcloud.net.cn/plugin?id=9035) 融合时的产品体验。 +- 新增接口 disablePostNotificationInForeground,此接口可实现应用在前台时,开/关通知栏通知(默认开)。 +- 新增接口 createNotificationChannel,支持 FCM/OPPO 自定义铃音。 + +## 0.4.0(2024-10-17) +- 支持与 [TencentCloud-TUICallKit 插件](https://ext.dcloud.net.cn/plugin?id=9035) 融合打包。 + +## 0.3.0(2024-10-12) +- 新增接口 addPushListener/removePushListener,支持获取在线推送消息,支持推送消息撤回通知。 + +## 0.2.0(2024-09-18) +- 支持 FCM +- 支持 hihonor + +## 0.1.0(2024-09-10) + - 使用 uts 开发,基于腾讯云推送服务(Push),支持 iOS 和 Android 推送,同时适配各大厂商推送。 \ No newline at end of file diff --git a/pages/me/vlogerInfo.vue b/uni_modules/TencentCloud-Push/index.js old mode 100755 new mode 100644 similarity index 100% rename from pages/me/vlogerInfo.vue rename to uni_modules/TencentCloud-Push/index.js diff --git a/uni_modules/TencentCloud-Push/package.json b/uni_modules/TencentCloud-Push/package.json new file mode 100644 index 00000000..11c43552 --- /dev/null +++ b/uni_modules/TencentCloud-Push/package.json @@ -0,0 +1,90 @@ +{ + "name": "@tencentcloud/uni-app-push", + "id": "TencentCloud-Push", + "main": "index.js", + "displayName": "【官方】uni-app 腾讯云推送服务(Push)", + "version": "1.2.0", + "description": "使用 uts 开发,基于腾讯云推送服务(Push),支持 iOS 和 Android 推送,同时适配各大厂商推送。", + "license": "ISC", + "keywords": [ + "腾讯云", + "Push", + "推送", + "Android/iOS", + "谷歌FCM" +], + "repository": "", + "engines": { + "HBuilderX": "^3.6.8" + }, + "dcloudext": { + "type": "uts", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "腾讯云即时通信IM隐私保护指引: https://web.sdk.qcloud.com/document/Tencent-IM-Privacy-Protection-Guidelines.html\n移动推送隐私保护指引: https://privacy.qq.com/document/preview/8565a4a2d26e480187ed86b0cc81d727", + "permissions": "本地存储空间" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "y" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-android": "y", + "app-ios": "y", + "app-harmony": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/TencentCloud-Push/readme-npm.md b/uni_modules/TencentCloud-Push/readme-npm.md new file mode 100644 index 00000000..e3cba80a --- /dev/null +++ b/uni_modules/TencentCloud-Push/readme-npm.md @@ -0,0 +1,299 @@ +# TencentCloud-Push + +## 简介 + +使用 uts 开发,基于腾讯云推送服务(Push),支持 iOS 和 Android 推送,同时适配各大厂商推送。 + +腾讯云推送服务(Push)提供一站式 App 推送解决方案,助您轻松提升用户留存和互动活跃度,支持与腾讯云即时通信 IM SDK、实时音视频 TRTC SDK、音视频通话 SDK、直播 SDK等音视频终端产品协同集成,在不同场景联合使用,提升业务整体功能体验。 + + + + + +#### 数据可视化,辅助运营策略 + + + +#### 支持推送消息全链路问题排查 + + + +#### 六地服务部署,严守数据安全 + +提供了中国、东南亚(新加坡、印尼雅加达)、东北亚(韩国首尔)、欧洲(德国法兰克福)以及北美(美国硅谷)数据存储中心供选择,每个数据中心均支持全球接入。如果您的应用在境外上线且用户主要在境外,您可以根据消息传输需求及合规要求,选择适合您业务的境外数据中心,保障您的数据安全。 + + +## 快速跑通 + +### 步骤1:创建应用 + +进入 [控制台](https://console.cloud.tencent.com/im) ,单击创建应用,填写应用名称,选择数据中心,单击确定,完成应用创建。 + +![](https://qcloudimg.tencent-cloud.cn/image/document/e2761226f7d2bbdfb0a301192316c7d3.png) + +### 步骤2:开通推送服务 Push + +进入 [推送服务 Push](https://console.cloud.tencent.com/im/push-plugin-push-identifier),单击立即购买或免费试用 。(每个应用可免费试用一次,有效期7天) + +![](https://qcloudimg.tencent-cloud.cn/image/document/a7e1f3847c91a807ec9be3a586f1290f.png) + +### 步骤3:下载腾讯云推送服务(Push)并复制 Push SDK 到您的项目中 + +1. 下载腾讯云推送服务(Push)。 +``` +npm install @tencentcloud/uni-app-push +``` + +2. 复制 Push SDK 到您的项目中。 + +【macOS 端】 + +``` bash +mkdir -p ./uni_modules/TencentCloud-Push && rsync -av ./node_modules/@tencentcloud/uni-app-push/ ./uni_modules/TencentCloud-Push +``` +【Window 端】 + +``` bash +xcopy .\node_modules\@tencentcloud\uni-app-push .\uni_modules\TencentCloud-Push /i /e +``` + +### 步骤4:在 App.vue 中引入并注册腾讯云推送服务(Push) + +将 SDKAppID 和 appKey 替换为您在IM 控制台 - 推送服务 Push - 接入设置页面 获取的应用的信息。如图所示: + +![](https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/im/assets/push/push.png) + +```ts +// 集成 TencentCloud-Push +import * as Push from '@/uni_modules/TencentCloud-Push'; +const SDKAppID = 0; // 您的 SDKAppID +const appKey = ''; // 客户端密钥 +Push.registerPush(SDKAppID, appKey, (data) => { + console.log('registerPush ok', data); + Push.getRegistrationID((registrationID) => { + console.log('getRegistrationID ok', registrationID); + }); + }, (errCode, errMsg) => { + console.error('registerPush failed', errCode, errMsg); + } +); + +// 监听通知栏点击事件,获取推送扩展信息 +Push.addPushListener(Push.EVENT.NOTIFICATION_CLICKED, (res) => { + // res 为推送扩展信息 + console.log('notification clicked', res); +}); + +// 监听在线推送 +Push.addPushListener(Push.EVENT.MESSAGE_RECEIVED, (res) => { + // res 为消息内容 + console.log('message received', res); +}); + +// 监听在线推送被撤回 +Push.addPushListener(Push.EVENT.MESSAGE_REVOKED, (res) => { + // res 为被撤回的消息 ID + console.log('message revoked', res); +}); +``` + +### 步骤5:测试推送(测试前请务必打开手机通知权限,允许应用通知。) + +单击 HBuilderX 的 【运行 > 运行到手机或模拟器 > 制作自定义调试基座】,使用云端证书制作 Android 或 iOS 自定义调试基座。 + +![](https://qcloudimg.tencent-cloud.cn/image/document/742b7c05364e8ff9a16d5d5601aa038b.png) + +自定义调试基座打好后,安装到手机运行。 + +[登录](https://console.cloud.tencent.com/im/push-plugin-push-check) 控制台,使用测试工具进行在线推送测试。 +![](https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/im/assets/push/test-online-push.png) + +## 厂商推送配置 +> - 请注意!HBuilderX 4.36 发布了不向下兼容的更新,如果您使用的是 HBuilderX 4.36 或者更高版本,且需要 vivo/荣耀 的厂商推送, +请升级推送版本到 1.1.0 或更高版本,并参考文档正确配置 `manifestPlaceholders.json` 和 `mcs-services.json`。 +> - 请在 `nativeResources` 目录下进行推送配置。若项目根目录尚未创建该文件夹,请新建一个名为 `nativeResources` 的文件夹。 +> - 离线推送厂商配置完成后,需要打包自定义基座。参考:[[快速跑通]>[步骤5:测试推送(测试前请务必打开手机通知权限,允许应用通知。)]](#user-content-step5) + +### 【Android】 + +1. 新建 nativeResources/android/assets 目录。 + +2. 在 [推送服务 Push > 接入设置 > 一键式快速配置](https://console.cloud.tencent.com/im/push-plugin-push-identifier) 下载 `timpush-configs.json` 文件,配置到 nativeResources/android/assets 目录下。 + +3. For 华为: + + 配置 `agconnect-services.json` (此文件获取详见 [厂商配置 > uniapp > 华为 > 步骤4:获取应用信息](https://cloud.tencent.com/document/product/269/103769))到 nativeResources/android 目录下。 + +4. For Google FCM: + + 4.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `project.plugins`,添加 `"com.google.gms.google-services"`,如下: + ``` + { + ... + "project": { + "plugins": [ + ... + "com.google.gms.google-services" + ] + } + } + ``` + + 4.2. 配置 `google-services.json` 文件到 nativeResources/android/ 目录。 + +5. For 荣耀: + + 5.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `dependencies`,添加 `"com.tencent.timpush:honor:8.3.6498"`,如下: + ``` + { + ... + "dependencies": [ + ... + "com.tencent.timpush:honor:8.3.6498" + ] + } + ``` + + 5.2. 配置 `mcs-services.json` 文件到 nativeResources/android 目录下。 + + 5.3. 配置 `appID` 到 nativeResources/android/manifestPlaceholders.json 中的 `"HONOR_APPID"`,如下: + ``` + { + "HONOR_APPID": "" + } + ``` + +6. For vivo: + + 6.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `dependencies`,添加 `"com.tencent.timpush:vivo:8.3.6498"`,如下: + ``` + { + ... + "dependencies": [ + ... + "com.tencent.timpush:vivo:8.3.6498" + ] + } + ``` + + 6.2. 配置 `appID` 和 `appKey` 到 nativeResources/android/manifestPlaceholders.json 中的 `VIVO_APPKEY` 和 `VIVO_APPID`,如下: + ``` + { + "VIVO_APPKEY": "", + "VIVO_APPID": "", + } + ``` + +### 【iOS】 + +1. 新建 nativeResources/ios/Resources 目录。 + +2. 在 nativeResources/ios/Resources 中**新建 timpush-configs.json 文件**。 + +3. 并将在 [IM控制台 > 推送服务 Push > 接入设置](https://console.cloud.tencent.com/im/push-plugin-push-identifier) 获取的证书ID,补充到 timpush-configs.json 文件中。 + + ``` + { + "businessID":"xxx" + } + ``` + +## 接口 + +| API | 描述| +|----|---| +| registerPush | 注册推送服务 (必须在 App 用户同意了隐私政策,并且允许为 App 用户提供推送服务后,再调用该接口使用推送服务)。
首次注册成功后,TencentCloud-Push SDK 生成该设备的标识 - RegistrationID。
业务侧可以把这个 RegistrationID 保存到业务服务器。业务侧根据 RegistrationID 向设备推送消息或者通知。| +| unRegisterPush | 反注册关闭推送服务。| +| setRegistrationID | 设置注册推送服务使用的推送 ID 标识,即 RegistrationID。
如果业务侧期望业务账号 ID 和推送 ID 一致,方便使用,可使用此接口,此时需注意,此接口需在 registerPush(注册推送服务)之前调用。| +| getRegistrationID | 注册推送服务成功后,获取推送 ID 标识,即 RegistrationID。| +| getNotificationExtInfo | 获取推送扩展信息。| +| getNotificationExtInfo | 收到离线推送时,点击通知栏拉起 app,调用此接口可获取推送扩展信息。| +| addPushListener | 添加 Push 监听器。| +| removePushListener | 移除 Push 监听器。| +| disablePostNotificationInForeground | 应用在前台时,开/关通知栏通知。| +| createNotificationChannel | 创建客户端通知 channel。| + + +```ts +registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|SDKAppID|number|是|推送(Push)应用 ID| +|appKey|string|是|推送(Push)应用客户端密钥| +|onSuccess|function|是|接口调用成功的回调函数| +|onError|function|否|接口调用失败的回调函数| + +```ts +unRegisterPush(onSuccess: () => void, onError?: (errCode: number, errMsg: string) => void): void; +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|onSuccess|function|是|接口调用成功的回调函数| +|onError|function|否|接口调用失败的回调函数| + +```ts +setRegistrationID(registrationID: string, onSuccess: () => void): void; +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|registrationID|string|是|设备的推送标识 ID,卸载重装会改变。| +|onSuccess|function|是|接口调用成功的回调函数| + + +```ts +getRegistrationID(onSuccess: (registrationID: string) => void): void; +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|onSuccess|function|是|接口调用成功的回调函数| + +```ts +getNotificationExtInfo(onSuccess: (extInfo: string) => void): void; +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|onSuccess|function|是|接口调用成功的回调函数| + +```ts +addPushListener(eventName: string, listener: (data: any) => void); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|eventName|string|是|推送事件类型| +|listener|function|是|推送事件处理方法| + +```ts +removePushListener(eventName: string, listener?: (data: any) => void); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|eventName|string|是|推送事件类型| +|listener|function|否|推送事件处理方法| + +```ts +disablePostNotificationInForeground(disable: boolean); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|disable|boolean|是|应用在前台时,开/关通知栏通知,默认开
- true: 应用在前台时,关闭通知栏通知。
- false: 应用在前台时,开启通知栏通知。| + +```ts +createNotificationChannel(options: any, listener: (data: any) => void); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|options.channelID|string|是|自定义 channel 的 ID| +|options.channelName|string|是|自定义 channel 的名称| +|options.channelDesc|string|否|自定义 channel 的描述| +|options.channelSound|string|否|自定义 channel 的铃音,音频文件名,不带后缀,音频文件需要放到 xxx/nativeResources/android/res/raw 中。
例如:
`options.channelSound = private_ring`,即设置 `xxx/nativeResources/android/res/raw/private_ring.mp3` 为自定义铃音| +|listener|function|是|接口调用成功的回调函数| diff --git a/uni_modules/TencentCloud-Push/readme.md b/uni_modules/TencentCloud-Push/readme.md new file mode 100644 index 00000000..f49324ed --- /dev/null +++ b/uni_modules/TencentCloud-Push/readme.md @@ -0,0 +1,285 @@ +# TencentCloud-Push + +## 简介 + +使用 uts 开发,基于腾讯云推送服务(Push),支持 iOS 和 Android 推送,同时适配各大厂商推送。 + +腾讯云推送服务(Push)提供一站式 App 推送解决方案,助您轻松提升用户留存和互动活跃度,支持与腾讯云即时通信 IM SDK、实时音视频 TRTC SDK、音视频通话 SDK、直播 SDK等音视频终端产品协同集成,在不同场景联合使用,提升业务整体功能体验。 + + + + + +#### 数据可视化,辅助运营策略 + + + +#### 支持推送消息全链路问题排查 + + + +#### 六地服务部署,严守数据安全 + +提供了中国、东南亚(新加坡、印尼雅加达)、东北亚(韩国首尔)、欧洲(德国法兰克福)以及北美(美国硅谷)数据存储中心供选择,每个数据中心均支持全球接入。如果您的应用在境外上线且用户主要在境外,您可以根据消息传输需求及合规要求,选择适合您业务的境外数据中心,保障您的数据安全。 + + +## 快速跑通 + +### 步骤1:创建应用 + +进入 [控制台](https://console.cloud.tencent.com/im) ,单击创建应用,填写应用名称,选择数据中心,单击确定,完成应用创建。 + +![](https://qcloudimg.tencent-cloud.cn/image/document/e2761226f7d2bbdfb0a301192316c7d3.png) + +### 步骤2:开通推送服务 Push + +进入 [推送服务 Push](https://console.cloud.tencent.com/im/push-plugin-push-identifier),单击立即购买或免费试用 。(每个应用可免费试用一次,有效期7天) + +![](https://qcloudimg.tencent-cloud.cn/image/document/a7e1f3847c91a807ec9be3a586f1290f.png) + +### 步骤3:将 [uni-app 腾讯云推送服务(Push)](https://ext.dcloud.net.cn/plugin?id=20169)插件导入 HbuilderX 中的工程。如图所示: + +![](https://qcloudimg.tencent-cloud.cn/image/document/ab8061fea2bf6659f571c2c11aa0d8f4.png) +![](https://qcloudimg.tencent-cloud.cn/image/document/13a3e33564e6ab79d3e609b36e8ba0d5.png) +![](https://qcloudimg.tencent-cloud.cn/image/document/3c7060f9db637c826009926c5f34a1b8.png) + +### 步骤4:在 App.vue 中引入并注册腾讯云推送服务(Push) + +将 SDKAppID 和 appKey 替换为您在IM 控制台 - 推送服务 Push - 接入设置页面 获取的应用的信息。如图所示: + +![](https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/im/assets/push/push.png) + +```ts +// 集成 TencentCloud-Push +import * as Push from '@/uni_modules/TencentCloud-Push'; +const SDKAppID = 0; // 您的 SDKAppID +const appKey = ''; // 客户端密钥 +Push.registerPush(SDKAppID, appKey, (data) => { + console.log('registerPush ok', data); + Push.getRegistrationID((registrationID) => { + console.log('getRegistrationID ok', registrationID); + }); + }, (errCode, errMsg) => { + console.error('registerPush failed', errCode, errMsg); + } +); + +// 监听通知栏点击事件,获取推送扩展信息 +Push.addPushListener(Push.EVENT.NOTIFICATION_CLICKED, (res) => { + // res 为推送扩展信息 + console.log('notification clicked', res); +}); + +// 监听在线推送 +Push.addPushListener(Push.EVENT.MESSAGE_RECEIVED, (res) => { + // res 为消息内容 + console.log('message received', res); +}); + +// 监听在线推送被撤回 +Push.addPushListener(Push.EVENT.MESSAGE_REVOKED, (res) => { + // res 为被撤回的消息 ID + console.log('message revoked', res); +}); +``` + +### 步骤5:测试推送(测试前请务必打开手机通知权限,允许应用通知。) + +单击 HBuilderX 的 【运行 > 运行到手机或模拟器 > 制作自定义调试基座】,使用云端证书制作 Android 或 iOS 自定义调试基座。 + +![](https://qcloudimg.tencent-cloud.cn/image/document/742b7c05364e8ff9a16d5d5601aa038b.png) + +自定义调试基座打好后,安装到手机运行。 + +[登录](https://console.cloud.tencent.com/im/push-plugin-push-check) 控制台,使用测试工具进行在线推送测试。 +![](https://sdk-web-1252463788.cos.ap-hongkong.myqcloud.com/im/assets/push/test-online-push.png) + + +## 厂商推送配置 + +> - 请注意!HBuilderX 4.36 发布了不向下兼容的更新,如果您使用的是 HBuilderX 4.36 或者更高版本,且需要 vivo/荣耀 的厂商推送, +请升级推送版本到 1.1.0 或更高版本,并参考文档正确配置 `manifestPlaceholders.json` 和 `mcs-services.json`。 +> - 请在 `nativeResources` 目录下进行推送配置。若项目根目录尚未创建该文件夹,请新建一个名为 `nativeResources` 的文件夹。 +> - 厂商推送配置完成后,需要打包自定义基座。参考:[[快速跑通]>[步骤5:测试推送(测试前请务必打开手机通知权限,允许应用通知。)]](#step5) + +#### 【Android】 + +1. 新建 nativeResources/android/assets 目录。 + +2. 在 [推送服务 Push > 接入设置 > 一键式快速配置](https://console.cloud.tencent.com/im/push-plugin-push-identifier) 下载 `timpush-configs.json` 文件,配置到 nativeResources/android/assets 目录下。 + +3. For 华为: + + 配置 `agconnect-services.json` (此文件获取详见 [厂商配置 > uniapp > 华为 > 步骤4:获取应用信息](https://cloud.tencent.com/document/product/269/103769))到 nativeResources/android 目录下。 + +4. For Google FCM: + + 4.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `project.plugins`,添加 `"com.google.gms.google-services"`,如下: + ``` + { + ... + "project": { + "plugins": [ + ... + "com.google.gms.google-services" + ] + } + } + ``` + + 4.2. 配置 `google-services.json` 文件到 nativeResources/android/ 目录。 + +5. For 荣耀: + + 5.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `dependencies`,添加 `"com.tencent.timpush:honor:8.3.6498"`,如下: + ``` + { + ... + "dependencies": [ + ... + "com.tencent.timpush:honor:8.3.6498" + ] + } + ``` + + 5.2. 配置 `mcs-services.json` 文件到 nativeResources/android 目录下。 + + 5.3. 配置 `appID` 到 nativeResources/android/manifestPlaceholders.json 中的 `"HONOR_APPID"`,如下: + ``` + { + "HONOR_APPID": "" + } + ``` + +6. For vivo: + + 6.1. 编辑 uni_modules/TencentCloud-Push/utssdk/app-android/config.json 的 `dependencies`,添加 `"com.tencent.timpush:vivo:8.3.6498"`,如下: + ``` + { + ... + "dependencies": [ + ... + "com.tencent.timpush:vivo:8.3.6498" + ] + } + ``` + + 6.2. 配置 `appID` 和 `appKey` 到 nativeResources/android/manifestPlaceholders.json 中的 `VIVO_APPKEY` 和 `VIVO_APPID`,如下: + ``` + { + "VIVO_APPKEY": "", + "VIVO_APPID": "", + } + ``` + +#### 【iOS】 + +1. 新建 nativeResources/ios/Resources 目录。 + +2. 在 nativeResources/ios/Resources 目录下新建 `timpush-configs.json` 文件。 + +3. 将在 [IM控制台 > 推送服务 Push > 接入设置](https://console.cloud.tencent.com/im/push-plugin-push-identifier) 获取的证书ID,补充到 `timpush-configs.json` 文件中。 + + ``` + { + "businessID":"xxx" + } + ``` + +## 接口 + +| API | 描述| +|----|---| +| registerPush | 注册推送服务 (必须在 App 用户同意了隐私政策,并且允许为 App 用户提供推送服务后,再调用该接口使用推送服务)。
首次注册成功后,TencentCloud-Push SDK 生成该设备的标识 - RegistrationID。
业务侧可以把这个 RegistrationID 保存到业务服务器。业务侧根据 RegistrationID 向设备推送消息或者通知。| +| unRegisterPush | 反注册关闭推送服务。| +| setRegistrationID | 设置注册推送服务使用的推送 ID 标识,即 RegistrationID。
如果业务侧期望业务账号 ID 和推送 ID 一致,方便使用,可使用此接口,此时需注意,此接口需在 registerPush(注册推送服务)之前调用。| +| getRegistrationID | 注册推送服务成功后,获取推送 ID 标识,即 RegistrationID。| +| getNotificationExtInfo | 收到离线推送时,点击通知栏拉起 app,调用此接口可获取推送扩展信息。| +| addPushListener | 添加 Push 监听器。| +| removePushListener | 移除 Push 监听器。| +| disablePostNotificationInForeground | 应用在前台时,开/关通知栏通知(默认开)。| +| createNotificationChannel | 创建客户端通知 channel。| + +```ts +registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|SDKAppID|number|是|推送(Push)应用 ID| +|appKey|string|是|推送(Push)应用客户端密钥| +|onSuccess|function|是|接口调用成功的回调函数| +|onError|function|否|接口调用失败的回调函数| + +```ts +unRegisterPush(onSuccess: () => void, onError?: (errCode: number, errMsg: string) => void): void; +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|onSuccess|function|是|接口调用成功的回调函数| +|onError|function|否|接口调用失败的回调函数| + +```ts +setRegistrationID(registrationID: string, onSuccess: () => void): void; +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|registrationID|string|是|设备的推送标识 ID,卸载重装会改变。| +|onSuccess|function|是|接口调用成功的回调函数| + + +```ts +getRegistrationID(onSuccess: (registrationID: string) => void): void; +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|onSuccess|function|是|接口调用成功的回调函数| + +```ts +getNotificationExtInfo(onSuccess: (extInfo: string) => void): void; +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|onSuccess|function|是|接口调用成功的回调函数| + +```ts +addPushListener(eventName: string, listener: (data: any) => void); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|eventName|string|是|推送事件类型| +|listener|function|是|推送事件处理方法| + +```ts +removePushListener(eventName: string, listener?: (data: any) => void); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|eventName|string|是|推送事件类型| +|listener|function|否|推送事件处理方法| + +```ts +disablePostNotificationInForeground(disable: boolean); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|disable|boolean|是|应用在前台时,开/关通知栏通知,默认开
- true: 应用在前台时,关闭通知栏通知。
- false: 应用在前台时,开启通知栏通知。| + +```ts +createNotificationChannel(options: any, listener: (data: any) => void); +``` + +|属性|类型|必填|说明| +|----|---|----|----| +|options.channelID|string|是|自定义 channel 的 ID| +|options.channelName|string|是|自定义 channel 的名称| +|options.channelDesc|string|否|自定义 channel 的描述| +|options.channelSound|string|否|自定义 channel 的铃音,音频文件名,不带后缀,音频文件需要放到 xxx/nativeResources/android/res/raw 中。
例如:
`options.channelSound = private_ring`,即设置 `xxx/nativeResources/android/res/raw/private_ring.mp3` 为自定义铃音| +|listener|function|是|接口调用成功的回调函数| diff --git a/uni_modules/TencentCloud-Push/utssdk/app-android/config.json b/uni_modules/TencentCloud-Push/utssdk/app-android/config.json new file mode 100644 index 00000000..ff8cfffd --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-android/config.json @@ -0,0 +1,29 @@ +{ + "minSdkVersion": "21", + "dependencies": [ + "com.google.android.material:material:1.3.0", + "com.google.code.gson:gson:2.9.1", + "commons-codec:commons-codec:1.15", + "com.github.bumptech.glide:glide:4.12.0", + "com.tencent.timpush:timpush:8.5.6864", + "com.tencent.liteav.tuikit:tuicore:8.5.6864", + "com.tencent.timpush:huawei:8.5.6864", + "com.tencent.timpush:xiaomi:8.5.6864", + "com.tencent.timpush:oppo:8.5.6864", + "com.tencent.timpush:meizu:8.5.6864", + "com.tencent.timpush:fcm:8.5.6864", + "com.tencent.timpush:honor:8.5.6864", + "com.tencent.timpush:vivo:8.5.6864" + ], + "project": { + "plugins": [ + "com.huawei.agconnect", + "com.hihonor.mcs.asplugin" + ], + "dependencies": [ + "com.huawei.agconnect:agcp:1.9.1.301", + "com.google.gms:google-services:4.3.15", + "com.hihonor.mcs:asplugin:2.0.1.300" + ] + } +} \ No newline at end of file diff --git a/uni_modules/TencentCloud-Push/utssdk/app-android/index.uts b/uni_modules/TencentCloud-Push/utssdk/app-android/index.uts new file mode 100644 index 00000000..ebba8512 --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-android/index.uts @@ -0,0 +1,152 @@ +import { UTSAndroid } from 'io.dcloud.uts'; +import Context from 'android.content.Context'; +import TIMPushManager from 'com.tencent.qcloud.tim.push.TIMPushManager'; +import TIMPushConfig from 'com.tencent.qcloud.tim.push.config.TIMPushConfig'; +import { PushCallbackOptions } from './push-callback-options.uts'; +import { PushListenerOptions } from './push-listener-options.uts'; +import PushCallback from './push-callback.uts'; +import PushListener from './push-listener.uts'; + +const context: Context | null = UTSAndroid.getAppContext(); +console.warn('Push | package.name:', context?.getPackageName()); +TIMPushConfig.getInstance().setRunningPlatform(2); +const Push = TIMPushManager.getInstance(); + +export class EVENT { + static MESSAGE_RECEIVED: string = 'message_received' + static MESSAGE_REVOKED: string = 'message_revoked' + static NOTIFICATION_CLICKED: string = 'notification_clicked' +} + +let disableNotification = false; +export function disablePostNotificationInForeground(disable: boolean): void { + console.log('Push | disablePostNotificationInForeground', disable); + disableNotification = disable; + Push.disablePostNotificationInForeground(disableNotification); +} + +export function registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void): void { + if (SDKAppID == 0) { + onError?.(9010001, 'Invalid SDKAppID'); + } else if (appKey == '') { + onError?.(9010002, 'Invalid appKey'); + } + const pushCbOptions: PushCallbackOptions = { + apiName: 'registerPush', + success: (res?: any) => { + Push.disablePostNotificationInForeground(disableNotification); + // 强转下类型,避免类型推断错误 + let token: string = res as string; + onSuccess(token); + }, + fail: (errCode: number, errMsg: string) => { + onError?.(errCode, errMsg); + } + }; + // 注意!!! 这里不要写成 new PushCallback({ api, success, fail }),否则会因类型推断不一致导致编译错误 + Push.registerPush(context, SDKAppID.toInt(), appKey, new PushCallback(pushCbOptions)); +} + +export function setRegistrationID(registrationID: string, onSuccess: () => void): void { + const pushCbOptions: PushCallbackOptions = { + apiName: 'setRegistrationID', + success: (res?: any) => { + onSuccess(); + }, + fail: (errCode: number, errMsg: string) => { + // 空实现 + } + }; + // 注意!!! 这里不要写成 new PushCallback({ api, success, fail }),否则会因类型推断不一致导致编译错误 + Push.setRegistrationID(registrationID, new PushCallback(pushCbOptions)); +} + +export function getRegistrationID(onSuccess: (registrationID: string) => void): void { + const pushCbOptions: PushCallbackOptions = { + apiName: 'getRegistrationID', + success: (res?: any) => { + // 强转下类型,避免类型推断错误 + let registrationID: string = res as string; + onSuccess(registrationID); + }, + fail: (errCode: number, errMsg: string) => { + // 空实现 + } + }; + // 注意!!! 这里不要写成 new PushCallback({ api, success, fail }),否则会因类型推断不一致导致编译错误 + Push.getRegistrationID(new PushCallback(pushCbOptions)); +} + +export function unRegisterPush(onSuccess: () => void, onError?: (errCode: number, errMsg: string) => void): void { + const pushCbOptions: PushCallbackOptions = { + apiName: 'unRegisterPush', + success: (res?: any) => { + onSuccess(); + }, + fail: (errCode: number, errMsg: string) => { + // 空实现 + }, + }; + // 注意!!! 这里不要写成 new PushCallback({ api, success, fail }),否则会因类型推断不一致导致编译错误 + Push.unRegisterPush(new PushCallback(pushCbOptions)); +} + +export function createNotificationChannel(options: any, onSuccess: (extInfo: string) => void): void { + const pushCbOptions: PushCallbackOptions = { + apiName: 'createNotificationChannel', + success: (res?: any) => { + let ret: string = res as string; + onSuccess(ret); + }, + fail: (errCode: number, errMsg: string) => { + // 空实现 + }, + }; + Push.callExperimentalAPI('createNotificationChannel', JSON.stringify(options), new PushCallback(pushCbOptions)); +} + +export function getNotificationExtInfo(onSuccess: (extInfo: string) => void): void { + const pushCbOptions: PushCallbackOptions = { + apiName: 'getNotificationExtInfo', + success: (res?: any) => { + let ret: string = res as string; + onSuccess(ret); + }, + fail: (errCode: number, errMsg: string) => { + // 空实现 + }, + }; + Push.callExperimentalAPI('getNotificationExtInfo', null, new PushCallback(pushCbOptions)); +} + +const listenerMap = new Map void>>(); + +const pushListenerOptions: PushListenerOptions = { + listener: (eventName: string, data: any) => { + listenerMap.get(eventName)?.forEach(item => { + item(data); + }); + }, +}; + +const pushListener = new PushListener(pushListenerOptions); + +@UTSJS.keepAlive +export function addPushListener(eventName: string, listener: (res: any) => void): void { + if(listenerMap.size === 0) { + Push.addPushListener(pushListener); + } + const listeners:Array<(res: any) => void> = [listener]; + listenerMap.get(eventName)?.forEach(item => { + listeners.push(item); + }) + listenerMap.set(eventName, listeners); +} + + +export function removePushListener(eventName: string, listener?: (res: any) => void): void { + listenerMap.delete(eventName); + if(listenerMap.size === 0) { + Push.removePushListener(pushListener); + } +} diff --git a/uni_modules/TencentCloud-Push/utssdk/app-android/push-callback-options.uts b/uni_modules/TencentCloud-Push/utssdk/app-android/push-callback-options.uts new file mode 100644 index 00000000..ab6f7bf1 --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-android/push-callback-options.uts @@ -0,0 +1,5 @@ +export type PushCallbackOptions = { + apiName: string + success: (res?: any) => void + fail: (errCode: number, errMsg: string) => void +} diff --git a/uni_modules/TencentCloud-Push/utssdk/app-android/push-callback.uts b/uni_modules/TencentCloud-Push/utssdk/app-android/push-callback.uts new file mode 100644 index 00000000..32c9f572 --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-android/push-callback.uts @@ -0,0 +1,28 @@ +import TIMPushCallback from 'com.tencent.qcloud.tim.push.TIMPushCallback'; +import { PushCallbackOptions } from './push-callback-options.uts'; + +const LOG_PREFIX: string = 'Push |'; +export default class PushCallback implements TIMPushCallback { + private apiName: string; + private success: (data?: any) => void; + private fail: (errCode: number, errMsg: string) => void; + + constructor(options: PushCallbackOptions) { + this.apiName = options.apiName; + this.success = options.success; + this.fail = options.fail; + } + + override onSuccess(data?: any) { + console.log(`${LOG_PREFIX} ${this.apiName} ok, data:`, data); + if (data == null) { + this.success?.(''); + } else { + this.success?.(data); + } + } + + override onError(errCode: Int, errMsg: string, data?: any) { + this.fail?.(errCode as number, errMsg); + } +} \ No newline at end of file diff --git a/uni_modules/TencentCloud-Push/utssdk/app-android/push-listener-options.uts b/uni_modules/TencentCloud-Push/utssdk/app-android/push-listener-options.uts new file mode 100644 index 00000000..a155ba25 --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-android/push-listener-options.uts @@ -0,0 +1,3 @@ +export type PushListenerOptions = { + listener: (eventType: string, data: any) => void +} diff --git a/uni_modules/TencentCloud-Push/utssdk/app-android/push-listener.uts b/uni_modules/TencentCloud-Push/utssdk/app-android/push-listener.uts new file mode 100644 index 00000000..5c39c248 --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-android/push-listener.uts @@ -0,0 +1,25 @@ +import TIMPushListener from 'com.tencent.qcloud.tim.push.TIMPushListener'; +import TIMPushMessage from 'com.tencent.qcloud.tim.push.TIMPushMessage'; +import { PushListenerOptions } from './push-listener-options.uts'; + +const LOG_PREFIX: string = 'Push | PushListener'; +export default class PushListener implements TIMPushListener { + private listener: (eventType: string, data: any) => void; + + constructor(options: PushListenerOptions) { + this.listener = options.listener; + console.log(`${LOG_PREFIX} ok`); + } + + override onRecvPushMessage(message: TIMPushMessage) { + this.listener('message_received', { data: message }); + } + + override onRevokePushMessage(messageID: string) { + this.listener('message_revoked', { data: messageID }); + } + + override onNotificationClicked(ext: string) { + this.listener('notification_clicked', { data: ext }); + } +} \ No newline at end of file diff --git a/uni_modules/TencentCloud-Push/utssdk/app-ios/UTS.entitlements b/uni_modules/TencentCloud-Push/utssdk/app-ios/UTS.entitlements new file mode 100644 index 00000000..903def2a --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-ios/UTS.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/uni_modules/TencentCloud-Push/utssdk/app-ios/config.json b/uni_modules/TencentCloud-Push/utssdk/app-ios/config.json new file mode 100644 index 00000000..f4b471db --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-ios/config.json @@ -0,0 +1,11 @@ +{ + "deploymentTarget": "9.0", + "dependencies-pods": [ + { + "name": "TXIMSDK_Plus_iOS_XCFramework", + "version": "8.5.6864" + }, { + "name": "TIMPush", + "version": "8.5.6864" + }] +} diff --git a/uni_modules/TencentCloud-Push/utssdk/app-ios/index.uts b/uni_modules/TencentCloud-Push/utssdk/app-ios/index.uts new file mode 100644 index 00000000..0d58ad2f --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-ios/index.uts @@ -0,0 +1,125 @@ +import { TIMPushManager } from "TIMPush" +import { NSObject } from "DCloudUTSFoundation" +import PushListener from './push-listener.uts' +import { PushListenerOptions } from './push-listener-options.uts' + +const LOG_PREFIX = 'Push |'; + +export class EVENT { + static MESSAGE_RECEIVED: string = 'message_received' + static MESSAGE_REVOKED: string = 'message_revoked' + static NOTIFICATION_CLICKED: string = 'notification_clicked' +} + +function setRunningPlatform(): void { + console.log(LOG_PREFIX, 'setRunningPlatform'); + const param = new NSString("{\"runningPlatform\":2}"); + TIMPushManager.callExperimentalAPI('setPushConfig', param = param, succ = (ext?: NSObject): void => { + let platform: string = ext as string; + console.log(LOG_PREFIX, 'setRunningPlatform ok. platform:', platform); + }, fail = (code?: Int32 ,desc?:String): void => { + console.log(LOG_PREFIX, `setRunningPlatform fail. code: ${code}, desc: ${desc}`); + } + ); +} + +let disableNotification = false; + +export function disablePostNotificationInForeground(_disable: boolean): void { + console.log(LOG_PREFIX, 'disablePostNotificationInForeground', _disable); + disableNotification = _disable; + TIMPushManager.disablePostNotificationInForeground(disable = disableNotification); +} + +export function registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void): void { + if (SDKAppID == 0) { + onError?.(9010001, 'Invalid SDKAppID'); + } else if (appKey == '') { + onError?.(9010002, 'Invalid appKey'); + } + setRunningPlatform(); + TIMPushManager.registerPush(SDKAppID.toInt32(), appKey = appKey, succ = (deviceToken?: Data): void => { + TIMPushManager.disablePostNotificationInForeground(disable = disableNotification); + console.log('devicetoken ->', deviceToken, deviceToken?.count); + onSuccess(''); + }, fail = (code?: Int32 ,desc?:String): void => { + onError?.(code as number, desc as string); + } + ); +} + +export function unRegisterPush(onSuccess: () => void, onError: (errCode: number, errMsg: string) => void): void { + TIMPushManager.unRegisterPush((): void => { + onSuccess(); + }, fail = (code?: Int32 ,desc?:String): void => { + onError(code as number, desc as string); + } + ); +} + +export function setRegistrationID(registrationID: string, onSuccess: () => void): void { + console.log(LOG_PREFIX, 'setRegistrationID', `registrationID:${registrationID}`); + TIMPushManager.setRegistrationID(registrationID, callback = (): void => { + console.log(LOG_PREFIX, 'setRegistrationID ok'); + onSuccess(); + }); +} + +export function getRegistrationID(onSuccess: (registrationID: string) => void): void { + TIMPushManager.getRegistrationID((value ?: string): void => { + // 这里需要转一下,否则会有问题 + let ret: string = value as string; + onSuccess(ret); + }); +} + +export function createNotificationChannel(options: any, onSuccess: (data: string) => void): void { + // 空实现 +} + +// 注意!!!这里的 extInfo 不能写成 ext,否则会跟内部的 ext?:NSObject 有冲突;也不能写成 extension,否则会导致编译错误 +export function getNotificationExtInfo(onSuccess: (extInfo: string) => void): void { + console.log(LOG_PREFIX, 'getNotificationExtInfo'); + TIMPushManager.callExperimentalAPI('getNotificationExtInfo', param = {}, succ = (ext?: NSObject): void => { + let str: string = ext as string; + console.log(LOG_PREFIX, 'getNotificationExtInfo ok. ext:', str); + onSuccess(str); + }, fail = (code?: Int32 ,desc?:String): void => { + // 空实现 + } + ); +} + + +const listenerMap = new Map void>>(); + +const pushListenerOptions: PushListenerOptions = { + listener: (eventName: string, data: any) => { + listenerMap.get(eventName)?.forEach(item => { + item(data); + }); + }, +}; + +const pushListener = new PushListener(pushListenerOptions); + +@UTSJS.keepAlive +export function addPushListener(eventName: string, _listener: (res: any) => void): void { + console.log(LOG_PREFIX, 'addPushListener', eventName); + if(listenerMap.size === 0) { + TIMPushManager.addPushListener(listener = pushListener); + } + const listeners:Array<(res: any) => void> = [_listener]; + listenerMap.get(eventName)?.forEach(item => { + listeners.push(item); + }) + listenerMap.set(eventName, listeners); +} + +export function removePushListener(eventName: string, _listener?: (res: any) => void): void { + console.log(LOG_PREFIX, 'removePushListener', eventName); + listenerMap.delete(eventName); + if(listenerMap.size === 0) { + TIMPushManager.removePushListener(listener = pushListener); + } +} \ No newline at end of file diff --git a/uni_modules/TencentCloud-Push/utssdk/app-ios/push-listener-options.uts b/uni_modules/TencentCloud-Push/utssdk/app-ios/push-listener-options.uts new file mode 100644 index 00000000..db8c8ffa --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-ios/push-listener-options.uts @@ -0,0 +1,3 @@ +export type PushListenerOptions = { + listener: (eventType: string, data: any) => void +} diff --git a/uni_modules/TencentCloud-Push/utssdk/app-ios/push-listener.uts b/uni_modules/TencentCloud-Push/utssdk/app-ios/push-listener.uts new file mode 100644 index 00000000..5e87c347 --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/app-ios/push-listener.uts @@ -0,0 +1,24 @@ +import { TIMPushListener, TIMPushMessage} from "TIMPush" +import { PushListenerOptions } from './push-listener-options.uts'; + +const LOG_PREFIX: string = 'Push | PushListener'; +export default class PushListener implements TIMPushListener { + private listener: (eventType: string, data: any) => void; + + constructor(options: PushListenerOptions) { + this.listener = options.listener; + console.log(`${LOG_PREFIX} ok`); + } + + onRecvPushMessage(message: TIMPushMessage) { + this.listener('message_received', { data: message }); + } + + onRevokePushMessage(messageID: string) { + this.listener('message_revoked', { data: messageID }); + } + + onNotificationClicked(ext: string) { + this.listener('notification_clicked', { data: ext }); + } +} \ No newline at end of file diff --git a/uni_modules/TencentCloud-Push/utssdk/interface.uts b/uni_modules/TencentCloud-Push/utssdk/interface.uts new file mode 100644 index 00000000..ecf8da4b --- /dev/null +++ b/uni_modules/TencentCloud-Push/utssdk/interface.uts @@ -0,0 +1,11 @@ +interface Push { + setRegistrationID(registrationID: string, onSuccess: () => void): void, + registerPush(SDKAppID: number, appKey: string, onSuccess: (data: string) => void, onError?: (errCode: number, errMsg: string) => void): void, + getRegistrationID(onSuccess: (registrationID: string) => void): void, + unRegisterPush(onSuccess: () => void, onError?: (errCode: number, errMsg: string) => void): void, + getNotificationExtInfo(onSuccess: (extInfo: string) => void): void + addPushListener(eventName: string, listener: (res: any) => void): void + removePushListener(eventName: string, listener?: (res: any) => void): void + disablePostNotificationInForeground(disable: boolean): void + createNotificationChannel(options: any, onSuccess: (data: string) => void): void +} diff --git a/utils/request.js b/utils/request.js index db0f2ad5..f3040e90 100644 --- a/utils/request.js +++ b/utils/request.js @@ -17,17 +17,20 @@ function cleanStorage() { uni.hideLoading(); } - storage.setHasLogin(false); - storage.setAccessToken(""); - storage.setRefreshToken(""); - console.log("清空token"); - storage.setUuid(""); - storage.setUserInfo({}); - // 清理vlog信息 - storage.setVlogToken(""); - storage.setVlogUserInfo({}); - // 清除初始化数据内容 - storage.setRefreshVlogIndex("0"); //不需要刷新 + storage.setHasLogin(false); + storage.setAccessToken(""); + storage.setRefreshToken(""); + console.log("清空token"); + storage.setUuid(""); + storage.setUserInfo({}); + // 清理vlog信息 + storage.setVlogToken("") + storage.setVlogUserInfo(null) + // 清除初始化数据内容 + storage.setRefreshVlogIndex('0') //不需要刷新 + + // 防抖处理跳转 + // #ifdef MP-WEIXIN // 防抖处理跳转 // #ifdef MP-WEIXIN @@ -94,28 +97,29 @@ http.interceptors.request.use( ..._params, }; - config.params = params; - config.header.accessToken = accessToken; - } - // 配置vlog所需参数 - let vlogToken = storage.getVlogToken(); - let vlogId = storage.getVlogUserInfo(); - // console.log(vlogId) - // console.log(vlogToken) - if (vlogToken) { - config.header.headerUserToken = vlogToken; - config.header.headerUserId = vlogId.id; - } - config.header = { - ...config.header, - uuid: storage.getUuid() || uuid.v1(), - }; - // console.log(config) - return config; - }, - (config) => { - return Promise.reject(config); - } + config.params = params; + config.header.accessToken = accessToken; + + } + // 配置vlog所需参数 + let vlogToken = storage.getVlogToken(); + let vlogId = storage.getVlogUserInfo(); + // console.log(vlogId) + // console.log(vlogToken) + if (vlogToken) { + config.header.headerUserToken = vlogToken; + config.header.headerUserId = vlogId.id; + } + config.header = { + ...config.header, + uuid: storage.getUuid() || uuid.v1(), + }; + // console.log(config) + return config; + }, + (config) => { + return Promise.reject(config); + } ); // 是否正在刷新的标记 @@ -124,15 +128,15 @@ let isRefreshing = false; let requests = []; // 必须使用异步函数,注意 http.interceptors.response.use( - async (response) => { - // console.log(isRefreshing) - // console.log(response) - /* 请求之后拦截器。可以使用async await 做异步操作 */ - // token存在并且token过期 - if (isRefreshing && response.statusCode === 403) { - cleanStorage(); - isRefreshing = false; - } + async (response) => { + // console.log(isRefreshing) + // console.log(response) + /* 请求之后拦截器。可以使用async await 做异步操作 */ + // token存在并且token过期 + if (isRefreshing && response.statusCode === 403) { + cleanStorage(); + isRefreshing = false; + } let token = storage.getAccessToken(); if ( @@ -179,26 +183,26 @@ http.interceptors.response.use( ) { cleanStorage(); - // 如果当前状态码为正常但是success为不正常时 - } else if ( - (response.statusCode == 200 && !response.data.success) || - response.statusCode == 400 - ) { - if (response.data.message) { - uni.showToast({ - title: response.data.message, - icon: "none", - duration: 1500, - }); - } - } else if (response.data.code == 502) { - cleanStorage(); - } - return response; - }, - (error) => { - return error; - } + // 如果当前状态码为正常但是success为不正常时 + } else if ( + (response.statusCode == 200 && !response.data.success) || + response.statusCode == 400 + ) { + if (response.data.message) { + uni.showToast({ + title: response.data.message, + icon: "none", + duration: 1500, + }); + } + } else if (response.data.code == 502) { + cleanStorage(); + } + return response; + }, + (error) => { + return error; + } ); export { http }; diff --git a/utils/storage.js b/utils/storage.js index b2a14228..ba5ce864 100644 --- a/utils/storage.js +++ b/utils/storage.js @@ -10,7 +10,13 @@ const FACE_LOGIN = isDev ? "face_login_dev" : "face_login"; const FINGER_LOGIN = isDev ? "finger_login_dev" : "finger_login"; const CART_BACKBTN = isDev ? "cart_backbtn_dev" : "cart_backbtn"; const AFTERSALE_DATA = isDev ? "aftersale_data_dev" : "aftersale_data"; -export default { +export default { + setCityCode(val) { + return uni.setStorageSync('cityCode',val) + }, + getCityCode(){ + return uni.getStorageSync('cityCode') + }, /** * 写入RefreshVlogIndex */ diff --git a/utils/tools.js b/utils/tools.js index 5b254718..06497172 100644 --- a/utils/tools.js +++ b/utils/tools.js @@ -95,16 +95,28 @@ const theNextDayTime = () => { }; const graceNumber = (number) => { + // if (number == 0) { + // return "0"; + // } else if (number > 999 && number <= 9999) { + // return (number / 1000).toFixed(1) + "k"; + // } else if (number > 9999 && number <= 99999) { + // return (number / 10000).toFixed(1) + "w"; + // } else if (number > 99999) { + // return "10w+"; + // } + // return number; if (number == 0) { return "0"; - } else if (number > 999 && number <= 9999) { - return (number / 1000).toFixed(1) + "k"; - } else if (number > 9999 && number <= 99999) { - return (number / 10000).toFixed(1) + "w"; - } else if (number > 99999) { - return "10w+"; } - return number; + if (number < 1000) { + return number.toString(); + } else if (number < 10000) { + return (number / 1000).toFixed(1).replace(/\.0$/, '') + "k"; + } else if (number < 100000000) { + return (number / 10000).toFixed(1).replace(/\.0$/, '') + "w"; + } else { + return (number / 100000000).toFixed(1).replace(/\.0$/, '') + "亿+"; + } } // 时间格式化时间为: 多少分钟前、多少天前 @@ -237,6 +249,6 @@ export { getDateBeforeNow, isStrEmpty, getAstro, - getAnimal, + getAnimal, dateFormat }; \ No newline at end of file diff --git a/vue.config.js b/vue.config.js index 904355d0..7f91f533 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,35 +1,35 @@ -const ScriptSetup = require('unplugin-vue2-script-setup/webpack').default; - -module.exports = { - parallel: false, - configureWebpack: { - plugins: [ - ScriptSetup({ - /* options */ - }), - ], - }, - chainWebpack(config) { - // disable type check and let `vue-tsc` handles it - config.plugins.delete('fork-ts-checker'); - }, -}; -// module.exports = { -// /** -// * 此处为发行h5,微信小程序,app中删除console -// * 如需显示console 需要注释此处重新运行 -// */ -// chainWebpack: (config) => { -// // 发行或运行时启用了压缩时会生效 -// config.optimization.minimizer('terser').tap((args) => { -// const compress = args[0].terserOptions.compress -// // 非 App 平台移除 console 代码(包含所有 console 方法,如 log,debug,info...) -// compress.drop_console = true -// compress.pure_funcs = [ -// '__f__', // App 平台 vue 移除日志代码 -// // 'console.debug' // 可移除指定的 console 方法 -// ] -// return args -// }) -// } +const ScriptSetup = require('unplugin-vue2-script-setup/webpack').default; + +module.exports = { + parallel: false, + configureWebpack: { + plugins: [ + ScriptSetup({ + /* options */ + }), + ], + }, + chainWebpack(config) { + // disable type check and let `vue-tsc` handles it + config.plugins.delete('fork-ts-checker'); + }, +}; +// module.exports = { +// /** +// * 此处为发行h5,微信小程序,app中删除console +// * 如需显示console 需要注释此处重新运行 +// */ +// chainWebpack: (config) => { +// // 发行或运行时启用了压缩时会生效 +// config.optimization.minimizer('terser').tap((args) => { +// const compress = args[0].terserOptions.compress +// // 非 App 平台移除 console 代码(包含所有 console 方法,如 log,debug,info...) +// compress.drop_console = true +// compress.pure_funcs = [ +// '__f__', // App 平台 vue 移除日志代码 +// // 'console.debug' // 可移除指定的 console 方法 +// ] +// return args +// }) +// } // } \ No newline at end of file