Commit 45b2ed9f authored by wp song's avatar wp song

feat(推送): 添加推送功能相关组件和服务

实现推送功能的核心模块,包括:
1. 新增推送服务类PushService处理推送相关API调用
2. 创建推送权限请求组件PushPermission
3. 添加推送工具类UniPush封装uni-app推送能力
4. 在App.vue中集成推送初始化逻辑
5. 新增推送测试页面PushTest用于调试
6. 更新登录页面获取客户端ID
7. 配置manifest.json启用uniPush功能
8. 添加pages.json配置页面路由和样式
parent 40f185c7
Pipeline #11062 passed with stage
...@@ -18,15 +18,20 @@ ...@@ -18,15 +18,20 @@
</transition> </transition>
</div> </div>
<tab-bar v-if="showTab"></tab-bar> <tab-bar v-if="showTab"></tab-bar>
<!-- 推送权限请求组件 -->
<push-permission ref="pushPermission" />
</div> </div>
</template> </template>
<script> <script>
import tabBar from "@/components/TabBar"; import tabBar from "@/components/TabBar";
import PushPermission from "@/components/PushPermission";
import pushInstance from "@/utils/push";
export default { export default {
components: { components: {
tabBar, tabBar,
PushPermission,
}, },
data() { data() {
return { return {
...@@ -74,6 +79,9 @@ export default { ...@@ -74,6 +79,9 @@ export default {
this.appHeight = sH - tabH + "px"; this.appHeight = sH - tabH + "px";
this.tabH = tabH + "px"; this.tabH = tabH + "px";
this.screenH = sH + "px"; this.screenH = sH + "px";
// 初始化推送功能(启用 force_notification:true 模式)
this.initPush();
}, },
computed: { computed: {
showTab() { showTab() {
...@@ -107,6 +115,80 @@ export default { ...@@ -107,6 +115,80 @@ export default {
} }
}, },
}, },
methods: {
// 初始化推送功能
async initPush() {
try {
// 启用 force_notification:true 模式
pushInstance.setForceNotificationMode(true);
// 设置推送消息处理回调
pushInstance.onMessage = this.handlePushMessage;
pushInstance.onClick = this.handlePushClick;
console.log('推送功能初始化完成(force_notification模式已启用)');
} catch (error) {
console.error('推送功能初始化失败:', error);
}
},
// 处理推送消息
handlePushMessage(message) {
console.log('收到推送消息(使用 force_notification:true 时在线不会触发):', message);
// 注意:当后端使用 force_notification:true 时,应用在线时不会触发此方法
// 消息会直接显示在通知栏,用户点击后才会触发 handlePushClick
// 如果不使用 force_notification:true,可以在此处显示消息提示
if (!pushInstance.forceNotificationMode && message.data && message.data.title) {
this.$toast({
message: message.data.title,
duration: 3000
});
}
// 更新未读消息数量
this.updateUnreadCount();
},
// 处理推送点击
handlePushClick(message) {
console.log('用户点击推送消息:', message);
// 根据消息类型跳转到对应页面
if (message.data && message.data.page) {
this.navigateToPage(message.data.page, message.data.params);
}
},
// 更新未读消息数量
async updateUnreadCount() {
try {
// 这里可以调用API获取未读消息数量
// const count = await pushService.getUnreadCount(userId);
// 更新store中的未读消息数量
} catch (error) {
console.error('更新未读消息数量失败:', error);
}
},
// 页面跳转
navigateToPage(page, params = {}) {
const routes = {
'message': '/messageCenter',
'workbench': '/saveWorkbench',
'danger': '/danger',
'risk': '/risk'
};
const route = routes[page] || '/';
this.$router.push({
path: route,
query: params
});
}
},
}; };
</script> </script>
......
<template>
<div v-if="showPermissionDialog" class="push-permission-dialog">
<div class="dialog-mask"></div>
<div class="dialog-content">
<div class="dialog-header">
<h3>开启消息推送</h3>
</div>
<div class="dialog-body">
<p>开启推送通知,及时接收重要消息和提醒</p>
<ul class="benefits">
<li>• 实时接收安全检查提醒</li>
<li>• 及时获取任务通知</li>
<li>• 重要消息不错过</li>
</ul>
</div>
<div class="dialog-footer">
<button class="btn-cancel" @click="handleCancel">暂不开启</button>
<button class="btn-confirm" @click="handleConfirm">立即开启</button>
</div>
</div>
</div>
</template>
<script>
import pushInstance from '@/utils/push'
export default {
name: 'PushPermission',
data() {
return {
showPermissionDialog: false,
hasRequested: false
}
},
mounted() {
this.checkAndShowPermission()
},
methods: {
async checkAndShowPermission() {
// 检查是否已经请求过权限
const requested = localStorage.getItem('push_permission_requested')
if (requested) {
this.hasRequested = true
return
}
// 检查当前环境是否支持推送
const isSupported = await pushInstance.checkPermission()
if (!isSupported) {
// 延迟显示权限请求对话框
setTimeout(() => {
this.showPermissionDialog = true
}, 2000)
}
},
async handleConfirm() {
try {
await pushInstance.requestPermission()
this.showPermissionDialog = false
localStorage.setItem('push_permission_requested', 'true')
// 显示成功提示
this.$toast.success('推送权限已开启')
// 触发权限开启事件
this.$emit('permission-granted')
} catch (error) {
console.error('请求推送权限失败:', error)
this.$toast.fail('推送权限开启失败')
}
},
handleCancel() {
this.showPermissionDialog = false
localStorage.setItem('push_permission_requested', 'true')
this.$emit('permission-denied')
}
}
}
</script>
<style scoped>
.push-permission-dialog {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
}
.dialog-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
}
.dialog-content {
position: relative;
width: 80%;
max-width: 300px;
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.dialog-header {
padding: 20px 20px 10px;
text-align: center;
border-bottom: 1px solid #f0f0f0;
}
.dialog-header h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #333;
}
.dialog-body {
padding: 20px;
}
.dialog-body p {
margin: 0 0 15px 0;
font-size: 14px;
color: #666;
line-height: 1.5;
}
.benefits {
margin: 0;
padding: 0;
list-style: none;
}
.benefits li {
font-size: 13px;
color: #888;
margin-bottom: 5px;
}
.dialog-footer {
display: flex;
border-top: 1px solid #f0f0f0;
}
.btn-cancel,
.btn-confirm {
flex: 1;
padding: 15px;
border: none;
background: none;
font-size: 16px;
cursor: pointer;
transition: background-color 0.2s;
}
.btn-cancel {
color: #999;
border-right: 1px solid #f0f0f0;
}
.btn-cancel:hover {
background: #f5f5f5;
}
.btn-confirm {
color: #007aff;
font-weight: 600;
}
.btn-confirm:hover {
background: #f0f8ff;
}
</style>
\ No newline at end of file
<template>
<view class="webview-container">
<!-- 加载指示器 -->
<view v-if="loading" class="loading">
<text>加载中...</text>
</view>
<!-- Webview 组件 -->
<web-view
:src="webviewUrl"
@load="onLoad"
@error="onError"
class="webview"
></web-view>
<!-- 错误提示 -->
<view v-if="error" class="error">
<text>页面加载失败,请检查网络连接</text>
<button @click="reload" class="reload-btn">重新加载</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
loading: true,
error: false,
// 指定要加载的 H5 地址
webviewUrl: 'https://your-domain.com/bcdh-app/', // 替换为您的 H5 地址
}
},
onLoad(options) {
// 可以从参数中动态设置 H5 地址
if (options.url) {
this.webviewUrl = decodeURIComponent(options.url)
}
// 设置页面标题
uni.setNavigationBarTitle({
title: 'bcdh应用'
})
},
methods: {
onLoad() {
this.loading = false
this.error = false
console.log('H5页面加载完成')
},
onError() {
this.loading = false
this.error = true
console.error('H5页面加载失败')
},
reload() {
this.loading = true
this.error = false
// 重新加载页面
location.reload()
}
}
}
</script>
<style scoped>
.webview-container {
width: 100%;
height: 100vh;
position: relative;
}
.webview {
width: 100%;
height: 100%;
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: #666;
}
.error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: #f56c6c;
}
.reload-btn {
margin-top: 20px;
background-color: #007aff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
}
</style>
\ No newline at end of file
import axios from '@/utils/axios'
class PushService {
// 注册设备
async registerDevice(deviceInfo) {
try {
const response = await axios.post('/app-api/push/register', deviceInfo)
return response.data
} catch (error) {
console.error('设备注册失败:', error)
throw error
}
}
// 注销设备
async unregisterDevice(clientId) {
try {
const response = await axios.post('/app-api/push/unregister', { clientId })
return response.data
} catch (error) {
console.error('设备注销失败:', error)
throw error
}
}
// 更新设备标签
async updateDeviceTags(clientId, tags) {
try {
const response = await axios.post('/app-api/push/tags', {
clientId,
tags
})
return response.data
} catch (error) {
console.error('更新设备标签失败:', error)
throw error
}
}
// 发送测试推送
async sendTestPush(clientId, message) {
try {
const response = await axios.post('/app-api/push/test', {
clientId,
title: message.title || '测试推送',
content: message.content || '这是一条测试推送消息',
payload: message.payload || {}
})
return response.data
} catch (error) {
console.error('发送测试推送失败:', error)
throw error
}
}
// 获取推送历史
async getPushHistory(userId, page = 1, size = 20) {
try {
const response = await axios.get('/app-api/push/history', {
params: { userId, page, size }
})
return response.data
} catch (error) {
console.error('获取推送历史失败:', error)
throw error
}
}
// 标记消息为已读
async markAsRead(messageId) {
try {
const response = await axios.post('/app-api/push/mark-read', { messageId })
return response.data
} catch (error) {
console.error('标记消息已读失败:', error)
throw error
}
}
// 获取未读消息数量
async getUnreadCount(userId) {
try {
const response = await axios.get('/app-api/push/unread-count', {
params: { userId }
})
return response.data
} catch (error) {
console.error('获取未读消息数量失败:', error)
throw error
}
}
}
export default new PushService()
\ No newline at end of file
import { getCurrentInstance } from 'vue'
class UniPush {
constructor() {
this.isUniApp = typeof uni !== 'undefined'
this.clientId = null
this.pushEnabled = false
this.forceNotificationMode = false // 是否使用 force_notification:true 模式
this.init()
}
async init() {
if (!this.isUniApp) {
console.warn('当前环境不是uni-app,无法使用uni-push功能')
return
}
try {
// 获取客户端标识
const info = await this.getClientInfo()
uni.showModal({
title: 'uni-push客户端ID',
content:JSON.stringify(info),
showCancel: false
})
this.clientId = info.clientId
console.log('uni-push客户端ID:', this.clientId)
// 监听推送消息
this.setupPushListener()
this.pushEnabled = true
} catch (error) {
console.error('uni-push初始化失败:', error)
}
}
// 获取客户端信息
getClientInfo() {
return new Promise((resolve, reject) => {
if (!this.isUniApp) {
reject(new Error('非uni-app环境'))
return
}
uni.getPushClientId({
success: (res) => {
resolve({
clientId: res.cid
})
},
fail: (err) => {
reject(err)
}
})
})
}
// 设置推送监听
setupPushListener() {
if (!this.isUniApp) return
// 统一监听推送消息(适配 force_notification:true 模式)
uni.onPushMessage((res) => {
console.log('推送消息类型:', res.type, '内容:', res)
if (res.type === 'click') {
// 用户点击通知栏消息(在线/离线都会触发)
console.log('用户点击了推送消息:', res)
this.handlePushClick(res)
} else if (res.type === 'receive') {
// 应用在线时接收到的消息(不使用 force_notification:true 时触发)
console.log('应用在线收到推送消息:', res)
this.handlePushMessage(res)
}
})
}
// 处理推送消息
handlePushMessage(message) {
console.log('处理推送消息(使用 force_notification:true 时在线不会触发):', message)
// 注意:当后端使用 force_notification:true 时,应用在线时不会触发此方法
// 消息会直接显示在通知栏,用户点击后才会触发 handlePushClick
// 如果不使用 force_notification:true,可以在此处显示本地通知
if (!this.forceNotificationMode && this.isUniApp && message.data) {
uni.showNotification({
title: message.data.title || '新消息',
content: message.data.content || '您有一条新消息',
payload: message.data
})
}
// 触发自定义事件
if (typeof this.onMessage === 'function') {
this.onMessage(message)
}
}
// 处理推送点击
handlePushClick(message) {
// 根据消息内容跳转到对应页面
if (message.data && message.data.page) {
// this.navigateToPage(message.data.page, message.data.params)
}
// 触发自定义点击事件
if (typeof this.onClick === 'function') {
this.onClick(message)
}
}
// 页面跳转
navigateToPage(page, params = {}) {
if (!this.isUniApp) return
const pages = {
'home': '/pages/index/index',
'message': '/pages/message/message',
'workbench': '/pages/workbench/workbench'
}
const path = pages[page] || '/pages/index/index'
uni.navigateTo({
url: path + '?' + new URLSearchParams(params).toString()
})
}
// 检查推送权限
checkPermission() {
return new Promise((resolve) => {
if (!this.isUniApp) {
resolve(false)
return
}
uni.getPushProvider({
success: (res) => {
resolve(res.provider !== 'none')
},
fail: () => {
resolve(false)
}
})
})
}
// 请求推送权限
requestPermission() {
return new Promise((resolve, reject) => {
if (!this.isUniApp) {
reject(new Error('非uni-app环境'))
return
}
uni.requestPushPermission({
success: (res) => {
resolve(res)
},
fail: (err) => {
reject(err)
}
})
})
}
// 设置推送标签
setTags(tags) {
return new Promise((resolve, reject) => {
if (!this.isUniApp) {
reject(new Error('非uni-app环境'))
return
}
uni.setPushTags({
tags: tags,
success: (res) => {
resolve(res)
},
fail: (err) => {
reject(err)
}
})
})
}
// 获取推送标签
getTags() {
return new Promise((resolve, reject) => {
if (!this.isUniApp) {
reject(new Error('非uni-app环境'))
return
}
uni.getPushTags({
success: (res) => {
resolve(res.tags)
},
fail: (err) => {
reject(err)
}
})
})
}
// 设置 force_notification 模式
setForceNotificationMode(enabled) {
this.forceNotificationMode = enabled
console.log(`force_notification 模式: ${enabled ? '已启用' : '已禁用'}`)
}
// 销毁推送实例
destroy() {
if (this.isUniApp) {
uni.offPushMessage()
}
this.pushEnabled = false
}
}
// 创建全局推送实例
const pushInstance = new UniPush()
export default pushInstance
\ No newline at end of file
...@@ -13,36 +13,24 @@ ...@@ -13,36 +13,24 @@
欢迎登录<span>首开集团安全风险和隐患排查治理体系</span>! 欢迎登录<span>首开集团安全风险和隐患排查治理体系</span>!
</div> </div>
<div class="login-form"> <div class="login-form">
<van-form @submit="onSubmit" :show-error-message="false" > <van-form @submit="onSubmit" :show-error-message="false">
<div class="username-wrap"> <div class="username-wrap">
<div class="username-icon"> <div class="username-icon">
<van-image :src="require('@/assets/login/login-username.png')" /> <van-image :src="require('@/assets/login/login-username.png')" />
</div> </div>
<van-field <van-field v-model="username" name="username" label="" placeholder="账号"
v-model="username" :rules="[{ required: true, message: '请填写账号' }]" />
name="username"
label=""
placeholder="账号"
:rules="[{ required: true, message: '请填写账号' }]"
/>
</div> </div>
<div class="passworld-wrap"> <div class="passworld-wrap">
<div class="passworld-icon"> <div class="passworld-icon">
<van-image :src="require('@/assets/login/login-passworld.png')" /> <van-image :src="require('@/assets/login/login-passworld.png')" />
</div> </div>
<van-field <van-field v-model="password" type="password" name="password" placeholder="密码"
v-model="password" :rules="[{ required: true, message: '请填写密码' }]" />
type="password"
name="password"
placeholder="密码"
:rules="[{ required: true, message: '请填写密码' }]"
/>
</div> </div>
<div style="margin: 16px;"> <div style="margin: 16px;">
<van-button round block type="info" native-type="submit" <van-button round block type="info" native-type="submit">登录</van-button>
>登录</van-button
>
</div> </div>
</van-form> </van-form>
</div> </div>
...@@ -51,11 +39,12 @@ ...@@ -51,11 +39,12 @@
</template> </template>
<script> <script>
import { getFun,postFun } from '@/service/table' import { getFun, postFun } from '@/service/table'
import { setToken } from '@/utils/auth' import { setToken } from '@/utils/auth'
import { encrypt, decrypt } from '@/utils/jsencrypt' import { encrypt, decrypt } from '@/utils/jsencrypt'
import { crypto } from '@/utils/crypto' import { crypto } from '@/utils/crypto'
import { setUserInfo ,getUserInfo} from '@/utils/userInfo' import { setUserInfo, getUserInfo } from '@/utils/userInfo'
import pushInstance from '@/utils/push'
export default { export default {
data() { data() {
return { return {
...@@ -63,28 +52,42 @@ export default { ...@@ -63,28 +52,42 @@ export default {
conBg: require("@/assets/login/login-center.png"), conBg: require("@/assets/login/login-center.png"),
username: "", username: "",
password: "", password: "",
obj:{} obj: {},
clientId: null
}; };
}, },
mounted() { mounted() {
if(localStorage.getItem('pwd')){ if (localStorage.getItem('pwd')) {
this.password=decrypt(localStorage.getItem('pwd')) this.password = decrypt(localStorage.getItem('pwd'))
this.username=localStorage.getItem('username') this.username = localStorage.getItem('username')
}
if (pushInstance.clientId) {
this.clientId = pushInstance.clientId;
uni.showModal({
title: '客户端ID: ' + this.clientId,
success(res) {
if (res.confirm) {
console.log('用户点击确定')
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
} }
}, },
created() { created() {
// 扩展API是否准备好,如果没有则监听“plusready"事件 // 扩展API是否准备好,如果没有则监听“plusready"事件
if (window.plus) { if (window.plus) {
this.plusReady() this.plusReady()
} else { } else {
document.addEventListener('plusready', this.plusReady, false) document.addEventListener('plusready', this.plusReady, false)
} }
}, },
methods: { methods: {
// 扩展API准备完成后要执行的操作 // 扩展API准备完成后要执行的操作
plusReady () { plusReady() {
// var ws = plus.webview.currentWebview(); //pw回车可输出plus.webview // var ws = plus.webview.currentWebview(); //pw回车可输出plus.webview
var barcode = plus.barcode.create('barcode',"aaa", { var barcode = plus.barcode.create('barcode', "aaa", {
top: '-9999px', //改为-9999px隐藏该页面 top: '-9999px', //改为-9999px隐藏该页面
left: '0', left: '0',
width: '100%', width: '100%',
...@@ -93,53 +96,53 @@ export default { ...@@ -93,53 +96,53 @@ export default {
}); });
plus.webview.currentWebview().append(barcode); plus.webview.currentWebview().append(barcode);
//barcode.start();开始扫码识别(我们把这句代码注释了,因为我们不需要扫描任何东西) //barcode.start();开始扫码识别(我们把这句代码注释了,因为我们不需要扫描任何东西)
}, },
onSubmit(values) { onSubmit(values) {
var obj={ var obj = {
username:this.username, username: this.username,
password:this.password, password: this.password,
} }
this.$toast.loading({ this.$toast.loading({
message:'登录中...', message: '登录中...',
forbidClick: true, forbidClick: true,
loadingType: 'spinner', loadingType: 'spinner',
duration: 0 duration: 0
}) })
getFun('check/token',obj).then((Response)=>{ getFun('check/token', obj).then((Response) => {
if(Response.code==0){ if (Response.code == 0) {
this.$toast.clear() this.$toast.clear()
setToken(Response.data) setToken(Response.data)
postFun('mobile/user').then((Response2)=>{ postFun('mobile/user').then((Response2) => {
var userInfo={ var userInfo = {
avatar:Response2.data.avatar, avatar: Response2.data.avatar,
userName:Response2.data.userName, userName: Response2.data.userName,
deptName:Response2.data.dept.deptName, deptName: Response2.data.dept.deptName,
organizationId:Response2.data.organizationId, organizationId: Response2.data.organizationId,
organizationName:Response2.data.organizationName, organizationName: Response2.data.organizationName,
userId:Response2.data.userId, userId: Response2.data.userId,
roles:Response2.data.roles roles: Response2.data.roles
}
/* 存储用户信息 */
setUserInfo(userInfo)
localStorage.setItem('pwd',encrypt(this.password))
localStorage.setItem('username',this.username)
if(!Response2.data.initializePassword){
this.$router.push('/save-workbench')
}else{
this.$router.push('/reset-pas-two')
}
})
}else if(Response.code==301){
this.$toast.clear()
this.$toast.fail({
message:Response.msg,
duration: 2000
})
} }
}) /* 存储用户信息 */
setUserInfo(userInfo)
localStorage.setItem('pwd', encrypt(this.password))
localStorage.setItem('username', this.username)
if (!Response2.data.initializePassword) {
this.$router.push('/save-workbench')
} else {
this.$router.push('/reset-pas-two')
}
})
} else if (Response.code == 301) {
this.$toast.clear()
this.$toast.fail({
message: Response.msg,
duration: 2000
})
}
})
} }
} }
}; };
...@@ -151,10 +154,12 @@ export default { ...@@ -151,10 +154,12 @@ export default {
background-repeat: no-repeat; background-repeat: no-repeat;
// background-position: center; // background-position: center;
background-attachment: fixed; background-attachment: fixed;
.login-logo { .login-logo {
width: 120px; width: 120px;
margin: 22% auto 0px; margin: 22% auto 0px;
} }
.login-name { .login-name {
width: 100%; width: 100%;
text-align: center; text-align: center;
...@@ -163,6 +168,7 @@ export default { ...@@ -163,6 +168,7 @@ export default {
font-size: 18px; font-size: 18px;
font-weight: none; font-weight: none;
} }
.con { .con {
width: 345px; width: 345px;
height: 340px; height: 340px;
...@@ -171,22 +177,27 @@ export default { ...@@ -171,22 +177,27 @@ export default {
box-sizing: border-box; box-sizing: border-box;
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
.hello { .hello {
color: #004fb1; color: #004fb1;
font-size: 20px; font-size: 20px;
font-weight: none; font-weight: none;
} }
.welcome { .welcome {
margin: 15px 0; margin: 15px 0;
color: #7795e0; color: #7795e0;
span { span {
color: #71aaf2; color: #71aaf2;
} }
} }
.login-form { .login-form {
.username-wrap { .username-wrap {
position: relative; position: relative;
border-bottom: 1px solid #d2dcf5; border-bottom: 1px solid #d2dcf5;
.username-icon { .username-icon {
width: 20px; width: 20px;
position: absolute; position: absolute;
...@@ -194,17 +205,21 @@ export default { ...@@ -194,17 +205,21 @@ export default {
top: 9px; top: 9px;
z-index: 100; z-index: 100;
} }
/deep/ .van-field__body { /deep/ .van-field__body {
padding-left: 20px; padding-left: 20px;
box-sizing: border-box; box-sizing: border-box;
} }
/deep/ .van-field__error-message { /deep/ .van-field__error-message {
padding-left: 20px; padding-left: 20px;
} }
} }
.passworld-wrap { .passworld-wrap {
position: relative; position: relative;
border-bottom: 1px solid #d2dcf5; border-bottom: 1px solid #d2dcf5;
.passworld-icon { .passworld-icon {
width: 20px; width: 20px;
position: absolute; position: absolute;
...@@ -212,10 +227,12 @@ export default { ...@@ -212,10 +227,12 @@ export default {
top: 9px; top: 9px;
z-index: 100; z-index: 100;
} }
/deep/ .van-field__body { /deep/ .van-field__body {
padding-left: 20px; padding-left: 20px;
box-sizing: border-box; box-sizing: border-box;
} }
/deep/ .van-field__error-message { /deep/ .van-field__error-message {
padding-left: 20px; padding-left: 20px;
} }
......
<template>
<div class="push-test">
<van-nav-bar
title="推送测试"
left-text="返回"
left-arrow
@click-left="$router.back()"
/>
<div class="content">
<van-cell-group title="推送状态">
<van-cell title="推送支持" :value="pushSupported ? '支持' : '不支持'" />
<van-cell title="推送启用" :value="pushEnabled ? '已启用' : '未启用'" />
<van-cell title="客户端ID" :value="clientId || '未获取'" />
<van-cell title="force_notification模式" :value="forceNotificationMode ? '已启用' : '已禁用'" />
</van-cell-group>
<van-cell-group title="推送操作" style="margin-top: 20px;">
<van-cell title="请求推送权限" is-link @click="requestPermission" />
<van-cell title="检查推送权限" is-link @click="checkPermission" />
<van-cell title="设置推送标签" is-link @click="showTagDialog = true" />
<van-cell title="发送测试推送" is-link @click="sendTestPush" />
<van-cell
:title="forceNotificationMode ? '禁用force_notification模式' : '启用force_notification模式'"
is-link
@click="toggleForceNotificationMode"
/>
</van-cell-group>
<van-cell-group title="推送历史" style="margin-top: 20px;">
<van-cell
v-for="(message, index) in pushHistory"
:key="index"
:title="message.title"
:value="message.time"
@click="viewMessageDetail(message)"
/>
</van-cell-group>
</div>
<!-- 标签设置对话框 -->
<van-dialog
v-model:show="showTagDialog"
title="设置推送标签"
show-cancel-button
@confirm="setTags"
>
<div class="tag-dialog">
<van-field
v-model="tagInput"
label="标签"
placeholder="请输入标签,多个标签用逗号分隔"
/>
<div class="current-tags">
<span>当前标签:</span>
<van-tag
v-for="(tag, index) in currentTags"
:key="index"
type="primary"
style="margin-right: 5px;"
>
{{ tag }}
</van-tag>
</div>
</div>
</van-dialog>
</div>
</template>
<script>
import pushInstance from '@/utils/push'
import pushService from '@/service/push'
import { Toast } from 'vant'
export default {
name: 'PushTest',
data() {
return {
pushSupported: false,
pushEnabled: false,
clientId: null,
forceNotificationMode: false,
showTagDialog: false,
tagInput: '',
currentTags: [],
pushHistory: [
{ title: '测试推送消息1', time: '2024-01-01 10:00', content: '这是一条测试推送消息' },
{ title: '测试推送消息2', time: '2024-01-01 09:00', content: '这是另一条测试推送消息' }
]
}
},
async mounted() {
await this.initPushInfo()
},
methods: {
async initPushInfo() {
this.pushSupported = await pushInstance.checkPermission()
this.pushEnabled = pushInstance.pushEnabled
this.forceNotificationMode = pushInstance.forceNotificationMode || false
if (pushInstance.clientId) {
this.clientId = pushInstance.clientId
}
// 获取当前标签
try {
this.currentTags = await pushInstance.getTags() || []
} catch (error) {
console.error('获取标签失败:', error)
}
},
async requestPermission() {
try {
await pushInstance.requestPermission()
Toast.success('推送权限请求成功')
await this.initPushInfo()
} catch (error) {
Toast.fail('推送权限请求失败')
console.error('请求推送权限失败:', error)
}
},
async checkPermission() {
const hasPermission = await pushInstance.checkPermission()
Toast(hasPermission ? '已开启推送权限' : '未开启推送权限')
},
async setTags() {
if (!this.tagInput.trim()) {
Toast.fail('请输入标签')
return
}
const tags = this.tagInput.split(',').map(tag => tag.trim()).filter(tag => tag)
try {
await pushInstance.setTags(tags)
Toast.success('标签设置成功')
this.currentTags = tags
this.tagInput = ''
this.showTagDialog = false
} catch (error) {
Toast.fail('标签设置失败')
console.error('设置标签失败:', error)
}
},
async sendTestPush() {
if (!this.clientId) {
Toast.fail('请先获取客户端ID')
return
}
try {
const result = await pushService.sendTestPush(this.clientId, {
title: '测试推送',
content: '这是一条测试推送消息',
payload: {
page: 'message',
type: 'test'
}
})
Toast.success('测试推送发送成功')
console.log('推送结果:', result)
} catch (error) {
Toast.fail('测试推送发送失败')
console.error('发送测试推送失败:', error)
}
},
viewMessageDetail(message) {
this.$dialog.alert({
title: message.title,
message: message.content
})
},
// 切换 force_notification 模式
toggleForceNotificationMode() {
this.forceNotificationMode = !this.forceNotificationMode
pushInstance.setForceNotificationMode(this.forceNotificationMode)
Toast.success(`force_notification 模式 ${this.forceNotificationMode ? '已启用' : '已禁用'}`)
// 显示模式说明
if (this.forceNotificationMode) {
this.$dialog.alert({
title: 'force_notification 模式已启用',
message: '在此模式下:\n• 应用在线时推送会直接显示通知栏\n• 应用在线时不会触发消息接收监听器\n• 用户点击通知后才会触发消息处理'
})
}
}
}
}
</script>
<style scoped>
.push-test {
min-height: 100vh;
background-color: #f5f5f5;
}
.content {
padding: 16px;
}
.tag-dialog {
padding: 16px;
}
.current-tags {
margin-top: 16px;
font-size: 14px;
color: #666;
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment