# UniApp
# 开发者中心
用于注册应用以及申请对应证书
https://dev.dcloud.net.cn/pages/app/list
https://blog.csdn.net/fred_kang/article/details/124988303
下载证书后,获取 SHA1 关键 cmd
keytool -list -v -keystore test.keystore | |
Enter keystore password: //输入密码,回车 |
# 解决 h5 跨域问题
通过 manifest.json 里的 h5 配置来解决跨域问题 (注:如果要部署到服务器仍然需要配置 nginx)
"h5": { | |
"router": { | |
"mode": "hash" | |
}, | |
"devServer": { | |
"port": 8080, | |
"disableHostCheck": true, | |
"proxy": { | |
"/": { | |
"target": "http://localhost:3000", | |
"changeOrigin": true, | |
"secure": false | |
} | |
} | |
} | |
} |
# app 文件无法上传非媒体类文件问题
参考文档
核心思路:使用 renderjs,uniapp 自带的
完整样例
<template> | |
<button type="primary" size="mini" @tap="attchChoose.onClick">选择文件</button> | |
</template> | |
<script> | |
import { | |
API_SITE | |
} from '@/config/config' | |
import pop from '@/util/pop' | |
import { | |
uploadFile | |
} from '@/common/request.js' | |
export default { | |
data() { | |
return { | |
} | |
}, | |
methods: { | |
async upload(path) { | |
try { | |
// pop.showLoading() | |
// 参数一:本地路径,参数二:后端对应字段名,我这里只是进行了简单的封装 | |
const result = await uploadFile(path,"files"); | |
console.log(result); | |
//pop.showToast ("上传成功") | |
} catch (e) { | |
console.log(e); | |
// pop.showToast(e) | |
} | |
}, | |
async chooseFile(data) { | |
try { | |
const fileUrl = await this.base64toPath(data.base64Str, data.attachName); | |
this.upload(fileUrl.localAbsolutePath) | |
} catch (e) { | |
console.log("err", e); | |
} | |
}, | |
/** | |
* @param {Object} base64 文件 base64 | |
* @param {Object} attachName // 文件名需要后缀,如:张三.jpg | |
*/ | |
async base64toPath(base64, attachName) { | |
let _that = this; | |
return new Promise(function(resolve, reject) { | |
const filePath = `_doc/yourFilePath/${attachName}`; | |
plus.io.resolveLocalFileSystemURL('_doc', function(entry) { | |
entry.getDirectory("yourFilePath", { | |
create: true, | |
exclusive: false, | |
}, function(entry) { | |
entry.getFile(attachName, { | |
create: true, | |
exclusive: false, | |
}, function(entry) { | |
entry.createWriter(function(writer) { | |
writer.onwrite = function(res) { | |
const obj = { | |
relativePath: filePath, | |
localAbsolutePath: plus.io | |
.convertLocalFileSystemURL( | |
filePath) | |
} | |
resolve(obj); | |
} | |
writer.onerror = reject; | |
writer.seek(0); | |
writer.writeAsBinary(_that | |
.getSymbolAfterString(base64, | |
',')); | |
}, reject) | |
}, reject) | |
}, reject) | |
}, reject) | |
}) | |
}, | |
// 取某个符号后面的字符 | |
getSymbolAfterString(val, symbolStr) { | |
if (val == undefined || val == null || val == "") { | |
return ""; | |
} | |
val = val.toString(); | |
const index = val.indexOf(symbolStr); | |
if (index != -1) { | |
val = val.substring(index + 1, val.length); | |
return val; | |
} else { | |
return val | |
} | |
} | |
} | |
} | |
</script> | |
<script module="attchChoose" lang="renderjs"> | |
let fileInputDom = null; | |
export default { | |
methods: { | |
createFileInputDom() { | |
fileInputDom = document.createElement("input"); | |
fileInputDom.setAttribute('type', 'file'); | |
fileInputDom.setAttribute('accept', '*'); | |
}, | |
onClick(event, ownerInstance) { | |
if (!fileInputDom) { | |
this.createFileInputDom(); | |
} | |
fileInputDom.click(); // 模拟 click | |
fileInputDom.addEventListener('change', (e) => { | |
fileInputDom = null; | |
let choicesFiles = e.target.files[0]; | |
let reader = new FileReader(); | |
// 读取图像文件 result 为 DataURL, DataURL 可直接 赋值给 img.src | |
reader.readAsDataURL(choicesFiles); | |
reader.onload = function(event) { | |
const base64Str = event.target.result; // 文件的 base64 | |
ownerInstance.callMethod('chooseFile', { | |
attachName: choicesFiles.name, | |
size: choicesFiles.size, | |
base64Str, | |
}) | |
} | |
e.target.value = ""; | |
}) | |
} | |
} | |
} | |
</script> |
# 本地打包
生成 key 的方法
keytool -genkey -alias costmgr -keyalg RSA -keysize 2048 -validity 36500 -keystore costmgr.keystore |
查看内容
keytool -list -v -keystore costmgr.keystore |
不推荐,虽然打包很快,但很多设置需要加入,还有对应 sdk(消息推送各个厂商 sdk,uniapp 自己的 sdk 等)
# H5 打包
需要 nginx 进行代理 api 接口
nginx 修改配置
server { | |
listen 80; | |
server_name obsidianlyg.top; | |
location / { | |
root /root/mobile/html; | |
index index.html index.htm; | |
} | |
location /rest/ { | |
proxy_pass http://obsidianlyg.top/rest/; | |
} | |
} |
web 文件修改,需要将 api 接口改为相对 url
以当前 nginx 配置为例:
这里仅做参考,后期可以直接放入 store 中通过 uniapp 独有的 #ifdef H5 注释可以自动判别
// 封装请求方法 | |
function request(url, method, data) { | |
return new Promise((resolve, reject) => { | |
uni.request({ | |
// app | |
// url: 'https://obsidianlyg.top' + url, | |
// 网页专属 | |
url: url, | |
method: method, | |
data: data, | |
header: { | |
'Content-Type': 'application/json', | |
}, | |
success: (res) => { | |
if (res.statusCode === 200) { | |
resolve(res.data); | |
} else { | |
reject(res); | |
} | |
}, | |
fail: (err) => { | |
reject(err); | |
}, | |
}); | |
}); | |
} |
# App 远程打包
命令行打包
# 消息发送
# 本地消息发送
// 开启 Socket | |
let socket; | |
export function connectWebSocket(empId) { | |
socket = uni.connectSocket({ | |
url: 'ws://obsidianlyg.top/ws/' + empId, | |
complete: () => {} | |
}); | |
socket.onOpen(() => { | |
console.log('WebSocket连接已打开'); | |
}); | |
socket.onMessage((res) => { | |
console.log('收到消息:', res.data); | |
sendNotification('新消息', res.data); | |
}); | |
socket.onClose(() => { | |
console.log('WebSocket连接已关闭'); | |
// 可以在这里实现重连逻辑 | |
}); | |
socket.onError((err) => { | |
console.error('WebSocket连接错误:', err); | |
}); | |
} | |
function sendNotification(title, message) { | |
uni.createPushMessage({ | |
title: title, | |
content: message, | |
success: function (res) { | |
console.log('推送消息发送成功', res); | |
}, | |
fail: function (err) { | |
console.error('推送消息发送失败', err); | |
} | |
}); | |
} | |
// 在页面加载时调用 connectWebSocket | |
// connectWebSocket(); |
# 消息推送
采用 uni-cloud-push,实际也是使用的 WebSocket,接入云端,app 离线后仍然接收不到消息,但是再次打开 app 后可以接收到原来发送的消息
需要申请各个手机品牌的 appid 接入后才能实现离线接收消息
参考文档
实现云函数代码
'use strict';
const uniPush = uniCloud.getPushManager({appId:"__UNI__A0D029F"})
exports.main = async (event) => {
let obj = JSON.parse(event.body) //这是重点 解析json字符串
const res = await uniPush.sendMessage({
"push_clientid": obj.cids, // 设备id,支持多个以数组的形式指定多个设备,如["cid-1","cid-2"],数组长度不大于1000
"title": obj.title, // 标题
"content": obj.content, // 内容
"payload": obj.data, // 数据
"force_notification": true, // true 自动创建通知栏,没有回调;false 无通知栏,触发onMessage回调
"request_id": obj.request_id ,//请求唯一标识号,10-32位之间;如果request_id重复,会导致消息丢失
"options":obj.options //消息分类,没申请可以不传这个参数
})
return res;
};
# 设置手机通知权限提示
setPermissions, 这个方法可以放入 onLaunch,但是其下方不能放入其他方法,会被阻止,所以需要将对应方法加入到 created 中(虽然可以放到上方,但是测试发现使用 uni 自带 api 获取远端信息的方法加入后,setPermissions 无法正常运行)
// 设置手机通知权限 | |
setPermissions() { | |
let bool = uni.getStorageSync("notificationStatus"); | |
// #ifdef APP-PLUS | |
if (plus.os.name == 'Android') { // 判断是 Android | |
var main = plus.android.runtimeMainActivity(); | |
var pkName = main.getPackageName(); | |
var uid = main.getApplicationInfo().plusGetAttribute("uid"); | |
var NotificationManagerCompat = plus.android.importClass("android.support.v4.app.NotificationManagerCompat"); | |
//android.support.v4 升级为 androidx | |
if (NotificationManagerCompat == null) { | |
NotificationManagerCompat = plus.android.importClass("androidx.core.app.NotificationManagerCompat"); | |
} | |
var areNotificationsEnabled = NotificationManagerCompat.from(main).areNotificationsEnabled(); | |
// 未开通‘允许通知’权限,则弹窗提醒开通,并点击确认后,跳转到系统设置页面进行设置 | |
if (!areNotificationsEnabled && !bool) { | |
uni.showModal({ | |
title: '通知权限开启提醒', | |
content: '您还没有开启通知权限,无法接受到消息通知,请前往设置!', | |
// showCancel: false, | |
confirmText: '去设置', | |
success: function(res) { | |
if (res.confirm) { | |
var Intent = plus.android.importClass('android.content.Intent'); | |
var Build = plus.android.importClass("android.os.Build"); | |
//android 8.0 引导 | |
if (Build.VERSION.SDK_INT >= 26) { | |
var intent = new Intent('android.settings.APP_NOTIFICATION_SETTINGS'); | |
intent.putExtra('android.provider.extra.APP_PACKAGE', pkName); | |
} else if (Build.VERSION.SDK_INT >= 21) { //android 5.0-7.0 | |
var intent = new Intent('android.settings.APP_NOTIFICATION_SETTINGS'); | |
intent.putExtra("app_package", pkName); | |
intent.putExtra("app_uid", uid); | |
} else { //(<21) 其他 -- 跳转到该应用管理的详情页 | |
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); | |
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null); | |
intent.setData(uri); | |
} | |
// 跳转到该应用的系统通知设置页 | |
main.startActivity(intent); | |
} else { | |
console.log(res,"cancel"); | |
uni.setStorageSync("notificationStatus", false); | |
} | |
} | |
}); | |
} | |
} else if (plus.os.name == 'iOS') { // 判断是 ISO | |
var isOn = undefined; | |
var types = 0; | |
var app = plus.ios.invoke('UIApplication', 'sharedApplication'); | |
var settings = plus.ios.invoke(app, 'currentUserNotificationSettings'); | |
if (settings) { | |
types = settings.plusGetAttribute('types'); | |
plus.ios.deleteObject(settings); | |
} else { | |
types = plus.ios.invoke(app, 'enabledRemoteNotificationTypes'); | |
} | |
plus.ios.deleteObject(app); | |
isOn = (0 != types); | |
if (isOn == false && !bool) { | |
uni.showModal({ | |
title: '通知权限开启提醒', | |
content: '您还没有开启通知权限,无法接受到消息通知,请前往设置!', | |
// showCancel: false, | |
confirmText: '去设置', | |
success: function(res) { | |
if (res.confirm) { | |
var app = plus.ios.invoke('UIApplication', 'sharedApplication'); | |
var setting = plus.ios.invoke('NSURL', 'URLWithString:', 'app-settings:'); | |
plus.ios.invoke(app, 'openURL:', setting); | |
plus.ios.deleteObject(setting); | |
plus.ios.deleteObject(app); | |
} else { | |
uni.setStorageSync("notificationStatus", false); | |
} | |
} | |
}); | |
} | |
} | |
// #endif | |
}, |
# 清除缓存
- 移除指定缓存
uni.removeStorageSync("key值");
- 清空所有缓存
uni.clearStorageSync();