HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

GitHub 1.8k stars 的 vue3 组件库 Wot UI 关闭了插件市场下载广告,请自由下载使用

组件 广告

GitHub 1.8k stars 的 vue3 组件库 Wot UI 关闭了插件市场下载广告,欢迎自由下载使用。

插件市场:https://ext.dcloud.net.cn/plugin?id=13889

继续阅读 »

GitHub 1.8k stars 的 vue3 组件库 Wot UI 关闭了插件市场下载广告,欢迎自由下载使用。

插件市场:https://ext.dcloud.net.cn/plugin?id=13889

收起阅读 »

【官方消息uni.webview.js下载地址】gitcode近期进行了改版,原gitcode.net仓库迁移到了gitcode.com

文档 uniapp Webview

gitcode近期进行了改版,原gitcode.net仓库迁移到了gitcode.com,新gitcode地址
uni.webview.js下载地址
后续以官方更新的文档内,uni.webview.js的链接地址为准!

继续阅读 »

gitcode近期进行了改版,原gitcode.net仓库迁移到了gitcode.com,新gitcode地址
uni.webview.js下载地址
后续以官方更新的文档内,uni.webview.js的链接地址为准!

收起阅读 »

现在有没有要开发uniapp的?

外包接单 外包

本人很早之前开发了很多uniapp代码,那会儿比较流行开发小程序和h5,不知道这么多年过去了,现在还有人开发小程序或者自己的网站吗?

现在外包市场怎么样?

本人很早之前开发了很多uniapp代码,那会儿比较流行开发小程序和h5,不知道这么多年过去了,现在还有人开发小程序或者自己的网站吗?

现在外包市场怎么样?

uniapp-x下的tab组件的简单实现

tab

<view class="tablist flex-row flex-wrap justify-between">
<text class="item t-center" :class="{'active': selectTab == index}" v-for="(item, index) in tablist" @click="changeTab(index)">{{item}}</text>
</view>
<swiper :indicator-dots="false" :autoplay="false" :current="selectTab" @change="changeEvent">
<swiper-item v-for="(item, index) in tablist">
<view class="swiper-item">
{{item}}
</view>
</swiper-item>
</swiper>


const selectTab = ref(0)  
const tablist = ["内容1", "内容2", "内容3", "内容4"]  

const changeTab = (index: number)=>{  
    selectTab.value = index  
}  
const changeEvent = (event: UniSwiperChangeEvent)=>{  
    selectTab.value = event.detail.current  
}  
继续阅读 »

<view class="tablist flex-row flex-wrap justify-between">
<text class="item t-center" :class="{'active': selectTab == index}" v-for="(item, index) in tablist" @click="changeTab(index)">{{item}}</text>
</view>
<swiper :indicator-dots="false" :autoplay="false" :current="selectTab" @change="changeEvent">
<swiper-item v-for="(item, index) in tablist">
<view class="swiper-item">
{{item}}
</view>
</swiper-item>
</swiper>


const selectTab = ref(0)  
const tablist = ["内容1", "内容2", "内容3", "内容4"]  

const changeTab = (index: number)=>{  
    selectTab.value = index  
}  
const changeEvent = (event: UniSwiperChangeEvent)=>{  
    selectTab.value = event.detail.current  
}  
收起阅读 »

鸿蒙 UTS 插件使用三方依赖、本地依赖

uts插件

鸿蒙 UTS 插件使用三方依赖

在鸿蒙开发中市场需要使用三方依赖,可能是三方包,可能是一个本地 har 包,这里介绍如何接入。

接入三方依赖

这里举例 https://ohpm.openharmony.cn/ 最受欢迎的三方库 @pura/harmony-utils 。这个库,提供了众多方法,可以加速功能开发。

harmony-utils 一款功能丰富且极易上手的HarmonyOS工具库,借助众多实用工具类,致力于助力开发者迅速构建鸿蒙应用。其封装的工具涵盖了APP、设备、屏幕、授权、通知、线程间通信、弹框、吐司、生物认证、用户首选项、拍照、相册、扫码、文件、日志,异常捕获、字符、字符串、数字、集合、日期、随机、base64、加密、解密、JSON等一系列的功能和操作,能够满足各种不同的开发需求。

从原生鸿蒙角度开发,使用这个工具库,需要两个步骤

  • 安装依赖
  • 调用方法

在 UTS 中使用这个工具库,需要三个步骤

  • 创建 UTS 插件
  • 引用依赖
  • 调用方法

下面介绍具体步骤

假设我们希望通过 harmony-utils 获取当前的工具包名。调用的是 AppUtil.getBundleName

1. 创建 UTS 插件

在 HBuilderX 中操作。首先创建 uni_modules 功能,如果当前目录中没有对应文件夹,可在项目文件夹节点单击右键选择 新建 uni_modules 目录

这会创建 uni_moduels 文件夹,在这个文件夹上单击右键。

在新窗口中选择 UTS 插件-API 插件,点击创建。

假定插件的 ID 是 invoke-utils,找到这个文件夹,观察是否存在对应的文件,如果没有就创建 uni_modules/invoke-utils/utssdk/app-harmony/index.uts

这样插件就创建好了。这部分可参考 UTS 插件介绍原生混编 做进一步了解。

2. 安装依赖

创建 uni_modules/invoke-utils/utssdk/app-harmony/config.json,添加依赖。可参考文档 配置uts插件依赖

鸿蒙的库管理工具是ohpm。类似于js的npm,Android的仓储。鸿蒙的三方sdk封装文件为.har,类似于Android的.aar
uts插件的utssdk/app-harmony/config.json文件内可以配置依赖使用鸿蒙的三方库

代码填写下面方案:

{  
   "dependencies": {  
     "@pura/harmony-utils":"1.3.6"  
   }  
}

接下来准备使用依赖功能。

3. 调用方法

在 index.uts 中添加下面逻辑

import { AppUtil } from '@pura/harmony-utils'  

UTSHarmony.onAppAbilityCreate(() => {  
  const abilityCtx = UTSHarmony.getUIAbilityContext();  
  const ctx = abilityCtx  
  AppUtil.init(ctx);  
})  

export const getAppId = () => {  
  let bundleName = AppUtil.getBundleName();  
  return bundleName  
}

在原始文档中要求在 AbilityCreate 中初始化,这里可以使用 UTSHarmony.onAppAbilityCreate 来实现初始化。

和 TS 代码类似,调用了提供的方法,返回了具体数据。

在实际的 Vue 逻辑中,比如 button 通过 click 调用下面逻辑即可

<script setup>  
  import { getAppId } from '@/uni_modules/invoke-utils'  
  function openTest() {  
    const res = getAppId()  
    console.log('获取应用ID:', res)  
  }  
</script>

调用此方法,观察控制台,可以看到包名。这说明工具调用成功。

接入 har 依赖

接入 har 依赖。如何制作 har 依赖,在下面单独说明。

假定已经得到了一个 localLib.har 文件。放置 har 文件在 uts 插件内,比如在 index.uts 的同级目录, libs/localLib.har 路径。

修改 config.json,配置相对路径。

{  
  "dependencies": {  
    "locallib": "./libs/localLib.har"  
  }  
}

在 index.uts 中引用和导出。

import { add } from 'locallib'  

export const addFun = (a:number, b:number):number => {  
  return add(a, b)  
}

在页面中引入这个 uts 插件并使用即可。

<script setup>  
  import { addFun} from '@/uni_modules/otto-thirdhar'  
  function openTest() {  
    console.log(addFun(3,4))  
  }  
</script>

执行这个方法,顺利的话可以看到控制台打印数字 7.

注意事项:

  • 引入 har 可能有版本兼容性要求,可在 harmony-configs/build-profile.json5 内修改 compatibleSdkVersion
  • har 可能构建内容有误,实际运行不正常,可在原生工程项目中自测,排除 har 文件内部问题

如何构建 har 模块

在 DevEco 中打开一个项目,选择 文件 - 新建 - 模块 - Static Library,定义模块名选择创建。

在创建的文件中选择 index.ets 找到模块入口,可导出组件、方法。

在 DevEco 中选择 构建(在重构和运行中间) - 构建模块(第一个选项),等待编译结束,观察模块目录中的 build/default/outputs/default 找到 har 文件。

har 文件本身是一个压缩文件,可自行拆包了解结构,内部存在方法 d.ets 等文件。

继续阅读 »

鸿蒙 UTS 插件使用三方依赖

在鸿蒙开发中市场需要使用三方依赖,可能是三方包,可能是一个本地 har 包,这里介绍如何接入。

接入三方依赖

这里举例 https://ohpm.openharmony.cn/ 最受欢迎的三方库 @pura/harmony-utils 。这个库,提供了众多方法,可以加速功能开发。

harmony-utils 一款功能丰富且极易上手的HarmonyOS工具库,借助众多实用工具类,致力于助力开发者迅速构建鸿蒙应用。其封装的工具涵盖了APP、设备、屏幕、授权、通知、线程间通信、弹框、吐司、生物认证、用户首选项、拍照、相册、扫码、文件、日志,异常捕获、字符、字符串、数字、集合、日期、随机、base64、加密、解密、JSON等一系列的功能和操作,能够满足各种不同的开发需求。

从原生鸿蒙角度开发,使用这个工具库,需要两个步骤

  • 安装依赖
  • 调用方法

在 UTS 中使用这个工具库,需要三个步骤

  • 创建 UTS 插件
  • 引用依赖
  • 调用方法

下面介绍具体步骤

假设我们希望通过 harmony-utils 获取当前的工具包名。调用的是 AppUtil.getBundleName

1. 创建 UTS 插件

在 HBuilderX 中操作。首先创建 uni_modules 功能,如果当前目录中没有对应文件夹,可在项目文件夹节点单击右键选择 新建 uni_modules 目录

这会创建 uni_moduels 文件夹,在这个文件夹上单击右键。

在新窗口中选择 UTS 插件-API 插件,点击创建。

假定插件的 ID 是 invoke-utils,找到这个文件夹,观察是否存在对应的文件,如果没有就创建 uni_modules/invoke-utils/utssdk/app-harmony/index.uts

这样插件就创建好了。这部分可参考 UTS 插件介绍原生混编 做进一步了解。

2. 安装依赖

创建 uni_modules/invoke-utils/utssdk/app-harmony/config.json,添加依赖。可参考文档 配置uts插件依赖

鸿蒙的库管理工具是ohpm。类似于js的npm,Android的仓储。鸿蒙的三方sdk封装文件为.har,类似于Android的.aar
uts插件的utssdk/app-harmony/config.json文件内可以配置依赖使用鸿蒙的三方库

代码填写下面方案:

{  
   "dependencies": {  
     "@pura/harmony-utils":"1.3.6"  
   }  
}

接下来准备使用依赖功能。

3. 调用方法

在 index.uts 中添加下面逻辑

import { AppUtil } from '@pura/harmony-utils'  

UTSHarmony.onAppAbilityCreate(() => {  
  const abilityCtx = UTSHarmony.getUIAbilityContext();  
  const ctx = abilityCtx  
  AppUtil.init(ctx);  
})  

export const getAppId = () => {  
  let bundleName = AppUtil.getBundleName();  
  return bundleName  
}

在原始文档中要求在 AbilityCreate 中初始化,这里可以使用 UTSHarmony.onAppAbilityCreate 来实现初始化。

和 TS 代码类似,调用了提供的方法,返回了具体数据。

在实际的 Vue 逻辑中,比如 button 通过 click 调用下面逻辑即可

<script setup>  
  import { getAppId } from '@/uni_modules/invoke-utils'  
  function openTest() {  
    const res = getAppId()  
    console.log('获取应用ID:', res)  
  }  
</script>

调用此方法,观察控制台,可以看到包名。这说明工具调用成功。

接入 har 依赖

接入 har 依赖。如何制作 har 依赖,在下面单独说明。

假定已经得到了一个 localLib.har 文件。放置 har 文件在 uts 插件内,比如在 index.uts 的同级目录, libs/localLib.har 路径。

修改 config.json,配置相对路径。

{  
  "dependencies": {  
    "locallib": "./libs/localLib.har"  
  }  
}

在 index.uts 中引用和导出。

import { add } from 'locallib'  

export const addFun = (a:number, b:number):number => {  
  return add(a, b)  
}

在页面中引入这个 uts 插件并使用即可。

<script setup>  
  import { addFun} from '@/uni_modules/otto-thirdhar'  
  function openTest() {  
    console.log(addFun(3,4))  
  }  
</script>

执行这个方法,顺利的话可以看到控制台打印数字 7.

注意事项:

  • 引入 har 可能有版本兼容性要求,可在 harmony-configs/build-profile.json5 内修改 compatibleSdkVersion
  • har 可能构建内容有误,实际运行不正常,可在原生工程项目中自测,排除 har 文件内部问题

如何构建 har 模块

在 DevEco 中打开一个项目,选择 文件 - 新建 - 模块 - Static Library,定义模块名选择创建。

在创建的文件中选择 index.ets 找到模块入口,可导出组件、方法。

在 DevEco 中选择 构建(在重构和运行中间) - 构建模块(第一个选项),等待编译结束,观察模块目录中的 build/default/outputs/default 找到 har 文件。

har 文件本身是一个压缩文件,可自行拆包了解结构,内部存在方法 d.ets 等文件。

收起阅读 »

uniapp开发的App实现在windows免费上传ios app到App Store

Appstore上传

首先下载Appuploader,下载地址:https://www.applicationloader.net

下载安装完成后,直接使用你的appleId加密码登陆进去就可以了。

只能用7天,7天过后就收费了。

高阶玩法,7天过期后如果还有使用的需要。
1.使用主账号登陆到苹果官网https://appstoreconnect.apple.com 。

  1. 找到用户和访问。
  2. 在“管理”那里加一个appleid。
  3. 使用这个新的appleid登录进去,接下来就是跟主账号密码一样的操作。
  4. 过期后再来一次,可以再免费体验一次。
  5. Appuploader官方不知道发现这个问题了没有,后面可能会封掉这种情况的。
  6. 反正我用了几年了。
  7. 有缘人看到了可以试一下。
  8. 不要告诉我是谁,请叫我雷锋。
继续阅读 »

首先下载Appuploader,下载地址:https://www.applicationloader.net

下载安装完成后,直接使用你的appleId加密码登陆进去就可以了。

只能用7天,7天过后就收费了。

高阶玩法,7天过期后如果还有使用的需要。
1.使用主账号登陆到苹果官网https://appstoreconnect.apple.com 。

  1. 找到用户和访问。
  2. 在“管理”那里加一个appleid。
  3. 使用这个新的appleid登录进去,接下来就是跟主账号密码一样的操作。
  4. 过期后再来一次,可以再免费体验一次。
  5. Appuploader官方不知道发现这个问题了没有,后面可能会封掉这种情况的。
  6. 反正我用了几年了。
  7. 有缘人看到了可以试一下。
  8. 不要告诉我是谁,请叫我雷锋。
收起阅读 »

【安卓权限说明同步显示】uniapp/uniappx项目选择图片或视频自动同步权限说明解决华为上架审核因权限说明驳回问题

uts插件 分享插件 权限描述 图片选择

很多小伙伴在上架华为应用市场的时候总是遇到选择图片或选择视频等文件需要申请权限告知目的问题被驳回
今天这里一个UTS图片/视频选择器插件就帮大家解决这个问题:图片视频选择器(支持同步告知权限申请目的.已支持鸿蒙,自定义选择器界面样式)
插件直达地址:https://ext.dcloud.net.cn/plugin?id=20793
在UNIAPP支持vue和nvue,在UNIAPPX支持uvue,安卓支持在选择图片或视频文件权限申请的时候自动同步告知权限申请目的,
轻松解决在华为应用市场审核,要求告知权限申请目的或说明的问题。
相册图片视频选择器功能介绍:
1.支持uniapp和uniappx,现在已经兼容了纯血鸿蒙和安卓后续将会兼容IOS
2.支持打开相册选择相片或视频
3.支持多选和单选
4.支持设置多种语言(仅安卓)
5.支持自定义界面主题样式(仅安卓)
6.支持媒体文件类型选择 0: ALL(视频和图片) 1: IMAGE(图片) 2: VIDEO(视频) 3: AUDIO(音频)
7.支持媒体文件预览
8.支持设置开启和关闭原图功能

  1. 支持设置开启和关闭相机拍照功能
    注意:集成完成后需要云打包或自定义基座才能生效,因为这是UTS原生SDK插件。

UNIAPP实现方法如下:

鸿蒙无需申请权限
安卓端首先在项目AndroidManifest.xml里配置示例文件里所需的权限也可直接复制插件示例里的AndroidManifest.xml文件到项目根目录
1.vue页面或nvue页面接口引入如下代码:

import { RHFselcet } from '@/uni_modules/fz-media-selcet';
  1. 在需要调用图片视频选择器的地方引入如下代码:

    let parameter ={  
        MediaType:2,//设置选择类型  0: ALL(全部) 1: IMAGE(图片) 2: VIDEO(视频) 3: AUDIO(音频) 不传默认为1图片  
        Single:2,//设置单选或多选(鸿蒙无需传这个参数), 1为单选,2为多选,不传默认为多选  
        maxNum:6,//设置最大选中数,不传默认为9 仅多选时生效  
        isOriginal:false, //是否开启原图功能,不传默认为false  
        isDisplayCamera:true,//是否显示拍摄按钮,不传默认为true  
    //语言设置, 0:简体中文,1:繁体,2:英语,3:韩语,4:德语,5:法语,6:日语,7:越语,8:西班牙语,9:葡萄牙语,10:阿拉伯语,11:俄语,12:捷克,13:哈萨克斯坦,不传默认为0  
        Language:2,  
        //主题界面样式设置,不传为默认样式  
        theme:{  
        titleBarStyle:{  
        TitleBackgroundColor:'#8E07FD'  
       },  
      bottomNavBarStyle:{  
          PreviewNormalTextColor:'#8E07FD',  
          PreviewSelectTextColor:'#8E07FD',  
          BarBackgroundColor:'#FFFFFF',  
          EditorTextColor:'#8E07FD',  
          OriginalTextColor:'#8E07FD',  
        },  
       selectMainStyle:{  
           NumberStyle:true, //选中样式是否为数字,不传默认显示勾选样式  
           isbtn:true, //完成按钮是否为显示背景,不传为默认不显示按钮背景  
        //如需修改按钮颜色需要在res/values/colors.xml里修改btn_primary和btn_hover的颜色值和按下后的颜色值  
           SelectTextColor:'#ffffff',//选择结果文字颜色  
          // SelectText:'%1$d/%2$d 完成',  
           SelectText:'使用(%1$d)',//选择结果要显示的文字,不传默认显示:已完成  
           StatusBarColor:'#8E07FD',  
           OriginalTextColor:'#8E07FD',  
       }  
        }  
       }  
    
    RHFselcet.getPicture( parameter, (data) => {  
    let arrData = data.mediaArray  
    let jsonData = JSON.stringify(arrData)  
    console.log(jsonData)  
    //回调结果:[{"mimeType":"video/mp4","num":1,"Pathurl":"/storage/emulated/0/DCIM/Camera/lv_0_20241025172529.mp4","height":1920,"fiename":"lv_0_20241025172529.mp4","path":"content://media/external/video/media/427606","Pathid":427606,"duration":16393,"size":20002573,"FolderName":"Camera","dateAddedTime":1729848339,"width":1080},{"mimeType":"video/mp4","num":2,"Pathurl":"/storage/emulated/0/DCIM/Camera/video_20241025_170438.mp4","height":1920,"fiename":"video_20241025_170438.mp4","path":"content://media/external/video/media/427604","Pathid":427604,"duration":16331,"size":35778914,"FolderName":"Camera","dateAddedTime":1729847078,"width":1080}]  
    });

    3.集成后云打包或自定义基座调试既可看到效果

uniappx实现方法如下:

1.uvue页面接口引入如下代码

import { RHFselcet } from '@/uni_modules/fz-media-selcet';

2.在uvue页面里要调用的地方加入如下代码:

let parameter: UTSJSONObject ={  
    MediaType:2,//设置选择类型  0: ALL(图片和视频) 1: IMAGE(图片) 2: VIDEO(视频) 3: AUDIO(音频) 不传默认为1图片  
    Single:2,//设置单选或多选(鸿蒙无需传这个参数), 1为单选,2为多选,不传默认为多选  
    maxNum:6,//设置最大选中数,不传默认为9  
    isOriginal:false, //是否开启原图功能,不传默认为false  
    isDisplayCamera:true,//是否显示拍摄按钮,不传默认为true  
//语言设置(仅安卓), 0:简体中文,1:繁体,2:英语,3:韩语,4:德语,5:法语,6:日语,7:越语,8:西班牙语,9:葡萄牙语,10:阿拉伯语,11:俄语,12:捷克,13:哈萨克斯坦,不传默认为0  
    Language:2,  
    //主题界面样式设置(仅安卓),不传为默认样式  
    theme:{  
    titleBarStyle:{  
    TitleBackgroundColor:'#8E07FD'  
   },  
  bottomNavBarStyle:{  
      PreviewNormalTextColor:'#8E07FD',  
      PreviewSelectTextColor:'#8E07FD',  
      BarBackgroundColor:'#FFFFFF',  
      EditorTextColor:'#8E07FD',  
      OriginalTextColor:'#8E07FD',  
    },  
   selectMainStyle:{  
       NumberStyle:true, //选中样式是否为数字,不传默认显示勾选样式  
       isbtn:true, //完成按钮是否显示背景颜色  
       //如需修改按钮颜色需要在res/values/colors.xml里修改btn_primary和btn_hover的颜色值和按下后的颜色值  
       SelectTextColor:'#ffffff',//选择结果文字颜色  
      // SelectText:'%1$d/%2$d 完成',  
       SelectText:'使用(%1$d)',//选择结果要显示的文字,不传默认显示:已完成  
       StatusBarColor:'#8E07FD',  
       OriginalTextColor:'#8E07FD',  
   }  
    }  
       }  
//打开相册或视频  
   RHFselcet.getPicture( parameter, (data) => {  
    // console.log(data["mediaArray"])  
    let arrData = data["mediaArray"]  
    let jsonData = JSON.stringify(arrData)  
    console.log(jsonData)  
//回调结果:[{"mimeType":"video/mp4","num":1,"Pathurl":"/storage/emulated/0/DCIM/Camera/lv_0_20241025172529.mp4","height":1920,"fiename":"lv_0_20241025172529.mp4","path":"content://media/external/video/media/427606","Pathid":427606,"duration":16393,"size":20002573,"FolderName":"Camera","dateAddedTime":1729848339,"width":1080},{"mimeType":"video/mp4","num":2,"Pathurl":"/storage/emulated/0/DCIM/Camera/video_20241025_170438.mp4","height":1920,"fiename":"video_20241025_170438.mp4","path":"content://media/external/video/media/427604","Pathid":427604,"duration":16331,"size":35778914,"FolderName":"Camera","dateAddedTime":1729847078,"width":1080}]  
    let DatalArray = JSON.parseArray(jsonData)  
    console.log(DatalArray)  
});
继续阅读 »

很多小伙伴在上架华为应用市场的时候总是遇到选择图片或选择视频等文件需要申请权限告知目的问题被驳回
今天这里一个UTS图片/视频选择器插件就帮大家解决这个问题:图片视频选择器(支持同步告知权限申请目的.已支持鸿蒙,自定义选择器界面样式)
插件直达地址:https://ext.dcloud.net.cn/plugin?id=20793
在UNIAPP支持vue和nvue,在UNIAPPX支持uvue,安卓支持在选择图片或视频文件权限申请的时候自动同步告知权限申请目的,
轻松解决在华为应用市场审核,要求告知权限申请目的或说明的问题。
相册图片视频选择器功能介绍:
1.支持uniapp和uniappx,现在已经兼容了纯血鸿蒙和安卓后续将会兼容IOS
2.支持打开相册选择相片或视频
3.支持多选和单选
4.支持设置多种语言(仅安卓)
5.支持自定义界面主题样式(仅安卓)
6.支持媒体文件类型选择 0: ALL(视频和图片) 1: IMAGE(图片) 2: VIDEO(视频) 3: AUDIO(音频)
7.支持媒体文件预览
8.支持设置开启和关闭原图功能

  1. 支持设置开启和关闭相机拍照功能
    注意:集成完成后需要云打包或自定义基座才能生效,因为这是UTS原生SDK插件。

UNIAPP实现方法如下:

鸿蒙无需申请权限
安卓端首先在项目AndroidManifest.xml里配置示例文件里所需的权限也可直接复制插件示例里的AndroidManifest.xml文件到项目根目录
1.vue页面或nvue页面接口引入如下代码:

import { RHFselcet } from '@/uni_modules/fz-media-selcet';
  1. 在需要调用图片视频选择器的地方引入如下代码:

    let parameter ={  
        MediaType:2,//设置选择类型  0: ALL(全部) 1: IMAGE(图片) 2: VIDEO(视频) 3: AUDIO(音频) 不传默认为1图片  
        Single:2,//设置单选或多选(鸿蒙无需传这个参数), 1为单选,2为多选,不传默认为多选  
        maxNum:6,//设置最大选中数,不传默认为9 仅多选时生效  
        isOriginal:false, //是否开启原图功能,不传默认为false  
        isDisplayCamera:true,//是否显示拍摄按钮,不传默认为true  
    //语言设置, 0:简体中文,1:繁体,2:英语,3:韩语,4:德语,5:法语,6:日语,7:越语,8:西班牙语,9:葡萄牙语,10:阿拉伯语,11:俄语,12:捷克,13:哈萨克斯坦,不传默认为0  
        Language:2,  
        //主题界面样式设置,不传为默认样式  
        theme:{  
        titleBarStyle:{  
        TitleBackgroundColor:'#8E07FD'  
       },  
      bottomNavBarStyle:{  
          PreviewNormalTextColor:'#8E07FD',  
          PreviewSelectTextColor:'#8E07FD',  
          BarBackgroundColor:'#FFFFFF',  
          EditorTextColor:'#8E07FD',  
          OriginalTextColor:'#8E07FD',  
        },  
       selectMainStyle:{  
           NumberStyle:true, //选中样式是否为数字,不传默认显示勾选样式  
           isbtn:true, //完成按钮是否为显示背景,不传为默认不显示按钮背景  
        //如需修改按钮颜色需要在res/values/colors.xml里修改btn_primary和btn_hover的颜色值和按下后的颜色值  
           SelectTextColor:'#ffffff',//选择结果文字颜色  
          // SelectText:'%1$d/%2$d 完成',  
           SelectText:'使用(%1$d)',//选择结果要显示的文字,不传默认显示:已完成  
           StatusBarColor:'#8E07FD',  
           OriginalTextColor:'#8E07FD',  
       }  
        }  
       }  
    
    RHFselcet.getPicture( parameter, (data) => {  
    let arrData = data.mediaArray  
    let jsonData = JSON.stringify(arrData)  
    console.log(jsonData)  
    //回调结果:[{"mimeType":"video/mp4","num":1,"Pathurl":"/storage/emulated/0/DCIM/Camera/lv_0_20241025172529.mp4","height":1920,"fiename":"lv_0_20241025172529.mp4","path":"content://media/external/video/media/427606","Pathid":427606,"duration":16393,"size":20002573,"FolderName":"Camera","dateAddedTime":1729848339,"width":1080},{"mimeType":"video/mp4","num":2,"Pathurl":"/storage/emulated/0/DCIM/Camera/video_20241025_170438.mp4","height":1920,"fiename":"video_20241025_170438.mp4","path":"content://media/external/video/media/427604","Pathid":427604,"duration":16331,"size":35778914,"FolderName":"Camera","dateAddedTime":1729847078,"width":1080}]  
    });

    3.集成后云打包或自定义基座调试既可看到效果

uniappx实现方法如下:

1.uvue页面接口引入如下代码

import { RHFselcet } from '@/uni_modules/fz-media-selcet';

2.在uvue页面里要调用的地方加入如下代码:

let parameter: UTSJSONObject ={  
    MediaType:2,//设置选择类型  0: ALL(图片和视频) 1: IMAGE(图片) 2: VIDEO(视频) 3: AUDIO(音频) 不传默认为1图片  
    Single:2,//设置单选或多选(鸿蒙无需传这个参数), 1为单选,2为多选,不传默认为多选  
    maxNum:6,//设置最大选中数,不传默认为9  
    isOriginal:false, //是否开启原图功能,不传默认为false  
    isDisplayCamera:true,//是否显示拍摄按钮,不传默认为true  
//语言设置(仅安卓), 0:简体中文,1:繁体,2:英语,3:韩语,4:德语,5:法语,6:日语,7:越语,8:西班牙语,9:葡萄牙语,10:阿拉伯语,11:俄语,12:捷克,13:哈萨克斯坦,不传默认为0  
    Language:2,  
    //主题界面样式设置(仅安卓),不传为默认样式  
    theme:{  
    titleBarStyle:{  
    TitleBackgroundColor:'#8E07FD'  
   },  
  bottomNavBarStyle:{  
      PreviewNormalTextColor:'#8E07FD',  
      PreviewSelectTextColor:'#8E07FD',  
      BarBackgroundColor:'#FFFFFF',  
      EditorTextColor:'#8E07FD',  
      OriginalTextColor:'#8E07FD',  
    },  
   selectMainStyle:{  
       NumberStyle:true, //选中样式是否为数字,不传默认显示勾选样式  
       isbtn:true, //完成按钮是否显示背景颜色  
       //如需修改按钮颜色需要在res/values/colors.xml里修改btn_primary和btn_hover的颜色值和按下后的颜色值  
       SelectTextColor:'#ffffff',//选择结果文字颜色  
      // SelectText:'%1$d/%2$d 完成',  
       SelectText:'使用(%1$d)',//选择结果要显示的文字,不传默认显示:已完成  
       StatusBarColor:'#8E07FD',  
       OriginalTextColor:'#8E07FD',  
   }  
    }  
       }  
//打开相册或视频  
   RHFselcet.getPicture( parameter, (data) => {  
    // console.log(data["mediaArray"])  
    let arrData = data["mediaArray"]  
    let jsonData = JSON.stringify(arrData)  
    console.log(jsonData)  
//回调结果:[{"mimeType":"video/mp4","num":1,"Pathurl":"/storage/emulated/0/DCIM/Camera/lv_0_20241025172529.mp4","height":1920,"fiename":"lv_0_20241025172529.mp4","path":"content://media/external/video/media/427606","Pathid":427606,"duration":16393,"size":20002573,"FolderName":"Camera","dateAddedTime":1729848339,"width":1080},{"mimeType":"video/mp4","num":2,"Pathurl":"/storage/emulated/0/DCIM/Camera/video_20241025_170438.mp4","height":1920,"fiename":"video_20241025_170438.mp4","path":"content://media/external/video/media/427604","Pathid":427604,"duration":16331,"size":35778914,"FolderName":"Camera","dateAddedTime":1729847078,"width":1080}]  
    let DatalArray = JSON.parseArray(jsonData)  
    console.log(DatalArray)  
});
收起阅读 »

基于flutter3.32+window_manager仿mac/wins桌面版os

flutter

flutter3-winchat:最新版原创flutter3.32+dart3.8+getx+window_manager+reorderables桌面端仿macOS/windows风格os实例。毛玻璃虚化背景、桌面栅格布局模板、Dock菜单可拖拽排序、自定义JSON配置桌面/dock菜单。

实现技术

  • 跨平台框架:Flutter3.32+Dart3.8
  • 窗口管理:window_manager^0.5.1
  • 路由/状态管理:get^4.7.2
  • 缓存服务:get_storage^2.1.1
  • 拖拽排序:reorderables^0.6.0
  • 图表组件:fl_chart^1.0.0
  • 托盘管理:system_tray^2.0.3

项目框架结构

> ### flutter3-macos桌面端os系统已经更新到我的原创作品集。
> flutter3.32+window_manager桌面端OS系统

热文推荐

最新研发flutter3.27+bitsdojo_window+getx客户端仿微信聊天Exe应用
最新版Flutter3.32+Dart3.8跨平台仿微信app聊天界面|朋友圈
最新版uniapp+vue3+uv-ui跨三端短视频+直播+聊天【H5+小程序+App端】
原创uniapp+vue3+deepseek+uv-ui跨端实战仿deepseek/豆包流式ai聊天对话助手。
vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果
Electron35-DeepSeek桌面端AI系统|vue3.5+electron+arco客户端ai模板
uniapp+vue3聊天室|uni-app+vite4+uv-ui跨端仿微信app聊天语音/朋友圈
uniapp+vue3酒店预订|vite5+uniapp预约订房系统模板(h5+小程序+App端)
Electron32-Vue3OS桌面版os系统|vue3+electron+arco客户端OS管理模板
Tauri2.0+Vite5聊天室|vue3+tauri2+element-plus仿微信|tauri聊天应用
tauri2.0-admin桌面端后台系统|Tauri2+Vite5+ElementPlus管理后台EXE程序

继续阅读 »

flutter3-winchat:最新版原创flutter3.32+dart3.8+getx+window_manager+reorderables桌面端仿macOS/windows风格os实例。毛玻璃虚化背景、桌面栅格布局模板、Dock菜单可拖拽排序、自定义JSON配置桌面/dock菜单。

实现技术

  • 跨平台框架:Flutter3.32+Dart3.8
  • 窗口管理:window_manager^0.5.1
  • 路由/状态管理:get^4.7.2
  • 缓存服务:get_storage^2.1.1
  • 拖拽排序:reorderables^0.6.0
  • 图表组件:fl_chart^1.0.0
  • 托盘管理:system_tray^2.0.3

项目框架结构

> ### flutter3-macos桌面端os系统已经更新到我的原创作品集。
> flutter3.32+window_manager桌面端OS系统

热文推荐

最新研发flutter3.27+bitsdojo_window+getx客户端仿微信聊天Exe应用
最新版Flutter3.32+Dart3.8跨平台仿微信app聊天界面|朋友圈
最新版uniapp+vue3+uv-ui跨三端短视频+直播+聊天【H5+小程序+App端】
原创uniapp+vue3+deepseek+uv-ui跨端实战仿deepseek/豆包流式ai聊天对话助手。
vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果
Electron35-DeepSeek桌面端AI系统|vue3.5+electron+arco客户端ai模板
uniapp+vue3聊天室|uni-app+vite4+uv-ui跨端仿微信app聊天语音/朋友圈
uniapp+vue3酒店预订|vite5+uniapp预约订房系统模板(h5+小程序+App端)
Electron32-Vue3OS桌面版os系统|vue3+electron+arco客户端OS管理模板
Tauri2.0+Vite5聊天室|vue3+tauri2+element-plus仿微信|tauri聊天应用
tauri2.0-admin桌面端后台系统|Tauri2+Vite5+ElementPlus管理后台EXE程序

收起阅读 »

uniapp Android离线打包,覆盖安装后,界面回到上一版本的问题

资源升级 Android离线打包

Uniapp离线打包App覆盖安装后界面回退问题分析与解决

问题描述

  • 环境信息
    • 开发框架:Uniapp
    • 硬件平台:RK3568开发板
    • 系统版本:Android 11
    • 打包方式:离线打包
  • 现象
    • 覆盖安装新版本后,偶现界面显示为上一个版本
    • 重启应用后恢复正常(显示最新版本界面)
    • 问题出现概率难统计,有时需要设备断电重启,有时设备软重启也会出现

可能原因分析

1. 资源缓存问题

  • Android AssetManager缓存机制
  • Uniapp资源热更新机制冲突

2. 安装流程时序问题

  • APK安装完成但资源未完全解压
  • RK3568芯片组特定处理逻辑

3. WebView缓存问题

  • Uniapp基于WebView的渲染机制
  • 旧版本缓存未被及时清除

排查过程

  1. 日志分析

    • 增加安装完成回调日志
    • 监控WebView初始化过程
  2. 测试对比

    • 不同Android版本设备对比测试
    • 相同代码在不同主板的表现
  3. 关键发现

    • 问题仅出现在RK3568这款主板上
    • 安装启动后,在 /data/data/${packageName}/ 目录下,发现了一个apps文件夹,该文件夹下,居然包含了离线打包的资源文件;
    • 正常启动过程,资源文件应在 /data/data/${packageName}/files 目录下。同时查看该目录,也发现了离线打包的资源文件;
    • 分别打开两个目录下,资源文件里的manifest.json 文件,对比versionCode 和versionName,豁然开朗。

正常目录:

异常目录:

可能原因分析

1. 主板固件问题

  • Android固件为厂商定制,存在Bug。测试方式,使用纯原生应用,检测验证;

    2. uniapp框架Bug;

  • 个人感觉可能性非常大。此处仅为技术方面探讨,不存在抹黑、针对任何方。😅

解决方案

临时解决方案

  • 要从根本原因这两方面入手解决都比较困难。知道了原因,就好针对性的解决了。
    每次启动的时候,检测异常目录下,是否存在manifest.json文件,如存在则尝试解析其中的versionCode和versionName。不管是否一致,都应删除掉异常目录下的所有文件,然后重启应用。主要示例代码如下:

    private fun checkAndCleanLegacyResources() {  
        try {  
            // 获取应用私有目录  
            val datasDir = applicationContext.dataDir  
    
            TARGET_FOLDER_NAME=BaseInfo.sDefaultBootApp  
            // 构建目标目录路径  
            val targetDir = File(datasDir, "apps/$TARGET_FOLDER_NAME")  
    
            // 检查目标目录是否存在  
            if (!targetDir.exists() || !targetDir.isDirectory) {  
                ToastUtils.show("ResourceCleaner 目标目录不存在: " + targetDir.absolutePath)  
                return  
            }  
    
            // 检查 manifest 文件是否存在  
            val manifestFile = File(targetDir, MANIFEST_PATH)  
            if (!manifestFile.exists()) {  
                ToastUtils.show("ResourceCleaner manifest.json 文件不存在")  
                return  
            }  
    
            // 解析 manifest 文件中的版本信息  
            val legacyVersion = parseManifestVersion(manifestFile)  
            if (legacyVersion == null) {  
                ToastUtils.show("ResourceCleaner 无法解析版本信息")  
                return  
            }  
    
            // 获取当前应用版本  
            val currentVersion = getCurrentAppVersion()  
    
            // 比较版本并决定是否删除  
            if (shouldDeleteLegacyResources(legacyVersion, currentVersion)) {  
                ToastUtils.show("ResourceCleaner 发现旧版本资源: $legacyVersion (当前版本: $currentVersion)")  
                deleteRecursive(targetDir)  
                ToastUtils.show("ResourceCleaner 已成功删除旧资源")  
            } else {  
                ToastUtils.show("ResourceCleaner 资源版本是最新的: $legacyVersion")  
            }  
        } catch (e: Exception) {  
            ToastUtils.show("ResourceCleaner 清理资源时出错: " + e.message)  
        }  
    }  
    private fun deleteRecursive(fileOrDirectory: File) {  
        if (fileOrDirectory.isDirectory) {  
            val children = fileOrDirectory.listFiles()  
            if (children != null) {  
                for (child in children) {  
                    deleteRecursive(child)  
                }  
            }  
        }  
        fileOrDirectory.delete()  
    }

    看下效果

应用重启后,旧版资源文件已经被删除了,重启几次,持续观察了几天,也没有出现回退的情况。

尽管方案不太完美,用户体验略差,期待官方重视,或者大家有更好的解决办法,一起交流下吧。

继续阅读 »

Uniapp离线打包App覆盖安装后界面回退问题分析与解决

问题描述

  • 环境信息
    • 开发框架:Uniapp
    • 硬件平台:RK3568开发板
    • 系统版本:Android 11
    • 打包方式:离线打包
  • 现象
    • 覆盖安装新版本后,偶现界面显示为上一个版本
    • 重启应用后恢复正常(显示最新版本界面)
    • 问题出现概率难统计,有时需要设备断电重启,有时设备软重启也会出现

可能原因分析

1. 资源缓存问题

  • Android AssetManager缓存机制
  • Uniapp资源热更新机制冲突

2. 安装流程时序问题

  • APK安装完成但资源未完全解压
  • RK3568芯片组特定处理逻辑

3. WebView缓存问题

  • Uniapp基于WebView的渲染机制
  • 旧版本缓存未被及时清除

排查过程

  1. 日志分析

    • 增加安装完成回调日志
    • 监控WebView初始化过程
  2. 测试对比

    • 不同Android版本设备对比测试
    • 相同代码在不同主板的表现
  3. 关键发现

    • 问题仅出现在RK3568这款主板上
    • 安装启动后,在 /data/data/${packageName}/ 目录下,发现了一个apps文件夹,该文件夹下,居然包含了离线打包的资源文件;
    • 正常启动过程,资源文件应在 /data/data/${packageName}/files 目录下。同时查看该目录,也发现了离线打包的资源文件;
    • 分别打开两个目录下,资源文件里的manifest.json 文件,对比versionCode 和versionName,豁然开朗。

正常目录:

异常目录:

可能原因分析

1. 主板固件问题

  • Android固件为厂商定制,存在Bug。测试方式,使用纯原生应用,检测验证;

    2. uniapp框架Bug;

  • 个人感觉可能性非常大。此处仅为技术方面探讨,不存在抹黑、针对任何方。😅

解决方案

临时解决方案

  • 要从根本原因这两方面入手解决都比较困难。知道了原因,就好针对性的解决了。
    每次启动的时候,检测异常目录下,是否存在manifest.json文件,如存在则尝试解析其中的versionCode和versionName。不管是否一致,都应删除掉异常目录下的所有文件,然后重启应用。主要示例代码如下:

    private fun checkAndCleanLegacyResources() {  
        try {  
            // 获取应用私有目录  
            val datasDir = applicationContext.dataDir  
    
            TARGET_FOLDER_NAME=BaseInfo.sDefaultBootApp  
            // 构建目标目录路径  
            val targetDir = File(datasDir, "apps/$TARGET_FOLDER_NAME")  
    
            // 检查目标目录是否存在  
            if (!targetDir.exists() || !targetDir.isDirectory) {  
                ToastUtils.show("ResourceCleaner 目标目录不存在: " + targetDir.absolutePath)  
                return  
            }  
    
            // 检查 manifest 文件是否存在  
            val manifestFile = File(targetDir, MANIFEST_PATH)  
            if (!manifestFile.exists()) {  
                ToastUtils.show("ResourceCleaner manifest.json 文件不存在")  
                return  
            }  
    
            // 解析 manifest 文件中的版本信息  
            val legacyVersion = parseManifestVersion(manifestFile)  
            if (legacyVersion == null) {  
                ToastUtils.show("ResourceCleaner 无法解析版本信息")  
                return  
            }  
    
            // 获取当前应用版本  
            val currentVersion = getCurrentAppVersion()  
    
            // 比较版本并决定是否删除  
            if (shouldDeleteLegacyResources(legacyVersion, currentVersion)) {  
                ToastUtils.show("ResourceCleaner 发现旧版本资源: $legacyVersion (当前版本: $currentVersion)")  
                deleteRecursive(targetDir)  
                ToastUtils.show("ResourceCleaner 已成功删除旧资源")  
            } else {  
                ToastUtils.show("ResourceCleaner 资源版本是最新的: $legacyVersion")  
            }  
        } catch (e: Exception) {  
            ToastUtils.show("ResourceCleaner 清理资源时出错: " + e.message)  
        }  
    }  
    private fun deleteRecursive(fileOrDirectory: File) {  
        if (fileOrDirectory.isDirectory) {  
            val children = fileOrDirectory.listFiles()  
            if (children != null) {  
                for (child in children) {  
                    deleteRecursive(child)  
                }  
            }  
        }  
        fileOrDirectory.delete()  
    }

    看下效果

应用重启后,旧版资源文件已经被删除了,重启几次,持续观察了几天,也没有出现回退的情况。

尽管方案不太完美,用户体验略差,期待官方重视,或者大家有更好的解决办法,一起交流下吧。

收起阅读 »

nvue 踩坑整理,持续更新

iOS 安卓 nvue

最近使用nvue开发手机app,踩了不少坑,虽然大部分都勉强解决了,办法十分邪道,也仍有部分问题无解,特此整理公布,以赠后来者。

打包相关

1.关于midbutton高度对齐的问题。

答: 因原生平台差异,对图片型的midbutton设置高度,在安卓和苹果端可能产生无法垂直对齐的问题,又因为pages.json并未提供条件编译功能,建议手动校准修改两端高度,分别打包安卓和苹果端。

有解的问题

1.关于nvue的v-show能力。

答: 因 nvue 渲染层采用 weex,底层对元素的渲染机制,容易造成切换元素时的抖动现象。插件市场有相关 weex-v-show 插件,或者使用动态设置宽度和 opacity 达到类似效果。

2.关于input组件动态设置placeholder样式。

答: input组件可通过设置placeholder-style和placeholder-class属性设置placeholder的样式,但仅初次设置有效,无法动态改变,weex文档里提供了placeholder-color属性,可以通过css动态设置,改变placeholder的颜色;但是nvue编译器会对此属性抛出警告,在意者可通过写成行内样式进行规避。

3.关于overflow属性切换。

答: 因安卓端只支持overflow:hidden,此属性几乎无用,但weex文档里提到过只有同时满足以下四个条件,父view才会裁剪子view:
1.父view是 div, a, cell, refresh 或 loading
2.系统版本是 Android 4.3 或更高。
3.系统版本不是 Andorid 7.0。
4.父 view 没有 background-image 属性或系统版本是 Android 5.0 或更高。
这也意味着,似乎可以通过打破上述的任一条件限制,以此间接实现overflow:visible的效果?此想法仅笔者猜测,暂未实践。

4.关于subNvue的相关问题。

答: 可参考此回答,https://ask.dcloud.net.cn/article/41670?notification_id-1540939__item_id-65695。

5.关于swipera-action组件。

答: uni-ui提供的swipera-action在app端表现不佳,经常有异常抖动的问题,笔者在实践中采用了fui-swipe-action,当clickClose属性设置为false时,体验良好,基本满足需求。如果clickClose设置为true,也会出现和uni-ui类似的问题,猜测系bindingX的编写问题。

6.关于swiper组件。

答: 因底层限制,且nvue几乎不再维护,swiper相关的问题几乎无解,如设置动画时长或关闭动画等,笔者只能提出两种未经实践的想法,第一种是修改uniapp源码,手动添加weex的forbid-slide-animation属性;第二种即通过bindingX,自己实现一个swiper组件。

7.关于同行text设置不同样式的问题。

答: 因nvue不支持text嵌套,且仅有text组件能设置文字样式,此问题几乎无解,如果你没有文字换行的需求,可通过拆分文本,塞入不同的text并设置不同的样式;如果你需要换行,笔者的实践办法是更换为rich-text组件,数组型的nodes属性可以完美解决此类问题,虽然编写元素数组比较麻烦,但好在有效。

8.关于list和waterfall的长列表问题。

答: 正常使用几乎不存在性能问题,唯一的优化方向,可能就是当cell包含图片时,考虑优化图片大小减少内存消耗,另外cell组件的delete-animation="default"和insert-animation="default"需要明写,否则不会有默认的动画效果。

关于长列表内部的refresh和loading组件,refresh组件可以正常使用,几乎不存在兼容性问题,但loading组件需要分情况处理:

  1. 对于ios端,当列表元素长度大于1屏时,可以正常使用loading组件,当列表元素不足1屏时,会出现loading组件停留在列表顶部的Bug,建议在此种情况下,使用列表内置的header组件模拟loading(注意把header组件放置在列表底部),达到类似效果,当元素超过1屏后,再切换。
  2. 对于安卓端普通长列表,可以正常使用loading组件。
  3. 对于安卓端嵌套长列表,loading组件无法正常渲染,此种情况同样建议采用header组件模拟loading(注意把header组件放置在列表底部),基本可以达到比较良好的效果。

另外注意,无论是refresh还是Loading,其内置的loading-indicator组件都不建议使用,因其动画效果几乎不存在,考虑使用第三方图标替代。

9.底部输入区和键盘弹出的无缝动画协调问题。

答: 大部分App都会有留言或评论功能,往往需要从底部弹出自定义的留言区域,然而手机软键盘也会同时弹出,如何协同两者弹出时的动画效果,也需要分情况考虑,经观察,大部分app在ios端往往可以做到比较完美的效果,即软键盘从底部弹出时,留言区会顺势被顶起,整个过程流畅且无缝,观感十分好;而安卓端则没那么完美,软键盘和留言区的弹出动画往往是分先后完成的。

关于实际编码中如何解决这个问题,如果是原生编码,可以通过获取软键盘的属性来解决,uniapp做不到这点,只能曲线救国:

  1. 在ios端,考虑以subNvue来渲染弹出层,手动focus弹出层中的输入组件,并将adjust-position设置为true,弹出的键盘会把整个subNvue弹起,整个过程流畅无缝,效果很好,类似的办法也可以考虑使用透明的新页面,不过如何控制路由和如何隐藏都是个问题,subNvue会简单许多,另外第一次弹出的时候会稍有卡顿,这一点在其余app上也经常出现, 属于正常现象,后续的弹出就很正常了。
  2. 在安卓端,如上面所说的,暂时没有无缝弹出的办法,只能通过dom控制动画去尽量贴合键盘的弹起速度,关于这点不必花大力气去模拟贴合,毕竟做不到完美,能用即可。

10.关于安卓机进入新页面,image组件会出现闪白问题。

答: 因weex底层渲染机制,此问题无法直接解决,哪怕更换成Img组件一样会有此问题,只能通过两种方式缓解:

  1. 添加骨架图,等图片触发load事件后再显示。
  2. 在能计算出图片出现的位置和大小的情况下,可以考虑使用plus.nativeObj.view预先渲染图片,经笔者测试,此方式不会有延迟和闪白问题,结合startAnimation方法,几乎可以做到和页面进入动画无缝贴合,等到页面完全进入后,再关闭view即可。

11.关于如何在nvue绘制三角形元素。

答: 传统web绘制三角形的办法基于border做文章,但nvue支持的css属性有限,无法直接解决,笔者的办法是渲染两个元素,将底层元素旋转,用上层元素遮盖底层元素,以此来达到模拟三角形的效果。

暂时无解的问题

1.image 组件 mode 设置为 aspectFit 的时候,如果给 Image 加上 border-radius,安卓端图片显示会出现异常拉伸,此问题系框架底层bug,目前无解决办法。

2.原生配置的tabbar,点击切换tabbar后如何触发动画特效问题,笔者有两者思路,一种是全局只有一个页面,即单页面富应用,nvue自行渲染底部tabbar,不过这种方案应该只适用小型项目;另一种则是结合gif图片,笔者看到网络上有提到,gif可设置只循环一次,具体效果暂未实践,留待后续验证。

继续阅读 »

最近使用nvue开发手机app,踩了不少坑,虽然大部分都勉强解决了,办法十分邪道,也仍有部分问题无解,特此整理公布,以赠后来者。

打包相关

1.关于midbutton高度对齐的问题。

答: 因原生平台差异,对图片型的midbutton设置高度,在安卓和苹果端可能产生无法垂直对齐的问题,又因为pages.json并未提供条件编译功能,建议手动校准修改两端高度,分别打包安卓和苹果端。

有解的问题

1.关于nvue的v-show能力。

答: 因 nvue 渲染层采用 weex,底层对元素的渲染机制,容易造成切换元素时的抖动现象。插件市场有相关 weex-v-show 插件,或者使用动态设置宽度和 opacity 达到类似效果。

2.关于input组件动态设置placeholder样式。

答: input组件可通过设置placeholder-style和placeholder-class属性设置placeholder的样式,但仅初次设置有效,无法动态改变,weex文档里提供了placeholder-color属性,可以通过css动态设置,改变placeholder的颜色;但是nvue编译器会对此属性抛出警告,在意者可通过写成行内样式进行规避。

3.关于overflow属性切换。

答: 因安卓端只支持overflow:hidden,此属性几乎无用,但weex文档里提到过只有同时满足以下四个条件,父view才会裁剪子view:
1.父view是 div, a, cell, refresh 或 loading
2.系统版本是 Android 4.3 或更高。
3.系统版本不是 Andorid 7.0。
4.父 view 没有 background-image 属性或系统版本是 Android 5.0 或更高。
这也意味着,似乎可以通过打破上述的任一条件限制,以此间接实现overflow:visible的效果?此想法仅笔者猜测,暂未实践。

4.关于subNvue的相关问题。

答: 可参考此回答,https://ask.dcloud.net.cn/article/41670?notification_id-1540939__item_id-65695。

5.关于swipera-action组件。

答: uni-ui提供的swipera-action在app端表现不佳,经常有异常抖动的问题,笔者在实践中采用了fui-swipe-action,当clickClose属性设置为false时,体验良好,基本满足需求。如果clickClose设置为true,也会出现和uni-ui类似的问题,猜测系bindingX的编写问题。

6.关于swiper组件。

答: 因底层限制,且nvue几乎不再维护,swiper相关的问题几乎无解,如设置动画时长或关闭动画等,笔者只能提出两种未经实践的想法,第一种是修改uniapp源码,手动添加weex的forbid-slide-animation属性;第二种即通过bindingX,自己实现一个swiper组件。

7.关于同行text设置不同样式的问题。

答: 因nvue不支持text嵌套,且仅有text组件能设置文字样式,此问题几乎无解,如果你没有文字换行的需求,可通过拆分文本,塞入不同的text并设置不同的样式;如果你需要换行,笔者的实践办法是更换为rich-text组件,数组型的nodes属性可以完美解决此类问题,虽然编写元素数组比较麻烦,但好在有效。

8.关于list和waterfall的长列表问题。

答: 正常使用几乎不存在性能问题,唯一的优化方向,可能就是当cell包含图片时,考虑优化图片大小减少内存消耗,另外cell组件的delete-animation="default"和insert-animation="default"需要明写,否则不会有默认的动画效果。

关于长列表内部的refresh和loading组件,refresh组件可以正常使用,几乎不存在兼容性问题,但loading组件需要分情况处理:

  1. 对于ios端,当列表元素长度大于1屏时,可以正常使用loading组件,当列表元素不足1屏时,会出现loading组件停留在列表顶部的Bug,建议在此种情况下,使用列表内置的header组件模拟loading(注意把header组件放置在列表底部),达到类似效果,当元素超过1屏后,再切换。
  2. 对于安卓端普通长列表,可以正常使用loading组件。
  3. 对于安卓端嵌套长列表,loading组件无法正常渲染,此种情况同样建议采用header组件模拟loading(注意把header组件放置在列表底部),基本可以达到比较良好的效果。

另外注意,无论是refresh还是Loading,其内置的loading-indicator组件都不建议使用,因其动画效果几乎不存在,考虑使用第三方图标替代。

9.底部输入区和键盘弹出的无缝动画协调问题。

答: 大部分App都会有留言或评论功能,往往需要从底部弹出自定义的留言区域,然而手机软键盘也会同时弹出,如何协同两者弹出时的动画效果,也需要分情况考虑,经观察,大部分app在ios端往往可以做到比较完美的效果,即软键盘从底部弹出时,留言区会顺势被顶起,整个过程流畅且无缝,观感十分好;而安卓端则没那么完美,软键盘和留言区的弹出动画往往是分先后完成的。

关于实际编码中如何解决这个问题,如果是原生编码,可以通过获取软键盘的属性来解决,uniapp做不到这点,只能曲线救国:

  1. 在ios端,考虑以subNvue来渲染弹出层,手动focus弹出层中的输入组件,并将adjust-position设置为true,弹出的键盘会把整个subNvue弹起,整个过程流畅无缝,效果很好,类似的办法也可以考虑使用透明的新页面,不过如何控制路由和如何隐藏都是个问题,subNvue会简单许多,另外第一次弹出的时候会稍有卡顿,这一点在其余app上也经常出现, 属于正常现象,后续的弹出就很正常了。
  2. 在安卓端,如上面所说的,暂时没有无缝弹出的办法,只能通过dom控制动画去尽量贴合键盘的弹起速度,关于这点不必花大力气去模拟贴合,毕竟做不到完美,能用即可。

10.关于安卓机进入新页面,image组件会出现闪白问题。

答: 因weex底层渲染机制,此问题无法直接解决,哪怕更换成Img组件一样会有此问题,只能通过两种方式缓解:

  1. 添加骨架图,等图片触发load事件后再显示。
  2. 在能计算出图片出现的位置和大小的情况下,可以考虑使用plus.nativeObj.view预先渲染图片,经笔者测试,此方式不会有延迟和闪白问题,结合startAnimation方法,几乎可以做到和页面进入动画无缝贴合,等到页面完全进入后,再关闭view即可。

11.关于如何在nvue绘制三角形元素。

答: 传统web绘制三角形的办法基于border做文章,但nvue支持的css属性有限,无法直接解决,笔者的办法是渲染两个元素,将底层元素旋转,用上层元素遮盖底层元素,以此来达到模拟三角形的效果。

暂时无解的问题

1.image 组件 mode 设置为 aspectFit 的时候,如果给 Image 加上 border-radius,安卓端图片显示会出现异常拉伸,此问题系框架底层bug,目前无解决办法。

2.原生配置的tabbar,点击切换tabbar后如何触发动画特效问题,笔者有两者思路,一种是全局只有一个页面,即单页面富应用,nvue自行渲染底部tabbar,不过这种方案应该只适用小型项目;另一种则是结合gif图片,笔者看到网络上有提到,gif可设置只循环一次,具体效果暂未实践,留待后续验证。

收起阅读 »

IOS上uni.writeBLECharacteristicValue不进任何回调解决方案

iOS

在IOS上使用uni.writeBLECharacteristicValue写入数据,success、fail、complete是不进入任何一个的。

经我反复测试,失败是会进入fail的和complete的。所以不进入任何回调默认成功。
所以IOS上要加一个超时逻辑,进入超时就算是success.核心代码如下。
封装的sendData函数
let sendBuffer = new Promise(async (resolve, reject) => {

        var promise = new Promise((succ, err) => {  

            uni.writeBLECharacteristicValue({  
                deviceId: device.deviceId,  
                serviceId: device.writeServiceId,  
                characteristicId: device.wirteCharactId,  
                value: buffer,                    
                success: () => {  
                    console.log("发送数据成功");  
                    succ();  
                },  
                fail: (ex) => {  
                    ex=this.getError(ex);  
                    console.log("发送数据失败", ex);  

                    err(ex);  
                }  
            });  
        });  
        if (plus.os.name == 'iOS') {  

            function timeOut(ms) {  
                return new Promise((_, err) => {  
                    setTimeout(() => {  
                        err({  
                            code: -1,  
                            errMsg: '超时了'  
                        })  
                    }, ms);  
                });  
            }  

            Promise.race([timeOut(ms), promise]).then(resolve).catch((ex) => {  
                console.log("ex=", ex);  
                if (ex.code == -1) {  
                    resolve();  
                } else {  
                    reject(ex);  
                }  

            }).finally(() => {  
                //console.log("完成了")  
            });  
        } else {  
            promise.then(resolve).catch(reject);  
        }  
    });  

    return sendBuffer;  

还有,频繁调用该方法,会进入fail,此时错误编码是10007,官方文档解释是“当前特征值不支持此操作”,
解决方案是暂停一下再重试,直到成功或提示其他错误。
调用的核心代码:
ble.sendData(f.deviceId, buffer, f.writeServiceId, f
.wirteCharactId, inteval).then(() => {

                                    console.log("发送一段成功,发送下一段");  
                                    setTimeout(sendNextVideoPacket, inteval);  
                                }).catch(err => {  
                                    if(err.code=='10007'){//遇到这个错误自动重新发送这一段数据  

                                        setTimeout(sendNextVideoPacket, inteval);//自动重试,经测试大约一秒钟后会再次成功,所以interval这里可以是1000    
                                    }else{  
                                                                                   //处理发送失败的逻辑  
                                                                          }  

                                });
继续阅读 »

在IOS上使用uni.writeBLECharacteristicValue写入数据,success、fail、complete是不进入任何一个的。

经我反复测试,失败是会进入fail的和complete的。所以不进入任何回调默认成功。
所以IOS上要加一个超时逻辑,进入超时就算是success.核心代码如下。
封装的sendData函数
let sendBuffer = new Promise(async (resolve, reject) => {

        var promise = new Promise((succ, err) => {  

            uni.writeBLECharacteristicValue({  
                deviceId: device.deviceId,  
                serviceId: device.writeServiceId,  
                characteristicId: device.wirteCharactId,  
                value: buffer,                    
                success: () => {  
                    console.log("发送数据成功");  
                    succ();  
                },  
                fail: (ex) => {  
                    ex=this.getError(ex);  
                    console.log("发送数据失败", ex);  

                    err(ex);  
                }  
            });  
        });  
        if (plus.os.name == 'iOS') {  

            function timeOut(ms) {  
                return new Promise((_, err) => {  
                    setTimeout(() => {  
                        err({  
                            code: -1,  
                            errMsg: '超时了'  
                        })  
                    }, ms);  
                });  
            }  

            Promise.race([timeOut(ms), promise]).then(resolve).catch((ex) => {  
                console.log("ex=", ex);  
                if (ex.code == -1) {  
                    resolve();  
                } else {  
                    reject(ex);  
                }  

            }).finally(() => {  
                //console.log("完成了")  
            });  
        } else {  
            promise.then(resolve).catch(reject);  
        }  
    });  

    return sendBuffer;  

还有,频繁调用该方法,会进入fail,此时错误编码是10007,官方文档解释是“当前特征值不支持此操作”,
解决方案是暂停一下再重试,直到成功或提示其他错误。
调用的核心代码:
ble.sendData(f.deviceId, buffer, f.writeServiceId, f
.wirteCharactId, inteval).then(() => {

                                    console.log("发送一段成功,发送下一段");  
                                    setTimeout(sendNextVideoPacket, inteval);  
                                }).catch(err => {  
                                    if(err.code=='10007'){//遇到这个错误自动重新发送这一段数据  

                                        setTimeout(sendNextVideoPacket, inteval);//自动重试,经测试大约一秒钟后会再次成功,所以interval这里可以是1000    
                                    }else{  
                                                                                   //处理发送失败的逻辑  
                                                                          }  

                                });
收起阅读 »

悦邻(郑州)招后端研发,看过来看过来!

招聘与外包

悦邻是一家专注于提升生鲜快销流通效率的商业服务公司,我们秉承实效第一、真诚、专注和创新的价值观。我们不仅招募优秀的人才,还愿意提供比市场更高的薪酬,公司提供每年两次的涨薪窗口期,我们重视伙伴的贡献和价值,并致力于为团队成员提供良好的工作环境和发展机会。

薪资福利:10000-15000元/月,年中年底两次发放奖金,平均有3月的薪资,根据个人贡献支付奖金。双休,缴纳六险二金,中午提供豪华午餐(四菜一汤一份水果),晚餐报销,9点30-18点30(中午休息1个半小时)。
岗位职责:
1.本科统招及以上学历,计算机相关专业,2年以上后端开发经验,熟悉Node.js领域常用的开源框架,如Express、NestJS、Koa等
2.熟悉分布式服务和微服务开发框架,熟悉缓存、消息等机制,如Redis、kafka等消息中间件
3.熟练掌握关系型或非关系型数据库,如MySQL,MongoDB,ElasticSearch等,能够熟练编写SQL语句并进行调优
4.优秀的文档编写能力,包括功能描述文档,业务流程图,时序图等
6.具备海量数据、大批量、高并发、高性能分析及处理相关经验者优先
7.具备前端相关工作经验优先,能够使用常见的UI框架进行页面开发,对HTML和CSS有一定了解
8.工作中参与过SaaS软件产品的设计与研发,渴望做产品并且有产品意识
9.对零售,仓储,供应链,直播等业务感兴趣或有相关工作经验者优先

岗位职责
1、分析产品功能需求,理解业务逻辑以及按规范执行开发流程,提高研发效率;
2、严格要求自己养成良好的编程规范,逻辑严谨,注释清晰,提高代码可读性和可维护性;
3、兴趣很重要,要不断的钻研项目中需要用到的开发技术,有良好的学习能力,能够独立负责产品的某个业务线;
4、遵守公司制度和企业文化,提高自身职业素养和业务水平,保质保量的完成各项工作任务;
5、积极参与需求分析、系统设计、软件研发、集成测试、安装部署以及基本的维护工作;
6、对各部门提出的产品的升级和缺陷等迅速反应,提供良好的技术支持服务;
7、按计划完成功能模块的功能设计、代码实现,代码编写和单元测试;

继续阅读 »

悦邻是一家专注于提升生鲜快销流通效率的商业服务公司,我们秉承实效第一、真诚、专注和创新的价值观。我们不仅招募优秀的人才,还愿意提供比市场更高的薪酬,公司提供每年两次的涨薪窗口期,我们重视伙伴的贡献和价值,并致力于为团队成员提供良好的工作环境和发展机会。

薪资福利:10000-15000元/月,年中年底两次发放奖金,平均有3月的薪资,根据个人贡献支付奖金。双休,缴纳六险二金,中午提供豪华午餐(四菜一汤一份水果),晚餐报销,9点30-18点30(中午休息1个半小时)。
岗位职责:
1.本科统招及以上学历,计算机相关专业,2年以上后端开发经验,熟悉Node.js领域常用的开源框架,如Express、NestJS、Koa等
2.熟悉分布式服务和微服务开发框架,熟悉缓存、消息等机制,如Redis、kafka等消息中间件
3.熟练掌握关系型或非关系型数据库,如MySQL,MongoDB,ElasticSearch等,能够熟练编写SQL语句并进行调优
4.优秀的文档编写能力,包括功能描述文档,业务流程图,时序图等
6.具备海量数据、大批量、高并发、高性能分析及处理相关经验者优先
7.具备前端相关工作经验优先,能够使用常见的UI框架进行页面开发,对HTML和CSS有一定了解
8.工作中参与过SaaS软件产品的设计与研发,渴望做产品并且有产品意识
9.对零售,仓储,供应链,直播等业务感兴趣或有相关工作经验者优先

岗位职责
1、分析产品功能需求,理解业务逻辑以及按规范执行开发流程,提高研发效率;
2、严格要求自己养成良好的编程规范,逻辑严谨,注释清晰,提高代码可读性和可维护性;
3、兴趣很重要,要不断的钻研项目中需要用到的开发技术,有良好的学习能力,能够独立负责产品的某个业务线;
4、遵守公司制度和企业文化,提高自身职业素养和业务水平,保质保量的完成各项工作任务;
5、积极参与需求分析、系统设计、软件研发、集成测试、安装部署以及基本的维护工作;
6、对各部门提出的产品的升级和缺陷等迅速反应,提供良好的技术支持服务;
7、按计划完成功能模块的功能设计、代码实现,代码编写和单元测试;

收起阅读 »