# 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  
			},

# 清除缓存

  1. 移除指定缓存
uni.removeStorageSync("key值"); 
  1. 清空所有缓存
uni.clearStorageSync();