首页
留言
导航
统计
Search
1
追番推荐!免费看动漫的网站 - 支持在线观看和磁力下载
2,510 阅读
2
推荐31个docker应用,每一个都很实用
1,311 阅读
3
PVE自动启动 虚拟机 | 容器 顺序设置及参数说明
931 阅读
4
一条命令,永久激活!Office 2024!
618 阅读
5
优选 Cloudflare 官方 / 中转 IP
490 阅读
默认分类
服务器
宝塔
VPS
Docker
OpenWRT
Nginx
群晖
前端编程
Vue
React
Angular
NodeJS
uni-app
后端编程
Java
Python
SpringBoot
SpringCloud
流程引擎
检索引擎
Linux
CentOS
Ubuntu
Debian
数据库
Redis
MySQL
Oracle
虚拟机
VMware
VirtualBox
PVE
Hyper-V
计算机
网络技术
网站源码
主题模板
登录
Search
标签搜索
Java
小程序
Redis
SpringBoot
docker
Typecho
Cloudflare
docker部署
虚拟机
WordPress
群晖
uni-app
CentOS
Vue
Java类库
Linux命令
防火墙配置
Mysql
脚本
Nginx
微醺
累计撰写
264
篇文章
累计收到
11
条评论
首页
栏目
默认分类
服务器
宝塔
VPS
Docker
OpenWRT
Nginx
群晖
前端编程
Vue
React
Angular
NodeJS
uni-app
后端编程
Java
Python
SpringBoot
SpringCloud
流程引擎
检索引擎
Linux
CentOS
Ubuntu
Debian
数据库
Redis
MySQL
Oracle
虚拟机
VMware
VirtualBox
PVE
Hyper-V
计算机
网络技术
网站源码
主题模板
页面
留言
导航
统计
搜索到
27
篇与
的结果
2023-02-15
VUE后台管理中使用富文本编辑器导入word 文档进行编辑
前言最近因业务需求在项目中嵌入了[tinymce]这个编辑器,用于满足平台给用户编辑各类新闻内容什么的业务需求,前后也花了不少时间体验和对比了市面上各类开源编辑器,直接将新闻部门的工作从半天工作量提升只要一个小时就可以搞到,一两分钟就可以搞定一个任务优点文档好,功能强,bug 少,无外部依赖。word 文档粘贴进来要带格式 兼容移动端 word 文档粘贴进来要正常显示并且还要兼容移动端 电脑网页里粘贴进来内容要正常显示并且排版还不能乱 电脑网页拷过来的内容还要兼容到移动端安装npm install tinymce @tinymce/tinymce-vue@3.2.8 -S下载语言包中文语言包下载开始操作文件将依赖包 node_modules 里找到 tinymce文件夹,复制到 public 里,【左图是 tinymce,右图是 public 复制后的目录】一下是注意点:很多的博客的写法是将 node_modules 里面的skins文件夹复制到public/tinymce目录下,经过尝试是不完善的,需要将整个目录倒入进 public 里不完全导入的错误显示,我还以为是语言包的问题,一个个语言包版本去试,这种展示一半英文一半中文的导入 tinymcejspublic/index.html 添加 tinymce.js<div id="app"></div> <script src="/tinymce/tinymce.min.js"></script>引入基本文件// 引入组件 import tinymce from 'tinymce/tinymce' import Editor from '@tinymce/tinymce-vue' // 引入富文本编辑器主题的js和css import 'tinymce/themes/silver/theme.min.js' import 'tinymce/skins/ui/oxide/skin.min.css' // 扩展插件 import 'tinymce/plugins/image' import 'tinymce/plugins/link' import 'tinymce/plugins/code' import 'tinymce/plugins/table' import 'tinymce/plugins/lists' import 'tinymce/plugins/wordcount'注册组件components: { Editor },使用组件<div class="activeConfig-container"> <Editor id="tinymce" v-model="tinymceHtml" :init="editorInit" /> </div>tinymce 初始化配置data() { return { // tinymce的绑定值 tinymceHtml: '', // tinymce的初始化配置 editorInit: { selector: '#tinymce', language_url: '/tinymce/langs/zh_CN.js', language: 'zh_CN', skin_url: '/tinymce/skins/ui/oxide', height: 400, plugins: 'link lists image code table wordcount importword', toolbar: 'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent blockquote | undo redo | link unlink image code | removeformat | importword', //点击富文本图片上传的时候将图片转成base64再通过success插入 images_upload_handler: (blobInfo, success) => { const img = 'data:image/jpeg;base64,' + blobInfo.base64() success(img) }, importword_filter: function(result, insert, message) { // console.log(result) // console.log(insert) // console.log(message) // 自定义操作部分 insert(result) // 回插函数 }, // statusbar: false // 是否隐藏底部的状态栏 // menubar: false, // 是否隐藏最上方的菜单 branding: false // 是否禁用“Powered by TinyMCE” } } },整体代码<template> <div class="activeConfig"> <div class="activeConfig-container"> <Editor id="tinymce" v-model="tinymceHtml" :init="editorInit" /> </div> </div> </template> <script> // 引入组件 import tinymce from 'tinymce/tinymce' import Editor from '@tinymce/tinymce-vue' // 引入富文本编辑器主题的js和css import 'tinymce/themes/silver/theme.min.js' import 'tinymce/skins/ui/oxide/skin.min.css' // 扩展插件 import 'tinymce/plugins/image' import 'tinymce/plugins/link' import 'tinymce/plugins/code' import 'tinymce/plugins/table' import 'tinymce/plugins/lists' import 'tinymce/plugins/wordcount' // import { uploadImgage } from '@/api/activeConfig' export default { name: 'ActiveConfig', components: { Editor }, data() { return { // tinymce的绑定值 tinymceHtml: '', // tinymce的初始化配置 editorInit: { selector: '#tinymce', language_url: '/tinymce/langs/zh_CN.js', language: 'zh_CN', skin_url: '/tinymce/skins/ui/oxide', height: 400, plugins: 'link lists image code table wordcount importword', toolbar: 'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent blockquote | undo redo | link unlink image code | removeformat | importword', // 此处为图片上传处理函数 // images_upload_handler: (blobInfo, success, failure) => { // this.handleImgUpload(blobInfo, success, failure) // }, images_upload_handler: (blobInfo, success) => { const img = 'data:image/jpeg;base64,' + blobInfo.base64() success(img) }, importword_filter: function(result, insert, message) { // console.log(result) // console.log(insert) // console.log(message) // 自定义操作部分 insert(result) // 回插函数 }, // statusbar: false // 是否隐藏底部的状态栏 // menubar: false, // 是否隐藏最上方的菜单 branding: false // 是否禁用“Powered by TinyMCE” } } }, mounted() { tinymce.init({}) }, methods: { // 图片上传 handleImgUpload(blobInfo, success, failure) { // this.baseUrl = process.env.VUE_APP_BASE_URL // const imgBase64 = 'data:image/jpeg;base64,' + blobInfo.base64() // const data = { img: [imgBase64] } // uploadImgage(data).then(res => { // // 传入success回调里的数据就是富文本编辑器里插入图片的src的值 // success(`${this.baseUrl}/${res.data[0]}`) // }).catch(() => { failure('error') }) } } } </script> <style lang="scss" scoped> .activeConfig { &-container { margin: 30px; } } </style>特殊需求需要找个能实现 word 文档上传到富文本编辑器,减轻编辑机构的负担,解决机构需要每次打开文档复制再编辑的繁琐工作,实现直接导入编辑tinymce 插件一个下载插件,一个是注意点,图片在富文本是 base64 的方式导入插件下载解压后将整个文件复制到 public 的插件目录再往 tinymce 配置添加即可editorInit.plugins 和 editorInit.toolbar 添加 importword 即可editorInit: { selector: '#tinymce', language_url: '/tinymce/langs/zh_CN.js', language: 'zh_CN', skin_url: '/tinymce/skins/ui/oxide', height: 400, plugins: 'link lists image code table wordcount importword', toolbar: 'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent blockquote | undo redo | link unlink image code | removeformat | importword' }图片解决方案如果碰到图片的问题,处理图片有两种方式: 1、一种在提交内容的时候统一处理图片,稿件文件里图片只要提交一次。 2、一种是监听图片上传,每次更新图片都上传图片。图片上传函数有images_upload_handler第二种const files = [] const base64ImgSum = [] for (let i = 0; i < imgs.length; i++) { //去除不用的属性 imgs[i].removeAttribute('data-mce-src') imgs[i].removeAttribute('alt') //拿到所有的图片列表 imgs[i].src.includes('base') && base64ImgSum.push(imgs[i]) } //将base64图片转换file文件 dataURLToFile(dataURL, filename) { const arr = dataURL.split(',') const mime = arr[0].match(/:(.*?);/)[1] // mime类型 image/png const bstr = atob(arr[1]) // base64 解码 let n = bstr.length const u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new File([u8arr], filename, { type: mime }) // return new Blob([a8arr], {type: mime}); }, //将处理file文件当参处理即可 fd.append('file', this.dataURLToFile(base64ImgSum[i].src, `${(new Date()).getTime()}.jpg`))
2023年02月15日
106 阅读
0 评论
0 点赞
2023-02-15
uniapp webview 直接调用原生摄像头拍照 拍视频
uniapp webview 直接调用原生摄像头拍照 拍视频 img video,通过ajax获取blob(file为特殊的blob)对象并上传到后端服务器web-view中通过plus方法调用摄像头拍照或者拍视频并上传后端的操作步骤如下:plus.camera.getCamera()获取摄像头对象 cmrcmr.captureImage(callback)\ cmr.startVideoCapture(callback)获得临时资源的临时路径 pathplus.io.resolveLocalFileSystemURL(‘路径path’,callback) 通过临时路径获得文件对象 entryentry.toRemoteURL() 获得网络路径通过ajax,设置responseType为blob,获取文件的blob对象,根据后端接口要求进行处理并上传例子中的后端接口要求表单数据formData的形式上传附上htm5文档地址http://www.html5plus.org/doc/zh_cn/io.html#plus.io.DirectoryEntry.toRemoteURL 以图片为例,代码如下,let cmr = plus.camera.getCamera() //获取相机对象 cmr.captureImage( //调用拍照方法,获得临时路径 function (p) { plus.io.resolveLocalFileSystemURL(p, function (entry) { //通过临时路径,获得文件系统中的文件对象entry entry.file(function (file) { // 可通过entry对象的file方法,获取文件数据对象(该文件数据对象仍无法直接使用) axios({ method: 'get', url: entry.toRemoteURL(), responseType: 'blob', }).then(res => { let blob = res.data const uploadFile = new FormData() uploadFile.append('file', blob ) axios({ method: 'post', url: '/file/api/Upload', headers: { 'Content-Type': 'multipart/form-data'}, data: uploadFile, }) }) file.close() }) }) }, function (error) { console.log('---' + 'Capture image failed: ' + error.message) }, )视频相同,将 captureImage 换成 startVideoCapture 即可
2023年02月15日
68 阅读
0 评论
0 点赞
2023-02-15
用uni-app开发自定义微信小程序头部导航
uni-app 官网:uni-app 官网微信小程序开发文档:微信开放文档官方介绍 uni-app:uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可编译到 iOS、Android、H5、以及各种小程序等多个平台。即使不跨端,uni-app同时也是更好的小程序开发框架。个人感受(功能很丰富,bug 也很丰富)*接下来我会将在项目中用到的一些我感觉还不错的东西记录一下,以供分享以及自己的积累=。=如有不足,请大佬们指教!!1.环境介绍编译器就用官方的 HBuilderX 编译器, 然后好像没了,创建一个 uni-app 项目就可以开始撸了。2.首先要先去除小程序自带的头部导航,在 page.json 文件里的对应的页面的 style 配置项里加上:"style":{ "navigationStyle":"custom" }就可以开始自定义导航了。3.为了适配不同型号手机的头部高度,所以要通过(异步)uni.getSystemInfo()或者(同步)uni.getSystemInfoSync()获取顶部状态栏的高度得到的状态栏高度可以存 vuex 里。(增加,获取头部高度,定义在全局)/*main.js*/ import Vue from 'vue' import App from './App' import store from './store' Vue.prototype.$store = store Vue.config.productionTip = false Vue.prototype.isBarHeight999 = function() { return new Promise((resolve, reject) => { var that = this var isTemp = {} uni.getSystemInfo({ success(res) { let totalTopHeight = 68 if (res.model.indexOf('iPhone X') !== -1) { totalTopHeight = 88 } else if (res.model.indexOf('iPhone') !== -1) { totalTopHeight = 64 } isTemp['statusBarHeight'] = res.statusBarHeight isTemp['titleBarHeight'] = totalTopHeight - res.statusBarHeight isTemp['allHeight'] = totalTopHeight resolve(isTemp) }, fail(e) { reject(e) } }) }) } App.mpType = 'app' const app = new Vue({ store, ...App }) app.$mount()4.在 components 文件里创建头部导航组件,名字自己起,我这边是 navTop.vue,直接上代码:/*navTop.vue*/<template> <view> <view class="nav_top" :style="{height:barHeight.allHeight+'px','background-color':hasBgc}"> <view class="status-bar" :style="{height:barHeight.statusBarHeight+'px'}"></view> <view class="topContent"> <view v-if="isBack" class="goBack" @click="goBack(backUrl)" hover-class="bgBlack999"> <image :src="'../static/icon_back_'+backColor+'.png'" mode=""></image> </view> <view class="title" :style="{color:color}">{{title}}</view> </view> </view> <view v-if="isHeight" class="marginBox" :style="{height:barHeight.allHeight+'px'}"></view> </view> </template> <script> import {mapState} from 'vuex' export default { props:{ title:{ type:String, default:'XXX' }, hasBgc:{ //背景色 type:String, default:'#5FCBAD' }, color:{ //字体颜色 type:String, default:'#fff;' }, isBack:{ //是否有返回箭头 type: Boolean, default:false }, backColor:{ //回退箭头颜色 type:String, default:'white' }, isHeight:{ //头部撑开高度 type:Boolean, default:true }, isNavBack:{ //固定返回前面的页面 type:Boolean, default:true }, backUrl:{ //返回到那个页面 type:String, default:'index' }, backNum:{ //返回几个页面 type:Number, default:1 } }, computed:{ ...mapState(['barHeight']) }, data() { return { }; }, methods:{ goBack(url){ if(this.isNavBack){ const that = this uni.navigateBack({ delta:that.backNum }) }else{ var isUrl = `/pages/${url}/${url}` console.log(isUrl) uni.switchTab({ url: isUrl }) } } }, created() { if(!this.barHeight.allHeight||!this.barHeight.statusBarHeight){ this.isBarHeight999().then((isTemp)=>{ this.$store.state.barHeight = isTemp }) } } } </script> <style lang="scss"> .nav_top{ width: 100vw; position: fixed; top: 0; left: 0; z-index: 900; display: flex; flex-direction: column; background-color: transparent; &.bgColor{ background-color: #5FCBAD; } .topContent{ width: 100vw; height: 100upx; position: relative; .goBack{ position: absolute; top: 0; left: 0; width: 10vw; height: 100upx; display: flex; align-items: center; padding-left: 20upx; box-sizing: border-box; image{ width: 40upx; height: 42upx; } } .title{ text-align: center; width: 100%; height: 100%; line-height: 100upx; font-size: 40upx; font-family:'自定义字体'; } } } </style>使用:/*test.vue*/<template> <view> <navTop :title="navtop.title" :isHeight="false" hasBgc="red" :color="navtop.color" :isBack="true"></navTop> </view> </template> <script> import navTop from '../../components/navTop.vue' export default { components: { navTop }, data() { return { navtop: { title: 'XXX', color: '#024230' } }; } } </script>效果图如下:已适配大部分机型,具体尺寸可以根据需求自行调整。。(还有不足之处,慢慢完善吧 o(∩_∩)o 哈哈)
2023年02月15日
55 阅读
0 评论
0 点赞
2023-02-15
uni-app人脸识别功能
在听到人脸识别,连忙去看看,去阿里 腾讯 看他们的人脸识别方法,官方sdk什么的。到后来,需求确定了,拍照(照片)上传,后台去识别是不是本人,这一瞬间从天堂到地狱,放着官方那么好的方法,不要。用照片,还的自己去写,去实现。下面为大家提供一个 uni-app 自动拍照 上传照片 后端做匹配处理。参考插件市场的 https://ext.dcloud.net.cn/plugin?id=4892在使用前 先去manifest.json 选择APP模块配置, 勾选直播推流 直接采用nvue开发,直接使用live-pusher组件进行直播推流,如果是vue开发,则需要使用h5+的plus.video.LivePusher对象来获取 nuve js注意事项 注意nuve 页面 main.js 的封装函数 。无法直接调用(小程序其他的端没有测试)在APP端 this.api报错,显示是undefined,难道nvue页面,要重新引入api文件在APP端,main.js中挂载Vuex在nvue页面无法使用this.$store.state.xxx简单粗暴点直接用uni.getStorageSync 重新获取一遍//获取用户数据 userInfo在Data里定义 this.userInfo = uni.getStorageSync('userInfo')nuve css注意事项 单位只支持px其他的em,rem,pt,%,upx 都不支持需要重新引入外部css不支持使用 import 的方式引入外部 css<style src="@/common/test.css"></style>默认flex布局display: flex; //不需要写 //直接用下面的标签 flex-direction: column; align-items: center; justify-content: space-between;页面样式<view class="live-camera" :style="{ width: windowWidth, height: windowHeight }"> <view class="title"> {{second}}秒之后开始识别 </view> <view class="preview" :style="{ width: windowWidth, height: windowHeight-80 }"> <live-pusher id="livePusher" ref="livePusher" class="livePusher" mode="FHD" beauty="1" whiteness="0" aspect="2:3" min-bitrate="1000" audio-quality="16KHz" :auto-focus="true" :muted="true" :enable-camera="true" :enable-mic="false" :zoom="false" @statechange="statechange" :style="{ width: cameraWidth, height: cameraHeight }"></live-pusher> <!--提示语--> <cover-view class="remind"> <text class="remind-text" style="">{{ message }}</text> </cover-view> <!--辅助线--> <cover-view class="outline-box" :style="{ width: windowWidth, height: windowHeight-80 }"> <cover-image class="outline-img" src="../../static/idphotoskin.png"></cover-image> </cover-view> </view> </view>JS部分<script> import operate from '../../common/operate.js' import api from '../../common/api.js' export default { data() { return { //提示 message: '', //相机画面宽度 cameraWidth: '', //相机画面宽度 cameraHeight: '', //屏幕可用宽度 windowWidth: '', //屏幕可用高度 windowHeight: '', //流视频对象 livePusher: null, //照片 snapshotsrc: null, //倒计时 second: 0, ifPhoto: false, // 用户信息 userInfo: [] }; }, onLoad() { //获取屏幕高度 this.initCamera(); //获取用户数据 this.userInfo = uni.getStorageSync('userInfo') setTimeout(() => { //倒计时 this.getCount() }, 500) }, onReady() { // console.log('初始化 直播组件'); this.livePusher = uni.createLivePusherContext('livePusher', this); }, onShow() { //开启预览并设置摄像头 this.startPreview(); }, methods: { //获取屏幕高度 initCamera() { let that = this uni.getSystemInfo({ success: function(res) { that.windowWidth = res.windowWidth; that.windowHeight = res.windowHeight; that.cameraWidth = res.windowWidth; that.cameraHeight = res.windowWidth * 1.5; } }); }, //启动相机 startPreview() { this.livePusher.startPreview({ success(res) { console.log('启动相机', res) } }); }, //停止相机 stopPreview() { let that = this this.livePusher.stopPreview({ success(res) { console.log('停止相机', res) } }); }, //摄像头 状态 statechange(e) { console.log('摄像头', e); if (this.ifPhoto == true) { //拍照 this.snapshot() } }, //抓拍 snapshot() { let that = this this.livePusher.snapshot({ success(res) { that.snapshotsrc = res.message.tempImagePath; that.uploadingImg(res.message.tempImagePath) } }); }, // 倒计时 getCount() { this.second = 5 let timer = setInterval(() => { this.second--; if (this.second < 1) { clearInterval(timer); this.second = 0 this.ifPhoto = true this.statechange() } }, 1000) }, // 图片上传 uploadingImg(e) { let url = e // console.log(url); let that = this uni.uploadFile({ url: operate.api + 'api/common/upload', filePath: url, name: 'file', formData: { token: that.userInfo.token }, success(res) { // console.log(res); let list = JSON.parse(res.data) // console.log(list); that.request(list.data.fullurl) } }) }, //验证请求 request(url) { let data = { token: this.userInfo.token, photo: url } api.renzheng(data).then((res) => { // console.log(res); operate.toast({ title: res.data.msg }) if (res.data.code == 1) { setTimeout(() => { operate.redirectTo('/pages/details/details') }, 500) } if (res.data.code == 0) { setTimeout(() => { this.anew(res.data.msg) }, 500) } }) }, // 认证失败,重新认证 anew(msg) { let that = this uni.showModal({ content: msg, confirmText: '重新审核', success(res) { if (res.confirm) { // console.log('用户点击确定'); that.getCount() } else if (res.cancel) { // console.log('用户点击取消'); uni.navigateBack({ delta: 1 }) } } }) }, } }; </script>css 样式<style lang="scss"> // 标题 .title { font-size: 35rpx; align-items: center; justify-content: center; } .live-camera { .preview { justify-content: center; align-items: center; .outline-box { position: absolute; top: 0; left: 0; bottom: 0; z-index: 99; align-items: center; justify-content: center; .outline-img { width: 750rpx; height: 1125rpx; } } .remind { position: absolute; top: 880rpx; width: 750rpx; z-index: 100; align-items: center; justify-content: center; .remind-text { color: #dddddd; font-weight: bold; } } } } </style>
2023年02月15日
65 阅读
0 评论
0 点赞
2023-02-14
uni-app云打包成ipa文件安装到iPhone上全过程记录
今天记录一下关于uniapp开发出来的项目怎么进行云打包,到本地手机上,这篇文章将从注册苹果开发者开始记录,一直到安装到 iPhone 手机上整个完整的过程,之所以写这篇文章第一是我自己需要记录一下,第二是很多博主也有写,但是都是片段,我也早就想写了,但是之前的例子没有截图,所以借助这次又写类似项目的机会,记录一下,我希望您在我这可以直接解决关于云打包中遇到的大部分问题。ok 废话不多说,开整,篇幅较长,我尽量分段加导航的模式进行! 本文主要是记录 ios 的打包过程,安卓的不写原因是他没有那么多的要求,放张图说明一下:我们就下面的四个红框的文件怎么来的分别进行讲解。(获取的地方我都进行加粗标记了,看的时候可以注意一下)不喜欢看我的可以看官方的官方地址 我们今天就是搞定上面需要的四个文件。需要的准备工作一个苹果的开发者账号 申请流程APP 的一系列大小的 logo (配置 manifest.json)登录到开发者平台(使用注册的开发者账号)进行证书、设备、开发人员的配置Hbuilder 开发工具进行云打包一台 MAC 笔记本,需要用到 mac 自带的钥匙串功能进行证书的申请APP 图标配置找到项目中的 manifest.json 文件,打开以后将本地的图标配置好就 ok,此步骤在 hbuilder 中操作 下面的步骤都是在您申请开发者账号以后需要做的事情。证书、设备、开发人员的配置登录到开发者平台进行配置开发者平台 添加开发者如果需要别的开发人员进行开发这个项目,可以直接打开 People,进行添加开发者,需要注意的是这一步只有注册者可以打开,也就是被拉进去的开发者是没有权限拉别人的。下面说一下具体拉人的步骤: 然后就到邀请成功,被邀请的开发者会收到一封邮件,点击同意就可以了 配置 App ID App 的唯一标识打开登录以后的开发者中心,点击下面的 Certificates 点击 ➕ 选择注册 选择 App 这里的配置名字的时候建议是域名反过来,加上你的描述,保证唯一就可以 不要选择太多,每一个的审核不一样,太多到时候审核失败了,不好搞! 下一步就是点击确认注册 至此 AppID 就创建结束了,也就意味着我们云打包的截图上面的第一个红框(BundleID(AppID))内容有了。生成证书请求文件这里需要 mac 笔记本自带的钥匙串功能,我们打开钥匙串,可以 command+空格,输入钥匙串 我这里没办法截图了,尴尬, 点击钥匙串访问-证书管理-从证书颁发机构请求证书 至此先放着,我们接着注册申请开发证书和描述文件这个东西申请下来以后就可以将 APP 安装到我们的 iPhone 上,进行测试,但是这个需要注意的一个点是,一个描述文件最多可以绑定 100 台测试手机 此时 cer 文件双击可以打开这个证书,会直接打开钥匙串,这个 cer 文件是为了后面导出 p12 证书,也就是为第一张红框中的最后一个文件进行准备导出 p12 证书导出证书的目的就是添加到红框中最后一个选项,我们打开刚才下载的证书 我们右键进行导出 这里设置的密码就是我们云打包里面的第二个红框里面需要输入的密码(证书私钥密码) p12 文件就是红色框中的最后一个文件(私钥证书) 至此我们云打包需要的四个地方已经搞定了三个,就剩第三个红框中的内容了,我们接着看申请开发 描述文件这个文件是云打包中的第三个红框需要的文件,这个文件可以确定哪些设备可以进行安装 beta 版本的 APP,也就是没有发布之前可以在哪些手机上进行安装。 在生成描述文件之前要先添加设备信息添加设备 UDID 获取 UDID这个方式很多,最快的方式就是直接使用 mac 自带的 itunes 程序连接手机 这里很多人看到的是序列号,没事,点击序列号会变,变到 UDID 的时候右键进行拷贝就好了。 这样就获取到 udid,然后按照上面的步骤继续,最后保存就好了。至此设备就添加结束了。生成描述文件 下一步就是点击下载到本地,至此云打包中的第三个红色框(证书 profile 文件)的文件也准备完毕,现在就可以打包了。打包结束安装到本地 至此 ipa 文件就生成了,但是他和安卓不同的是 iPhone 不支持直接点击安装,这个时候我们需要下载一个爱思助手进行帮助我们安装本地的 ipa 文件,爱思助手下载地址 下载好以后连接上手机 如果是 windows 电脑也是可以安装的,只是没办法打包,如果给你一个 ipa 文件,你需要的是下载一个爱思助手 windows 版本的,然后安装 itunes,windows 版本的,连接上你的 iPhone 手机,下面的操作都一样了,我们接着看,打开以后添加本地文件,或者将 ipa 文件直接拖进去也可以,如图: 然后就等待安装结束,提示成功以后手机上就可以正常使用了。常见问题安装失败安装失败很大一个原因是没有配置 UDID,配置以后没有重新成功描述文件,也就是云打包中的第三个红框中文件没有更换就打包了。手机连接不上数据线是不是有问题是不是没有解锁是不是没有点击信任是不是 type-c 或者 usb 口坏掉了博客转载与 uni-app云打包成ipa文件安装到iPhone上全过程记录
2023年02月14日
86 阅读
1 评论
0 点赞
2023-02-11
移动APP开发框架盘点
移动APP开发框架盘点 总体概述 现在比较流行的移动APP开发框架有以下六种:网页、混合、渐进、原生、桥接、自绘。前三种体验与Web的体验相似,后三种与原生APP的体验相似。这六种框架形式,都有自己适用的范围。无所谓好坏,适用就是好。 l 网页应用适用于传统网站APP化,比如淘宝、京东,有大量WEB页面嵌入到APP中。 l 混合应用适用于小成本应用开发,全部代码都基于Web,好处是开发快速、成本低。 l 渐进应用适用于高机会成本的场合,边下载边使用,能快速获取,快速体验。 l 原生应用适用于大型和高体验要求的应用,能做出让人满意的体验效果。 l 桥接应用适用于高速迭代的创意类应用,让体验与成本都处于可接受的范围。 l 自绘应用适用于游戏和有特殊效果的应用,最大的好处是没有平台约束和表达瓶颈。 一、网页WebApp WebApp与传统Web的主要区别,在于前端框架,特别是V-DOM框架的应用。此类前端框架使得WebApp与NativeApp在机理上已经没有任何区别了。在众多的前端框架中, React、Vue和Angular是最有竞争力的选择。 React V-Dom技术的开创者,主流框架中的NO.1。React的贡献都是开创性的,在它基础上,也有很多兼容框架,比如Anu,Nerv。用以解决React在性能或IE兼容性上的问题。 主流技术方案: React + Redux + ReactRouter + Material-UI/AntD/Semantic-UI Vue 由国人创建,在中国拥有大量使用者,也有很多配套的开源项目。它的是要特点是学习成本低,容易上手。 主流技术方案: Vue + Vuex + Vue-Router + Vuetify/Quasar/vux/ Mint-UI Angular 大而全型的框架,为大型项目所推崇,深度整合Typescript和Rxjs。 主流技术方案: Angular + Typescript 二、原生NativeApp 由于操作系统的限制,原生应用只有那么几种。对于原生应用,架构是基础,框架是核心,加上海量的UI组件。 IOS iOS开发已经从OC全面转向Swift,最新的架构VIPER基本上可以视为MMVPP。 主流技术方案: VIPER + RxSwift + Moya + Alamofire + SwiftyJSON/ObjectMapper Android Android开发语言从Java更换为Kotlin。编译时依赖注入框架Dagger也成为不二法宝。 主流技术方案: MVP + Dagger + RxKotlin + Retrofit + OkHttp + Kotson(Gson) 三、混合HybridApp HybridApp的关键不在本身,而在WebApp。好的WebApp改为HybridApp很容易。所以混合应用框架实际是指WebApp的基础设施库,有Cordova(PhoneGap)就足够了。 Cordova PhoneGap开源而来。 官方网站: https://cordova.apache.org/ 四、桥接BridgeApp 桥接应用的特点在于使用原生界面,但应用逻辑使用脚本语言编写,通用桥接来控制原生界面。这样达到使用脚本编写原生应用的目的,甚至可以网页与原生应用使用同一套代码,节省大量开发成本。但是操作手感比网页应用强不少。 ReactNative与Weex代表了两种不同的思路。ReactNative提供工具,将平台差异化开放出来(Learn Once, Write Anywhere);而Weex提供框架,将平台差异化屏蔽(Write Once, Run Everywhere)。所以ReactNative最大的痛点是使用难度大,必须熟悉所有平台;Weex则注定功能相对弱小,并且坑比较多。 React Native React的大热,实际始于ReactNative的发布。 官方网站: https://facebook.github.io/react-native/ Weex 小众的框架,能不能壮大,关键在于学习者是否能有效率地编写应用。所以很多人认为文档是决定一个开源框架生死的关键。其实有几个使用框架的开源应用,比文档还要关键。因为这些应用活着,间接地证明了框架还有生存的价值和能力,也能成为更好的文档教材。Weex号称有一堆知名的应用,但开源项目就乏善可陈了。 官方网站: http://weex.apache.org/ Xamarin(C#) Xamarin在IOS与Android中的实现方式不一致,在IOS中是AOT直接编译,在Android中是使用桥接技术。 官方网站: http://xamarin.com/ RubyMotion(Ruby) 动态语言编写移动应用,对语言社区而言是能力问题,必须要证明语言的优越性和无所不能。但除开狂热爱好者,正确的做法是使用最有效率的平台和语言。Ruby的长处在于Web后端,所以编写APP并无多少继承性,编写效率也由于太过小众而存在掉坑的风险。有免费版本,但只支持最新的操作系统版本。 官方网站: http://www.rubymotion.com/ Titanium 这个框架的核心就是使用JavaScript开发应用,与Web开发的在形式上区别很大。所以从根本上,这就是一种脚本语言框架,和RubyMotion如出一辙。 官方网站: http://www.appcelerator.com/ 五、自绘OwndrawApp 自绘一直以来都是游戏界面的势力范围,事实上除开Flutter,其它的框架都是偏游戏开发的。所以Flutter的横空出世,吸引了很大的关注,毕竟这是真正跨平台的唯一可行方案。 Flutter(Dart) 除了使用Dart语言有些争议外,Flutter是真正值得关注的跨平台方案,没有之一。最近它的目标平台除开iOS 和 Android,Flutter Desktop Embedding项目将Flutter引入到桌面操作系统,Hummingbird项目将 Flutter 应用引入浏览器。它利用 Dart 平台的特性不仅可以编译原生 ARM 代码,还可以编译JavaScript 。这使得 Flutter 代码可以在基于标准的 Web 上运行而无需任何更改。 官方网站: https://flutter.dev/ CrossApp(C++) CrossApp是基于Cocos2d-x引擎的,而Cocos2d-x是基于OpenGL的 。9秒社团是由手游社区发展而来的,由此可见CrossApp的背景,使用C++开发也有一些忠实拥趸。 官方网站: https://crossapp.9miao.com/ Corona(Lua) 更适合做游戏,不适合做应用程序,主要是因为界面部分,官方提供的UI部分代码非常不好用,自己实现又很耗时耗力。 官方网站: https://coronalabs.com/ Kivy(Python) 又一个动态语言开发框架,和游戏引擎结合起来使得它在特定领域还是很有市场的。而且它还跨windows平台,可以在windows下直接运行,可以真正实现跨平台运行。 官方网站: https://kivy.org/ 六、渐进ProgressiveApp 渐进有边下载边使用这一层意思,也有下载完成后不依赖网络这一层意思。从类型上来讲,有Google主导的PWA(ProgressiveWebApp),还有微信主导的小程序。相比PWA一统天下的野心,小程序明显是实用主义导向,能用就好,没有长远的布局。急于与微信竞争的百度、支付宝、中国九大手机厂商联盟的QuickApp也复制了这种风格。随着各种跨平台转译工具(如Taro)的兴起,各个小程序平台也随之变成了一个专有浏览器实现,变成了前端千框万架大战中的小小注脚了。 PWA 全称Progressive Web App,即渐进式网页应用。相对于国内厂商的私有平台,谷歌主导的PWA从一开始就瞄准下一代浏览器标准。与传统网页最大的不同,是引入了Service Worker了,相当于本地服务器,能在离线时替代网站服务器继续工作。除此之外,PWA大致就是一个SPA(single page web app),开放标准的继承性还是比较高的。不过由于各平台厂商(如微软,苹果)对PWA的态度不明,PWA的推广进展缓慢。 MiniProgram 微信小程序,由于微信的体量与使用频度,使得小程序可以承载足够的野心。不过从技术上讲,也就是个使用人数较多的浏览器实现。小程序和大量的跟随者,促使多端统一框架也发展起来了。 官方网站: https://mp.weixin.qq.com/ Taro React兼容的跨平台多端统一开发框架。一键生成可以在微信/百度/支付宝/字节跳动小程序、H5、React Native等端运行的代码。虽然多端统一框架看似可以在不同类型的平台运行,但它还是要依赖那些平台工具,还是寄居之上的小程序而已。 官方网站: https://taro.aotu.io/ Nanachi 司徒正美开发的React兼容Anu框架的多端转译脚手架。但是对比Taro是一堆工具打包而成,nanachi仅是一个脚手架,未免寒酸。不包装一下? 官方网站: https://rubylouvre.github.io/nanachi/ Chameleon 类Vue的跨平台多端统一开发框架。专门拜读了昨天出炉的《Chameleon原理详解:其它跨多端统一框架都是假的?》。它有神奇的多态组件,就是重新定义了一个组件声明框架;严格全面的检查,不会不让你的代码不能不跨平台;更多的适配代码,听说有后端统一接口,而且还有一个后台管理系统。好吧,祝您玩得愉快。 官方网站: https://cmljs.org/ 趋势分析 国内由于微信开放了小程序流量主的广告收入,引爆了小程序的热潮,进一步催生了多端转译框架。但是这些框架所做的事并不多,引擎还是React/Vue,做了一套各平台统一的UI组件而已。由于个性化的原因,UI组件是大家一直都想统一,而不能如愿的目标。Taro框架也只是推销了自己的一套UI组件而已。在我看来,还不如提供一套组件“标准”,允许并鼓励用户自己实现。 如果有一套能在React/Vue,ReactNative/Weex,甚至Android,iOS,Flutter上使用的“标准”组件,那么全平台大一统,Write Once, Run Everywhere这个目标才有可能实现吧。 问题:试求React-Native、Taro、Weex、Chameleon框架组件的“最大公约数”和“最小公倍数”。 抽象的“公约数”好求,结合实现的“最小公倍数”只能在实践中证明了。 框架 组件 组件名称 抽象类型 ReactNative View 视图容器 <View> Text 文本 <Text> Image 图片 <Image> ImageBackground 图片背景 <View> TextInput 文本输入框 <Input> ScrollView 滚动视图 <View> Button 按钮 <Button> Picker 选择器 <Picker> Slider 范围值选择 <Slider> Switch 开关组件 <Checkbox> FlatList 简单列表 <List> SectionList 分组列表 <List> DatePickerIOS 日期/时间选择器 <DatePicker> MaskedViewIOS 带蒙版的视图 <Modal> ProgressViewIOS 进度条 <Progress> SegmentedControlIOS 分段显示多个选项 <Tab> SafeAreaView 非遮挡可视区域 <View> SnapshotViewIOS 截屏视图 <View> DrawerLayoutAndroid 抽屉导航 <View> ProgressBarAndroid 进度条 <Progress> ToolbarAndroid 工具栏 <Card> ActivityIndicator 加载提示符 <Loading> KeyboardAvoidingView 随键盘调整视图 <View> Modal 模式视图 <Modal> RefreshControl 下拉刷新 <Trigger> StatusBar 状态栏 <Card> TouchableHighlight 高亮触摸响应 <Trigger> TouchableOpacity 透明度触摸响应 <Trigger> Taro View 视图容器 <View> ScrollView 可滚动视图 <View> Swiper 滑块视图容器 < Swiper> MovableView 可移动的视图容器 <View> CoverView 覆盖在原生组件之上的文本视图 <View> Icon 图标 <Text> Text 文本 <Text> Progress 进度条 <Progress> RichText 富文本 <View> Button 按钮 <Button> CheckboxGroup 多项选择器 <View> Form 表单 <View> Input 文本输入框 <Input> Label 表单标签 <Text> Picker 普通选择器 <Picker> PickerView 嵌入的滚动选择器 <View> Radio 单项选择器 <Radio> Slider 滑动选择器 <Slider> Switch 开关选择器 <Checkbox> Textarea 多行输入框 <Input> Navigator 页面导航链接 <Card> Audio 音频 <Audio> Image 图片 <Image> Video 视频 <Video> Camera 系统相机 <Camera> LivePlayer 实时音视频播放 <Service> LivePusher 实时音视频录制 <Service> Map 地图 <Map> Canvas 画布 <Canvas> OpenData 展示微信开放的数据 <Card> WebView 网页承载容器 <Web> Weex a 页面间的跳转 <A> div 通用容器 <View> text 文本 <Text> image 图片 <Image> list 垂直列表 <List> cell 列表子组件 <View> loading 容器上拉加载 <Trigger> Refresh 容器下拉刷新 <Trigger> recycle-list 复用列表容器 <List> scroller 滚动的容器 <View> slider 轮播图 < Swiper> indicator 轮播图子组件 < Swiper> textarea 多行文本输入 <Input> input 输入 <Input> waterfall 瀑布流布局容器 <View> video 视频 <Video> web 网页 <Web richtext 富文本容器 <View> Chameleon view 视图容器 <View> text 文本容器 <Text> page 基础页面容器 <View> block 包装容器 <View> cell 子列表项容器 <View> scroller 可滚动视图区域 <View> list 可滚动长列表 <List> container 布局容器 <View> row flex布局行容器 <View> col flex布局列容器 <View> carousel 轮播图 < Swiper> carousel-item 轮播图子容器 < Swiper> button 按钮 <Button> input 输入框 <Input> textarea 多行输入框 <Input> switch 开关 <Checkbox> radio 单选框 <Radio> checkbox 复选框 <Checkbox> image 图片 <Image> video 视频播放器 <Video> c-animation 动画组件 <Animation> c-toast 提示框 <Toast> c-loading 加载中 <Loading> c-dialog 对话框 <Modal> c-popup 蒙层 <Modal> c-tip 提示 <Card> c-actionsheet 操作列表 <List> c-tab 标签页 <Tab> c-picker 底部弹起的选择器 <Picker> c-picker-panel 底部弹起的控制板 <Picker> c-picker-item 滚动选择器 <Picker> c-checkbox-group 复选框列表 <View> c-radio-group 单选框列表 <View> c-refresh 上拉&下拉刷新 <Trigger> <完>
2023年02月11日
99 阅读
0 评论
0 点赞
2023-02-11
uni-app中的版本更新
主要是拿本地的版本号和后端返回的版本号做对比(在每次修改完代码以后,要打个更新包或者整包给后端喔) <template> <view> <sxPopup ref="appUpdate" @closeMaks="closeMaks"> <view class="update-main"> <image src="/static/images/update_close.png" class="update-close" @tap="hide"></image> <view class="update-content"> 最新安装包已准备就绪恭请主人体验尝 </view> <view class="update-btn btn-solid" @tap="updateApp"> 立即体验 </view> </view> </sxPopup> </view> </template> 在data中定义 version:"", // 当前版本 serverVersion:"", // 服务器版本 clientType:'', // 客户端类型 updateInfo:{}, // 更新信息 curDownSize:0, // 当前下载大小 在props中 autoChangeUpdate:{ // 是否自动检测更新 type:Boolean, default:false } mounted() { _this = this; this.changeUpdate() }, 在methods中 show(){ //显示更新弹窗 this.$refs.appUpdate.open() }, hide(){ //隐藏更新弹窗 uni.showTabBar(); this.$refs.appUpdate.close() }, changeUpdate(){ // 检测更新 //如果父组件选择自动更新 if(this.autoChangeUpdate){ this.getServerVersion() } }, isUpdate(curVersion,serVersion){ // 判断是否需要更新 let [serArray,curArray] = [serVersion.split("."),curVersion.split(".")] if(parseInt(serArray[0]) > parseInt(curArray[0])){ return true }else if(parseInt(serArray[1]) > parseInt(curArray[1])){ return true }else if(parseInt(serArray[1]) >= parseInt(curArray[1]) && parseInt(serArray[2]) > parseInt(curArray[2])){ return true }else{ return false } }, computedVersion(version){ // 计算版本 let array = version.split("."); let sum = 0; sum += array[0]*10 sum += array[1]+array[2] return sum; }, getServerVersion(){ // 获取服务器版本 if(!this.autoChangeUpdate){ uni.showLoading({ title:"检测更新" }) } this.$ajax("index/apiUpdateVersion",{}).then(res=>{ uni.hideLoading() if(res.code == 200){ this.updateInfo = res.data plus.runtime.getProperty(plus.runtime.appid, function(wgtinfo) { // 获取当前app版本 _this.version = wgtinfo.version; console.log(uni.getSystemInfoSync().platform) // 判断手机类型 switch (uni.getSystemInfoSync().platform) { case 'android': if (_this.isUpdate(_this.version,res.data.android_version)) { _this.serverVersion = res.data.android_version; _this.clientType = 'android' _this.show() uni.hideTabBar(); }else{ if(!_this.autoChangeUpdate){ _this.$toast("当前已是最新版本") } } break; case 'ios': if (_this.isUpdate(_this.version,res.data.ios_version)) { _this.serverVersion = res.data.ios_version; _this.clientType = 'ios' _this.show() uni.hideTabBar(); }else{ if(!_this.autoChangeUpdate){ _this.$toast("当前已是最新版本") } } break; } // _this.$emit('isUpdata',false) }); }else{ this.$toast(res.data.msg) } }) }, updateApp(){ // 更新app let version = this.version.split('.'); let updateVersion = this.serverVersion.split('.'); this.hide() // 大更新 if (parseInt(updateVersion[0]) > parseInt(version[0])) { if(_this.clientType == 'android'){ console.log(_this.clientType) console.log(this.updateInfo.android_url) this.downApp(this.updateInfo.android_url,'bigUpdate'); }else{ plus.runtime.openURL(this.updateInfo.ios_url) } } else if (parseInt(updateVersion.join('.').replace(/\./g, '')) > parseInt(version.join('.').replace(/\./g, ''))) { //小更新 // 热更新 this.downApp(this.updateInfo.hot_update,'thermalRenewal'); } }, downApp(url,updateType){ // 文件大小 let fileSize = ''; if(updateType == 'bigUpdate'){ fileSize = this.updateInfo.android_url_file_size; }else{ fileSize = this.updateInfo.hot_update_file_size; } fileSize = parseFloat(fileSize) * 1024 * 1024 console.log(fileSize) let waiting = plus.nativeUI.showWaiting('下载中...'); // 创建下载任务 let dtask = plus.downloader.createDownload(url,{ filename: '_doc/update/' // 文件下载保存路径 },function(d, status) { console.log(status,5555) if (status == 200) { console.log(status,'.............') waiting.setTitle('安装中...'); // 下载成功 plus.runtime.install(d.filename, {}, function() { plus.nativeUI.closeWaiting(); plus.nativeUI.alert('更新完成!', function() { uni.showTabBar(); // 清除所有下载的包 plus.downloader.clear(-1); // 热更新自动安装 if(updateType == 'thermalRenewal'){ plus.runtime.restart(); } }); }, function(e) { plus.nativeUI.closeWaiting(); plus.nativeUI.alert('安装失败[' + e.code + ']:' + e.message); }); } else { //下载失败 plus.nativeUI.alert('下载失败!'); setTimeout(()=>{ plus.nativeUI.closeWaiting(); },1000) if(updateType == 'bigUpdate'){ plus.runtime.openURL(url) //打开网页手动下载 } } }) // 监听下载状态 // dtask.addEventListener("statechanged", function(download, status) { // // console.log(`监听:下载状态${status}`) // if (status == 200) { // let i = download.downloadedSize // i *= 100 / fileSize; // console.log(`监听:下载大小${i}`) // if(!isNaN(i)){ // i = parseInt(i) // waiting.setTitle('已下载 ' + i + "%"); // // waiting.setTitle(i); // } // }else if(status == 404){ // plus.nativeUI.closeWaiting(); // plus.runtime.toast("下载地址错误") // } // }, true); dtask.start(); }
2023年02月11日
51 阅读
0 评论
0 点赞
1
2
3