HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

【公告】uni-app首期鸿蒙支持计划

鸿蒙

鉴于很多开发者都在咨询uni-app对于鸿蒙next的兼容支持计划,特发此公告。

uni-app 支持鸿蒙

Vue 2 已于 2023 年 12 月 31 日达到终止支持时间,uni-app团队不计划提供 vue 2 的鸿蒙支持,仅规划 vue 3 的鸿蒙兼容支持。建议开发者尽快将历史项目从 vue 2 升级到 vue 3 版本。

鸿蒙系统的兼容支持,是个复杂浩瀚的工作。经和鸿蒙团队交流,我们共同制定了首期 uni-app 鸿蒙支持计划,分2个时间点发布:

  • 2024年6月底:发布 alpha 版,完成基础组件及API的适配,资深开发者可抢先体验;
  • 2024年9月底:发布正式版,完成常用组件及API的适配。

鉴于时间原因,部分功能模块将无法在首期 uni-app 鸿蒙支持计划中实现,比如:nvue、蓝牙、fileSystemManager、uniCloud等。

uni小程序SDK支持鸿蒙

预计2024年9月底,uni小程序SDK将会支持鸿蒙next,仅支持 vue 3 编译后的wgt文件,不支持 vue 2 版本。

uni-app x 支持鸿蒙

预计2024年12月底,完成 uni-app x 对于鸿蒙next的兼容支持。

继续阅读 »

鉴于很多开发者都在咨询uni-app对于鸿蒙next的兼容支持计划,特发此公告。

uni-app 支持鸿蒙

Vue 2 已于 2023 年 12 月 31 日达到终止支持时间,uni-app团队不计划提供 vue 2 的鸿蒙支持,仅规划 vue 3 的鸿蒙兼容支持。建议开发者尽快将历史项目从 vue 2 升级到 vue 3 版本。

鸿蒙系统的兼容支持,是个复杂浩瀚的工作。经和鸿蒙团队交流,我们共同制定了首期 uni-app 鸿蒙支持计划,分2个时间点发布:

  • 2024年6月底:发布 alpha 版,完成基础组件及API的适配,资深开发者可抢先体验;
  • 2024年9月底:发布正式版,完成常用组件及API的适配。

鉴于时间原因,部分功能模块将无法在首期 uni-app 鸿蒙支持计划中实现,比如:nvue、蓝牙、fileSystemManager、uniCloud等。

uni小程序SDK支持鸿蒙

预计2024年9月底,uni小程序SDK将会支持鸿蒙next,仅支持 vue 3 编译后的wgt文件,不支持 vue 2 版本。

uni-app x 支持鸿蒙

预计2024年12月底,完成 uni-app x 对于鸿蒙next的兼容支持。

收起阅读 »

uniappx中uni.switchTab跳转tabBar页面,url用相对路径报错绝对路径成功

uniappx中uni.switchTab跳转tabBar页面,url用相对路径报错目标页面不是tabBar页面使用绝对路径时可以

uniappx中uni.switchTab跳转tabBar页面,url用相对路径报错目标页面不是tabBar页面使用绝对路径时可以

开发一个定位拍照、打卡的程序,付费形式

外包

开发一个带地图定位拍照、打卡的程序,有偿付费

开发一个带地图定位拍照、打卡的程序,有偿付费

默认了加 count,选项卡字体无法实现变绿色效果,问题是出在哪里

<template>
<view class="home">
<view class="courseIntroduce_box">
<view class="courseIntroduce_des">
<view class="courseIntroduce_info">{{ introduce }}</view>
</view>
<courseIntroduceData :msg="introduceList"/>
<view class="question_line"></view>
<view class="courseIntroduce_tab_box">
<view class="courseIntroduce_tab_nav">
<view v-for="(item,index) in items" :class="{ 'btna':count === index }" @tap="change(index)" :key="index">{{ item }}</view>
</view>
<view class="courseIntroduce_tab_con">
<view class="discount_info" :class="{dis:count === 0}">
内容1
</view>
<view class="discount_info" :class="{dis:count === 1}">
内容2
</view>
</view>
</view>
</view>
</view>
</template>

<script>
import courseIntroduceData from "../../../components/courseIntroduce-data/courseIntroduce-data.vue"
export default {
data() {
return {
introduce:"",
introduceList:[],
items:["课程章节","课程介绍"],
count:0
}
},
components:{
courseIntroduceData
},
onLoad(options) {

        uni.request({  
            url:"https://www.itbaizhan.cn/api/course/detail",  
            data:{  
                id:options.id,  
                course:options.course  
            },  
            success: (res) => {  
                this.introduce = res.data.data.introduce  
                this.introduceList = res.data.data.introduceList  
            }  
        })  
    },  
    methods: {  
        change(index){  
            this.count = index;  
        }  
    }  
}  

</script>

<style lang="scss">
.courseIntroduce_box {
display: flex;
box-sizing: box;
flex-direction: column;
margin-bottom: 90px;

    .courseIntroduce_des {  
        display: flex;  
        box-sizing: border-box;  
        flex-direction: column;  
        justify-content: center;  
        align-items: center;  
        width: 100%;  
        background-color: #0c9c8f;  
        padding: 0 10px 15px;  
        overflow: hidden;  

        .courseIntroduce_info {  
            display: flex;  
            box-sizing: box;  
            width: 100%;  
            color: #fff;  
            font-size: 16px;  
            line-height: 24px;  
        }  
    }  

    .courseIntroduce_tab_box {  
        display: flex;  
        box-sizing: border-box;  
        flex-direction: column;  

        .courseIntroduce_tab_nav {  
            display: flex;  
            box-sizing: border-box;  
            flex-direction: row;  
            background-color: #fff;  
            border-bottom: 1px solid #e4e4e4;  
            margin-bottom: 20px;  

            view {  
                height: 50px;  
                line-height: 50px;  
                font-size: 16px;  
                flex-grow: 1;  
                text-align: center;  
                background-color: #fff;  
            }  
        }  
        .discount_info {  
            display: nome;  
        }  
        .btna {  
            display: flex;  
            box-sizing: border-box;  
            justify-content: center;  
            color: $zhu-color;  
            position: relative;  
        }  
        .btna::after {  
            content: '';  
            width: 40px;  
            height: 3px;  
            background-color: $zhu-color;  
            position: absolute;  
            bottom: 0;  
            left: 50%;  
            margin-left: -20px;  
        }  
        .dis {  
                display: block;  
        }  
    }  
}  

</style>
这个编程运行是这个效果:


我想要的是点课程章节或课程介绍会出现绿色字体效果,哪里有出错:

继续阅读 »

<template>
<view class="home">
<view class="courseIntroduce_box">
<view class="courseIntroduce_des">
<view class="courseIntroduce_info">{{ introduce }}</view>
</view>
<courseIntroduceData :msg="introduceList"/>
<view class="question_line"></view>
<view class="courseIntroduce_tab_box">
<view class="courseIntroduce_tab_nav">
<view v-for="(item,index) in items" :class="{ 'btna':count === index }" @tap="change(index)" :key="index">{{ item }}</view>
</view>
<view class="courseIntroduce_tab_con">
<view class="discount_info" :class="{dis:count === 0}">
内容1
</view>
<view class="discount_info" :class="{dis:count === 1}">
内容2
</view>
</view>
</view>
</view>
</view>
</template>

<script>
import courseIntroduceData from "../../../components/courseIntroduce-data/courseIntroduce-data.vue"
export default {
data() {
return {
introduce:"",
introduceList:[],
items:["课程章节","课程介绍"],
count:0
}
},
components:{
courseIntroduceData
},
onLoad(options) {

        uni.request({  
            url:"https://www.itbaizhan.cn/api/course/detail",  
            data:{  
                id:options.id,  
                course:options.course  
            },  
            success: (res) => {  
                this.introduce = res.data.data.introduce  
                this.introduceList = res.data.data.introduceList  
            }  
        })  
    },  
    methods: {  
        change(index){  
            this.count = index;  
        }  
    }  
}  

</script>

<style lang="scss">
.courseIntroduce_box {
display: flex;
box-sizing: box;
flex-direction: column;
margin-bottom: 90px;

    .courseIntroduce_des {  
        display: flex;  
        box-sizing: border-box;  
        flex-direction: column;  
        justify-content: center;  
        align-items: center;  
        width: 100%;  
        background-color: #0c9c8f;  
        padding: 0 10px 15px;  
        overflow: hidden;  

        .courseIntroduce_info {  
            display: flex;  
            box-sizing: box;  
            width: 100%;  
            color: #fff;  
            font-size: 16px;  
            line-height: 24px;  
        }  
    }  

    .courseIntroduce_tab_box {  
        display: flex;  
        box-sizing: border-box;  
        flex-direction: column;  

        .courseIntroduce_tab_nav {  
            display: flex;  
            box-sizing: border-box;  
            flex-direction: row;  
            background-color: #fff;  
            border-bottom: 1px solid #e4e4e4;  
            margin-bottom: 20px;  

            view {  
                height: 50px;  
                line-height: 50px;  
                font-size: 16px;  
                flex-grow: 1;  
                text-align: center;  
                background-color: #fff;  
            }  
        }  
        .discount_info {  
            display: nome;  
        }  
        .btna {  
            display: flex;  
            box-sizing: border-box;  
            justify-content: center;  
            color: $zhu-color;  
            position: relative;  
        }  
        .btna::after {  
            content: '';  
            width: 40px;  
            height: 3px;  
            background-color: $zhu-color;  
            position: absolute;  
            bottom: 0;  
            left: 50%;  
            margin-left: -20px;  
        }  
        .dis {  
                display: block;  
        }  
    }  
}  

</style>
这个编程运行是这个效果:


我想要的是点课程章节或课程介绍会出现绿色字体效果,哪里有出错:

收起阅读 »

uniapp ruoyi-app 中使用checkbox 无法选中问题

uniapp
<view class="flex align-center">  
        <checkbox-group>  
            <label>  
                <checkbox value="cb" checked="true" /> 记住密码  
            </label>  
        </checkbox-group>  
      </view>

colorui.css 文件中注释掉两处即可

checkbox::before {  
    font-family: "cuIcon";  
    content: "\e645";  
    position: absolute;  
    color: #ffffff !important;  
    top: 50%;  
    margin-top: -8px;  
    right: 5px;  
    font-size: 32upx;  
    line-height: 16px;  
    pointer-events: none;  
    transform: scale(1, 1);  
    transition: all 0.3s ease-in-out 0s;  
    /* z-index: 9; */  
}  

/* checkbox .uni-checkbox-input::before, */  
radio .uni-radio-input::before {  
    display: none;  
}
继续阅读 »
<view class="flex align-center">  
        <checkbox-group>  
            <label>  
                <checkbox value="cb" checked="true" /> 记住密码  
            </label>  
        </checkbox-group>  
      </view>

colorui.css 文件中注释掉两处即可

checkbox::before {  
    font-family: "cuIcon";  
    content: "\e645";  
    position: absolute;  
    color: #ffffff !important;  
    top: 50%;  
    margin-top: -8px;  
    right: 5px;  
    font-size: 32upx;  
    line-height: 16px;  
    pointer-events: none;  
    transform: scale(1, 1);  
    transition: all 0.3s ease-in-out 0s;  
    /* z-index: 9; */  
}  

/* checkbox .uni-checkbox-input::before, */  
radio .uni-radio-input::before {  
    display: none;  
}
收起阅读 »

全栈开发在线接单(有团队)

外包接单 外包

uniapp项目开发在线接单,可定制,可二开,时间充裕,保质保量
有需要请联系 vx : docxxlsx

uniapp项目开发在线接单,可定制,可二开,时间充裕,保质保量
有需要请联系 vx : docxxlsx

关于把google登录集成到uniapp安卓APP的最终帖子

Google登录

坦白讲,我真的被uniapp官方以及一些登录插件搞死了,总算是搞成了,记录一下有哪些坑。

先写有哪些坑,因为这些坑太痛苦,太隐藏了,然后官方也好,很多说明也好都没有提到!简直误国误民!
1,SHA1 有两个!一个是你用keystore通过命令行生成的,一个是google play console 里面的(在play console设置=》签名),这两个不是一样的,测试环境用前面的,正式环境用后面的。

我想问一下官方,为什么不说!为什么不说!我想问一下那些搞google 登录插件的,为什么不说!为什么不说!


2,不是创建web端的凭证,是创建安卓端的凭证,有人说用web端的,有一些插件也说是,反正我用的安卓端的,而且我的是要审核的,就是我这个最后要提交给谷歌审核,在审核通过前,好像只能验证100个用户。
3,firebase我也整了半天,反正最后是通过https://console.cloud.google.com/这里建立auth2.0凭证,选择的安卓客户端,然后弄成的。

反正最终我这个是可以的,也没有谁说过要去谷歌审核,但是话说回来,谷歌登录给你装到你app,你说你不要审核,你觉得可能吗!
如果是黄赌毒,用谷歌登录,是什么感觉!问题是我从来没有在官方以及其他任何一个帖子上看到。

继续阅读 »

坦白讲,我真的被uniapp官方以及一些登录插件搞死了,总算是搞成了,记录一下有哪些坑。

先写有哪些坑,因为这些坑太痛苦,太隐藏了,然后官方也好,很多说明也好都没有提到!简直误国误民!
1,SHA1 有两个!一个是你用keystore通过命令行生成的,一个是google play console 里面的(在play console设置=》签名),这两个不是一样的,测试环境用前面的,正式环境用后面的。

我想问一下官方,为什么不说!为什么不说!我想问一下那些搞google 登录插件的,为什么不说!为什么不说!


2,不是创建web端的凭证,是创建安卓端的凭证,有人说用web端的,有一些插件也说是,反正我用的安卓端的,而且我的是要审核的,就是我这个最后要提交给谷歌审核,在审核通过前,好像只能验证100个用户。
3,firebase我也整了半天,反正最后是通过https://console.cloud.google.com/这里建立auth2.0凭证,选择的安卓客户端,然后弄成的。

反正最终我这个是可以的,也没有谁说过要去谷歌审核,但是话说回来,谷歌登录给你装到你app,你说你不要审核,你觉得可能吗!
如果是黄赌毒,用谷歌登录,是什么感觉!问题是我从来没有在官方以及其他任何一个帖子上看到。

收起阅读 »

HBuilderX 发布目录从 H5 调整为 web 的兼容方案

uniapp HBuilderX

本文受众

本文用来解释和指导发布目录从 h5 调整 web 过程,收到影响的用户。

背景

HBuilderX 从 4.06 开始 uni-app 的 web 项目的编译目录从 h5 调整为 web 目录了。

  • 调整 uni-app运行 unpackage编译目录 h5目录名改为web

这一条改动很多人没有留意到,沿用历史的持续集成流程,会产生错误,因为目录发生了变更。

改动的原因,在相关 ask 社区中也有回答,主要还是为了统一概念。

早期 uni-app 在 web 端主要使用移动端展示内容,随着功能迭代和使用场景丰富,很多用户在宽屏 PC 端也会选择 uni-app 进行渲染和展示。

相关 api 的返回值,条件编译等已经陆陆续续统一到 web 上来,所以调整编译目录也是在计划中。

考虑到很多用户的使用场景,并不能轻易调整持续构建的流程,这里提供一些临时的兼容方案进行过度,最好的方案还是修改持续集成的流程。

临时兼容方案

如果是类似 jenkins/业务定制的 CICD 流程,可以尝试添加 post scripts,linux 服务器

cp -r unpackage/dist/build/web unpackage/dist/build/h5  
# 或者习惯用 mv,二选一即可  
mv unpackage/dist/build/web unpackage/dist/build/h5

如果你使用 windows 服务器

copy unpackage\\dist\\build\\web unpackage\\dist\\build\\h5

如果也不允许修改 jenkins ,可以尝试添加 npm scripts 添加上方命令。在 build 结束之后继续执行命令。

如果你仍有问题,欢迎留言讨论。

继续阅读 »

本文受众

本文用来解释和指导发布目录从 h5 调整 web 过程,收到影响的用户。

背景

HBuilderX 从 4.06 开始 uni-app 的 web 项目的编译目录从 h5 调整为 web 目录了。

  • 调整 uni-app运行 unpackage编译目录 h5目录名改为web

这一条改动很多人没有留意到,沿用历史的持续集成流程,会产生错误,因为目录发生了变更。

改动的原因,在相关 ask 社区中也有回答,主要还是为了统一概念。

早期 uni-app 在 web 端主要使用移动端展示内容,随着功能迭代和使用场景丰富,很多用户在宽屏 PC 端也会选择 uni-app 进行渲染和展示。

相关 api 的返回值,条件编译等已经陆陆续续统一到 web 上来,所以调整编译目录也是在计划中。

考虑到很多用户的使用场景,并不能轻易调整持续构建的流程,这里提供一些临时的兼容方案进行过度,最好的方案还是修改持续集成的流程。

临时兼容方案

如果是类似 jenkins/业务定制的 CICD 流程,可以尝试添加 post scripts,linux 服务器

cp -r unpackage/dist/build/web unpackage/dist/build/h5  
# 或者习惯用 mv,二选一即可  
mv unpackage/dist/build/web unpackage/dist/build/h5

如果你使用 windows 服务器

copy unpackage\\dist\\build\\web unpackage\\dist\\build\\h5

如果也不允许修改 jenkins ,可以尝试添加 npm scripts 添加上方命令。在 build 结束之后继续执行命令。

如果你仍有问题,欢迎留言讨论。

收起阅读 »

社交购物源码,小红书1:1还原,小红书APP社区源码模板软件开发社区电商支持即时通讯社交

小红书小程序

小红书APP源码,1:1还原小红书功能。可接定制二次开发需求。有疑问请随时联系在线客服,谢谢!

主要功能:推荐算法、视频、圈子、购物、直播、小视频、话题、投票、城市、天气、实名认证、发布、任务、邀请、深色模式、广告、采集、暗黑模式、多语言

客户端版本:本程序有H5、小程序、安卓、苹果、电脑端、5端同步共用同一套后台,数据互通。

本程序是我们自主开发,不依赖第三方系统。
接口开发语言:PHP,mysql,thinkphp5
官网地址:www.suxiangw.com
服务承诺:提供技术、维护、更新,提供源代码,合同等服务

继续阅读 »

小红书APP源码,1:1还原小红书功能。可接定制二次开发需求。有疑问请随时联系在线客服,谢谢!

主要功能:推荐算法、视频、圈子、购物、直播、小视频、话题、投票、城市、天气、实名认证、发布、任务、邀请、深色模式、广告、采集、暗黑模式、多语言

客户端版本:本程序有H5、小程序、安卓、苹果、电脑端、5端同步共用同一套后台,数据互通。

本程序是我们自主开发,不依赖第三方系统。
接口开发语言:PHP,mysql,thinkphp5
官网地址:www.suxiangw.com
服务承诺:提供技术、维护、更新,提供源代码,合同等服务

收起阅读 »

uniapp Composition API 写法,onLaunch和onLoad的异步问题最新解决方案,简单易懂

hook 登录状态 异步执行 微信登录

在使用 uni-app 开发项目时,会遇到需要在 onLaunch 中请求接口返回结果,并且此结果在项目各个页面的 onLoad 中都有可能使用到的需求,比如微信小程序在 onLaunch 中进行登录后取得 openid 并获得 token,项目各页面需要带上该 token 请求其他接口。

在onLaunch 中的请求是异步的,也就是说在执行 onLaunch 后页面 onLoad 就开始执行了,而不会等待 onLaunch 异步返回数据后再执行,这就导致了页面无法拿到 onLaunch 中异步获取的数据。

使用custom-hooks-plus库可以完美的帮我解决这个问题。

可以定义一个全局变量

// global.ts 文件  
import { proxyData } from 'custom-hooks-plus'  

interface GlobalData {  
  token: string  
  userInfo: number  
}  

export const globalData = proxyData({  
  token: '',  
})  

export function set<K extends keyof GlobalData>(key: K, val: GlobalData[K]) {  
  globalData[key] = val  
}  

export function get<K extends keyof GlobalData>(key: K): GlobalData[K] {  
  return globalData[key]  
}
// App.vue  
import { init } from 'custom-hooks-plus';  

init(  
  {  
    Token: {  
      key: 'token', // 监听global文件中globalData的token的变化  
    }  
  }  
);  

onLaunch(() => {  
  uni.login((res) => {  
    // 进行登录操作,修改globalData中的token  
  })  
})
// 页面中使用  
import { onCustomLoad, onCustomShow } from 'custom-hooks-plus';  

onCustomLoad((options) => {  
  console.log('globalData的token都被修改了才会触发');  
}, ['Token']);

通过上面的代码实现了,页面中的onCustomLoad的回调会等token有值后再执行,完美的解决了onLaunch和onLoad的异步问题。

具体可看:https://github.com/DBAAZzz/custom-hooks-plus

继续阅读 »

在使用 uni-app 开发项目时,会遇到需要在 onLaunch 中请求接口返回结果,并且此结果在项目各个页面的 onLoad 中都有可能使用到的需求,比如微信小程序在 onLaunch 中进行登录后取得 openid 并获得 token,项目各页面需要带上该 token 请求其他接口。

在onLaunch 中的请求是异步的,也就是说在执行 onLaunch 后页面 onLoad 就开始执行了,而不会等待 onLaunch 异步返回数据后再执行,这就导致了页面无法拿到 onLaunch 中异步获取的数据。

使用custom-hooks-plus库可以完美的帮我解决这个问题。

可以定义一个全局变量

// global.ts 文件  
import { proxyData } from 'custom-hooks-plus'  

interface GlobalData {  
  token: string  
  userInfo: number  
}  

export const globalData = proxyData({  
  token: '',  
})  

export function set<K extends keyof GlobalData>(key: K, val: GlobalData[K]) {  
  globalData[key] = val  
}  

export function get<K extends keyof GlobalData>(key: K): GlobalData[K] {  
  return globalData[key]  
}
// App.vue  
import { init } from 'custom-hooks-plus';  

init(  
  {  
    Token: {  
      key: 'token', // 监听global文件中globalData的token的变化  
    }  
  }  
);  

onLaunch(() => {  
  uni.login((res) => {  
    // 进行登录操作,修改globalData中的token  
  })  
})
// 页面中使用  
import { onCustomLoad, onCustomShow } from 'custom-hooks-plus';  

onCustomLoad((options) => {  
  console.log('globalData的token都被修改了才会触发');  
}, ['Token']);

通过上面的代码实现了,页面中的onCustomLoad的回调会等token有值后再执行,完美的解决了onLaunch和onLoad的异步问题。

具体可看:https://github.com/DBAAZzz/custom-hooks-plus

收起阅读 »

原创uniapp+vue3+uni-ui聊天室|uni-app+vue3仿微信app模板

vue3 uniapp

原创研发uniapp+vue3+pinia2+uv-ui+vite4.x跨端仿微信app聊天Uniapp-Wechat。支持编译到H5+小程序端+App端。实现编辑框多行自适应高度消息+emoj混合、长按仿微信语音面板、图片/视频预览、红包/朋友圈等功能。

https://ask.dcloud.net.cn/article/40928

编译h5+小程序+APP端效果

img

uni-vue3-wechat项目使用HbuilderX4.0.8开发工具,采用vue3 setup语法规范编码。

img

使用技术

  • 开发工具:HbuilderX 4.0.8
  • 技术框架:Uniapp+Vue3+Pinia2+Vite4.x
  • UI组件库:uni-ui+uv-ui
  • 弹框组件:uv3-popup(uniapp+vue3多端自定义弹框组件)
  • 自定义组件:uv3-navbar+uv3-tabbar组件
  • 缓存服务:pinia-plugin-unistorage
  • 编译支持:H5+小程序+APP端

img

img

img

img

项目结构图

img

main.js配置

/**  
 * 入口文件 main.js  
*/  

import { createSSRApp } from 'vue'  
import App from './App'  

// 引入pinia状态管理  
import pinia from '@/pinia'  

export function createApp() {  
    const app = createSSRApp(App)  
    app.use(pinia)  
    return {  
        app,  
        pinia  
    }  
}

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

在web端则是以750px像素布局。

img

App.vue配置

<script setup>  
    import { provide } from 'vue'  
    import { onLaunch, onShow, onHide, onPageNotFound } from '@dcloudio/uni-app'  

    onLaunch(() => {  
        console.log('App Launch')  

        uni.hideTabBar()  
        loadSystemInfo()  
    })  

    onShow(() => {  
        console.log('App Show')  
    })  

    onHide(() => {  
        console.log('App Hide')  
    })  

    onPageNotFound((e) => {  
        console.warn('Route Error:', `${e.path}`)  
    })  

    // 获取系统设备信息  
    const loadSystemInfo = () => {  
        uni.getSystemInfo({  
            success: (e) => {  
                // 获取手机状态栏高度  
                let statusBar = e.statusBarHeight  
                let customBar  

                // #ifndef MP  
                customBar = statusBar + (e.platform == 'android' ? 50 : 45)  
                // #endif  

                // #ifdef MP-WEIXIN  
                // 获取按钮的布局位置信息  
                let menu = wx.getMenuButtonBoundingClientRect()  
                // 导航栏高度 = 下距离 + 上距离 - 状态栏高度  
                customBar = menu.bottom + menu.top - statusBar  
                // #endif  

                // #ifdef MP-ALIPAY  
                customBar = statusBar + e.titleBarHeight  
                // #endif  

                // 由于globalData在vue3 setup存在兼容性问题,改为provide/inject替代方案  
                provide('globalData', {  
                    statusBarH: statusBar,  
                    customBarH: customBar,  
                    screenWidth: e.screenWidth,  
                    screenHeight: e.screenHeight,  
                    platform: e.platform  
                })  
            }  
        })  
    }  
</script>  

<style>  
    /* #ifndef APP-NVUE */  
    @import 'static/fonts/iconfont.css';  
    /* #endif */  
</style>  
<style lang="scss">  
    @import 'styles/reset.scss';  
    @import 'styles/layout.scss';  
</style>

公共布局模板

项目整体分为顶部区域+内容区+底部区域三个大的模块。

img

img

<!-- 公共布局模板 -->  

<!-- #ifdef MP-WEIXIN -->  
<script>  
    export default {  
        /**  
         * 解决小程序class、id问题  
         * manifest.json中配置mergeVirtualHostAttributes: true, 在微信小程序平台不生效,组件外部传入的class没有挂到组件根节点上,在组件中增加options: { virtualHost: true }  
         * https://github.com/dcloudio/uni-ui/issues/753  
         */  
        options: { virtualHost: true }  
    }  
</script>  
<!-- #endif -->  

<script setup>  
    const props = defineProps({  
        // 是否显示自定义tabbar  
        showTabBar: { type: [Boolean, String], default: false },  
    })  
</script>  

<template>  
    <view class="uv3__container flexbox flex-col flex1">  
        <!-- 顶部插槽 -->  
        <slot name="header" />  

        <!-- 内容区 -->  
        <view class="uv3__scrollview flex1">  
            <slot />  
        </view>  

        <!-- 底部插槽 -->  
        <slot name="footer" />  

        <!-- tabbar栏 -->  
        <uv3-tabbar v-if="showTabBar" hideTabBar fixed />  
    </view>  
</template>

uniapp+vue3实现微信九宫格图像

img

<script setup>  
    import { onMounted, ref, computed, watch, getCurrentInstance } from 'vue'  

    const props = defineProps({  
        // 图像组  
        avatar: { type: Array, default: null },  
    })  

    const instance = getCurrentInstance()  

    const uuid = computed(() => Math.floor(Math.random() * 10000))  
    const avatarPainterId = ref('canvasid' + uuid.value)  

    const createAvatar = () => {  
        const ctx = uni.createCanvasContext(avatarPainterId.value, instance)  
        // 计算图像在画布上的坐标  
        const avatarSize = 12  
        const gap = 2  
        for(let i = 0, len = props.avatar.length; i < len; i++) {  
            const row = Math.floor(i / 3)  
            const col = i % 3  
            const x = col * (avatarSize + gap)  
            const y = row * (avatarSize + gap)  

            ctx.drawImage(props.avatar[i], x, y, avatarSize, avatarSize)  
        }  
        ctx.draw(false, () => {  
            // 输出临时图片  
            /* uni.canvasToTempFilePath({  
                canvasId: avatarPainterId.value,  
                success: (res) => {  
                    console.log(res.tempFilePath)  
                }  
            }) */  
        })  
    }  

    onMounted(() => {  
        createAvatar()  
    })  

    watch(() => props.avatar, () => {  
        createAvatar()  
    })  
</script>  

<template>  
    <template v-if="avatar.length > 1">  
        <view class="uv3__avatarPainter">  
            <canvas :canvas-id="avatarPainterId" class="uv3__avatarPainter-canvas"></canvas>  
        </view>  
    </template>  
    <template v-else>  
        <image class="uv3__avatarOne" :src="avatar[0]" />  
    </template>  
</template>  

<style lang="scss" scoped>  
    .uv3__avatarPainter {background-color: #eee; border-radius: 5px; overflow: hidden; padding: 2px; height: 44px; width: 44px;}  
    .uv3__avatarPainter-canvas {height: 100%; width: 100%;}  
    .uv3__avatarOne {border-radius: 5px; height: 44px; width: 44px;}  
</style>

img

img

img

项目中顶部导航条、底部菜单栏、各种弹窗功能均是使用自定义组件实现功能。

uniapp+vue3聊天功能

img

img

img

<view v-if="voicePanelEnable" class="uv3__voicepanel-popup">  
    <view class="uv3__voicepanel-body flexbox flex-col">  
        <!-- 取消发送+语音转文字 -->  
        <view v-if="!voiceToTransfer" class="uv3__voicepanel-transfer">  
            <!-- 提示动效 -->  
            <view class="animtips flexbox" :class="voiceType == 2 ? 'left' : voiceType == 3 ? 'right' : null"><Waves :lines="[2, 3].includes(voiceType) ? 10 : 20" /></view>  
            <!-- 操作项 -->  
            <view class="icobtns flexbox">  
                <view class="vbtn cancel flexbox flex-col" :class="{'hover': voiceType == 2}" @click="handleVoiceCancel"><text class="vicon uv3-icon uv3-icon-close"></text></view>  
                <view class="vbtn word flexbox flex-col" :class="{'hover': voiceType == 3}"><text class="vicon uv3-icon uv3-icon-word"></text></view>  
            </view>  
        </view>  

        <!-- 识别结果状态 -->  
        <view v-if="voiceToTransfer" class="uv3__voicepanel-transfer result fail">  
            <!-- 提示动效 -->  
            <view class="animtips flexbox"><uni-icons type="info-filled" color="#fff" size="20"></uni-icons><text class="c-fff">未识别到文字</text></view>  
            <view class="icobtns flexbox">  
                <view class="vbtn cancel flexbox flex-col" @click="handleVoiceCancel"><text class="vicon uv3-icon uv3-icon-chexiao"></text>取消</view>  
                <view class="vbtn word flexbox flex-col"><text class="vicon uv3-icon uv3-icon-audio"></text>发送原语音</view>  
                <view class="vbtn check flexbox flex-col"><text class="vicon uv3-icon uv3-icon-duigou"></text></view>  
            </view>  
        </view>  

        <!-- 背景语音图 -->  
        <view class="uv3__voicepanel-cover">  
            <image v-if="!voiceToTransfer" src="/static/voice_bg.webp" :webp="true" mode="widthFix" style="width: 100%;" />  
        </view>  
        <!-- 提示文字 -->  
        <view v-if="!voiceToTransfer" class="uv3__voicepanel-tooltip">{{voiceTypeMap[voiceType]}}</view>  
        <!-- 背景图标 -->  
        <view v-if="!voiceToTransfer" class="uv3__voicepanel-fixico"><text class="uv3-icon uv3-icon-audio fs-50"></text></view>  
    </view>  
</view>

Okay,综上就是uni-app+vue3实战开发微信app聊天项目的一些知识分享。

作者:xiaoyan2015
链接: https://juejin.cn/post/7363121890791899170
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

原创研发uniapp+vue3+pinia2+uv-ui+vite4.x跨端仿微信app聊天Uniapp-Wechat。支持编译到H5+小程序端+App端。实现编辑框多行自适应高度消息+emoj混合、长按仿微信语音面板、图片/视频预览、红包/朋友圈等功能。

https://ask.dcloud.net.cn/article/40928

编译h5+小程序+APP端效果

img

uni-vue3-wechat项目使用HbuilderX4.0.8开发工具,采用vue3 setup语法规范编码。

img

使用技术

  • 开发工具:HbuilderX 4.0.8
  • 技术框架:Uniapp+Vue3+Pinia2+Vite4.x
  • UI组件库:uni-ui+uv-ui
  • 弹框组件:uv3-popup(uniapp+vue3多端自定义弹框组件)
  • 自定义组件:uv3-navbar+uv3-tabbar组件
  • 缓存服务:pinia-plugin-unistorage
  • 编译支持:H5+小程序+APP端

img

img

img

img

项目结构图

img

main.js配置

/**  
 * 入口文件 main.js  
*/  

import { createSSRApp } from 'vue'  
import App from './App'  

// 引入pinia状态管理  
import pinia from '@/pinia'  

export function createApp() {  
    const app = createSSRApp(App)  
    app.use(pinia)  
    return {  
        app,  
        pinia  
    }  
}

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

在web端则是以750px像素布局。

img

App.vue配置

<script setup>  
    import { provide } from 'vue'  
    import { onLaunch, onShow, onHide, onPageNotFound } from '@dcloudio/uni-app'  

    onLaunch(() => {  
        console.log('App Launch')  

        uni.hideTabBar()  
        loadSystemInfo()  
    })  

    onShow(() => {  
        console.log('App Show')  
    })  

    onHide(() => {  
        console.log('App Hide')  
    })  

    onPageNotFound((e) => {  
        console.warn('Route Error:', `${e.path}`)  
    })  

    // 获取系统设备信息  
    const loadSystemInfo = () => {  
        uni.getSystemInfo({  
            success: (e) => {  
                // 获取手机状态栏高度  
                let statusBar = e.statusBarHeight  
                let customBar  

                // #ifndef MP  
                customBar = statusBar + (e.platform == 'android' ? 50 : 45)  
                // #endif  

                // #ifdef MP-WEIXIN  
                // 获取按钮的布局位置信息  
                let menu = wx.getMenuButtonBoundingClientRect()  
                // 导航栏高度 = 下距离 + 上距离 - 状态栏高度  
                customBar = menu.bottom + menu.top - statusBar  
                // #endif  

                // #ifdef MP-ALIPAY  
                customBar = statusBar + e.titleBarHeight  
                // #endif  

                // 由于globalData在vue3 setup存在兼容性问题,改为provide/inject替代方案  
                provide('globalData', {  
                    statusBarH: statusBar,  
                    customBarH: customBar,  
                    screenWidth: e.screenWidth,  
                    screenHeight: e.screenHeight,  
                    platform: e.platform  
                })  
            }  
        })  
    }  
</script>  

<style>  
    /* #ifndef APP-NVUE */  
    @import 'static/fonts/iconfont.css';  
    /* #endif */  
</style>  
<style lang="scss">  
    @import 'styles/reset.scss';  
    @import 'styles/layout.scss';  
</style>

公共布局模板

项目整体分为顶部区域+内容区+底部区域三个大的模块。

img

img

<!-- 公共布局模板 -->  

<!-- #ifdef MP-WEIXIN -->  
<script>  
    export default {  
        /**  
         * 解决小程序class、id问题  
         * manifest.json中配置mergeVirtualHostAttributes: true, 在微信小程序平台不生效,组件外部传入的class没有挂到组件根节点上,在组件中增加options: { virtualHost: true }  
         * https://github.com/dcloudio/uni-ui/issues/753  
         */  
        options: { virtualHost: true }  
    }  
</script>  
<!-- #endif -->  

<script setup>  
    const props = defineProps({  
        // 是否显示自定义tabbar  
        showTabBar: { type: [Boolean, String], default: false },  
    })  
</script>  

<template>  
    <view class="uv3__container flexbox flex-col flex1">  
        <!-- 顶部插槽 -->  
        <slot name="header" />  

        <!-- 内容区 -->  
        <view class="uv3__scrollview flex1">  
            <slot />  
        </view>  

        <!-- 底部插槽 -->  
        <slot name="footer" />  

        <!-- tabbar栏 -->  
        <uv3-tabbar v-if="showTabBar" hideTabBar fixed />  
    </view>  
</template>

uniapp+vue3实现微信九宫格图像

img

<script setup>  
    import { onMounted, ref, computed, watch, getCurrentInstance } from 'vue'  

    const props = defineProps({  
        // 图像组  
        avatar: { type: Array, default: null },  
    })  

    const instance = getCurrentInstance()  

    const uuid = computed(() => Math.floor(Math.random() * 10000))  
    const avatarPainterId = ref('canvasid' + uuid.value)  

    const createAvatar = () => {  
        const ctx = uni.createCanvasContext(avatarPainterId.value, instance)  
        // 计算图像在画布上的坐标  
        const avatarSize = 12  
        const gap = 2  
        for(let i = 0, len = props.avatar.length; i < len; i++) {  
            const row = Math.floor(i / 3)  
            const col = i % 3  
            const x = col * (avatarSize + gap)  
            const y = row * (avatarSize + gap)  

            ctx.drawImage(props.avatar[i], x, y, avatarSize, avatarSize)  
        }  
        ctx.draw(false, () => {  
            // 输出临时图片  
            /* uni.canvasToTempFilePath({  
                canvasId: avatarPainterId.value,  
                success: (res) => {  
                    console.log(res.tempFilePath)  
                }  
            }) */  
        })  
    }  

    onMounted(() => {  
        createAvatar()  
    })  

    watch(() => props.avatar, () => {  
        createAvatar()  
    })  
</script>  

<template>  
    <template v-if="avatar.length > 1">  
        <view class="uv3__avatarPainter">  
            <canvas :canvas-id="avatarPainterId" class="uv3__avatarPainter-canvas"></canvas>  
        </view>  
    </template>  
    <template v-else>  
        <image class="uv3__avatarOne" :src="avatar[0]" />  
    </template>  
</template>  

<style lang="scss" scoped>  
    .uv3__avatarPainter {background-color: #eee; border-radius: 5px; overflow: hidden; padding: 2px; height: 44px; width: 44px;}  
    .uv3__avatarPainter-canvas {height: 100%; width: 100%;}  
    .uv3__avatarOne {border-radius: 5px; height: 44px; width: 44px;}  
</style>

img

img

img

项目中顶部导航条、底部菜单栏、各种弹窗功能均是使用自定义组件实现功能。

uniapp+vue3聊天功能

img

img

img

<view v-if="voicePanelEnable" class="uv3__voicepanel-popup">  
    <view class="uv3__voicepanel-body flexbox flex-col">  
        <!-- 取消发送+语音转文字 -->  
        <view v-if="!voiceToTransfer" class="uv3__voicepanel-transfer">  
            <!-- 提示动效 -->  
            <view class="animtips flexbox" :class="voiceType == 2 ? 'left' : voiceType == 3 ? 'right' : null"><Waves :lines="[2, 3].includes(voiceType) ? 10 : 20" /></view>  
            <!-- 操作项 -->  
            <view class="icobtns flexbox">  
                <view class="vbtn cancel flexbox flex-col" :class="{'hover': voiceType == 2}" @click="handleVoiceCancel"><text class="vicon uv3-icon uv3-icon-close"></text></view>  
                <view class="vbtn word flexbox flex-col" :class="{'hover': voiceType == 3}"><text class="vicon uv3-icon uv3-icon-word"></text></view>  
            </view>  
        </view>  

        <!-- 识别结果状态 -->  
        <view v-if="voiceToTransfer" class="uv3__voicepanel-transfer result fail">  
            <!-- 提示动效 -->  
            <view class="animtips flexbox"><uni-icons type="info-filled" color="#fff" size="20"></uni-icons><text class="c-fff">未识别到文字</text></view>  
            <view class="icobtns flexbox">  
                <view class="vbtn cancel flexbox flex-col" @click="handleVoiceCancel"><text class="vicon uv3-icon uv3-icon-chexiao"></text>取消</view>  
                <view class="vbtn word flexbox flex-col"><text class="vicon uv3-icon uv3-icon-audio"></text>发送原语音</view>  
                <view class="vbtn check flexbox flex-col"><text class="vicon uv3-icon uv3-icon-duigou"></text></view>  
            </view>  
        </view>  

        <!-- 背景语音图 -->  
        <view class="uv3__voicepanel-cover">  
            <image v-if="!voiceToTransfer" src="/static/voice_bg.webp" :webp="true" mode="widthFix" style="width: 100%;" />  
        </view>  
        <!-- 提示文字 -->  
        <view v-if="!voiceToTransfer" class="uv3__voicepanel-tooltip">{{voiceTypeMap[voiceType]}}</view>  
        <!-- 背景图标 -->  
        <view v-if="!voiceToTransfer" class="uv3__voicepanel-fixico"><text class="uv3-icon uv3-icon-audio fs-50"></text></view>  
    </view>  
</view>

Okay,综上就是uni-app+vue3实战开发微信app聊天项目的一些知识分享。

作者:xiaoyan2015
链接: https://juejin.cn/post/7363121890791899170
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

dcloud有销售联系方式吗?

dcloud有销售联系方式吗?

dcloud有销售联系方式吗?