HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

【记录】vue2--->vue3组合式

vue3

1、this.$nextTick,替换方案如下:

import { nextTick} from "vue";  
nextTick(function() {})

2、uni.createSelectorQuery().in(this),其他相似api需要this应该也是一样的,替换方案如下:

uniapp文档示例(选择组合式api):https://uniapp.dcloud.net.cn/api/ui/nodes-info.html#selectorquery-in

import { getCurrentInstance } from "vue";  
const instance = getCurrentInstance();  
const that = instance.proxy;  
const query = uni.createSelectorQuery().in(that);

评论区”朱小“的发问,加.in(that)和不加的区别。以下是查找到的内容,仅供参考:

3、子组件使用this.$parent,替换方案:props、provide/inject、emits或事件总线

4、子组件使用this.$root.$on("hook:onShow", () => {...相关逻辑})监听父组件onShow生命周期,替换方案如下:

import { onShow } from "@dcloudio/uni-app";  
onShow(() => {  
    ...相关逻辑  
})
继续阅读 »

1、this.$nextTick,替换方案如下:

import { nextTick} from "vue";  
nextTick(function() {})

2、uni.createSelectorQuery().in(this),其他相似api需要this应该也是一样的,替换方案如下:

uniapp文档示例(选择组合式api):https://uniapp.dcloud.net.cn/api/ui/nodes-info.html#selectorquery-in

import { getCurrentInstance } from "vue";  
const instance = getCurrentInstance();  
const that = instance.proxy;  
const query = uni.createSelectorQuery().in(that);

评论区”朱小“的发问,加.in(that)和不加的区别。以下是查找到的内容,仅供参考:

3、子组件使用this.$parent,替换方案:props、provide/inject、emits或事件总线

4、子组件使用this.$root.$on("hook:onShow", () => {...相关逻辑})监听父组件onShow生命周期,替换方案如下:

import { onShow } from "@dcloudio/uni-app";  
onShow(() => {  
    ...相关逻辑  
})
收起阅读 »

小程序版本

<template>
<view class="tagBall">
<view class="tag" v-for="(item, index) in texts" :key="index" :style="[getTagStyle(index)]">
<view>{{item.userName}}</view>
<view class="dot"></view>
</view>
</view>
</template>
<script>
export default {
props: {
speed: {
type: Number,
default: 0.5
},
texts: {
type: Array,
default: () => {
return [];
}
}
},
data() {
return {
tagEle: [],
RADIUS: 150,
fallLength: 240,
angleX: Math.PI / 400,
angleY: Math.PI / 400,
tags: [],
liviews: [],
CX: 0,
CY: 0,
timer: null,
clickX: 0,
clickY: 0
};
},
computed: {
getTagStyle() {
return (index) => {
const style = this.liviews[index] || {};
return {
fontSize: ${style.fontSize},
opacity: style.opacity,
transition: style.transition,
filter: style.filter,
zIndex: style.zIndex,
transformStyle: style.transformStyle,
left: ${style.left},
top: ${style.top},
perspective: '20000rpx',
transition: 'all linear 0.1s',
transform: style.transform,
};
};
}
},
destroyed() {
this.clearTimer();
},
mounted() {
this.initTags();
},
methods: {
clearTimer() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
getTags(option) {
this.tagEle = option.tagEle;
this.CX = option.CX;
this.CY = option.CY;
this.init();
},
rotateX() {
const cos = Math.cos(this.angleX this.speed);
const sin = Math.sin(this.angleX
this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const y1 = t.y cos + t.z sin;
const z1 = t.z cos - t.y sin;
t.y = y1;
t.z = z1;
}
},
rotateY() {
const cos = Math.cos(this.angleY this.speed);
const sin = Math.sin(this.angleY
this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const x1 = t.x cos - t.z sin;
const z1 = t.z cos + t.x sin;
t.x = x1;
t.z = z1;
}
},
init() {
this.tags = [];
this.liviews = [];
for (let i = 0; i < this.tagEle.length; i++) {
const k = (2 (i + 1) - 1) / this.tagEle.length - 1;
const a = Math.acos(k);
const b = a
Math.sqrt(this.tagEle.length Math.PI);
const x = this.RADIUS
Math.sin(a) Math.cos(b);
const y = this.RADIUS
Math.sin(a) Math.sin(b);
const z = this.RADIUS
Math.cos(a);
const t = this.tag(this.tagEle[i], x / 2, y / 2, z / 2);
this.tags.push(t);
}
this.animate(this.tags);
},
tag(ele, x, y, z) {
return {
ele,
x,
y,
z
};
},
move(t, i) {
const scale = this.fallLength / (this.fallLength - t.z 1.3);
const alpha = (t.z + this.RADIUS) / (2
this.RADIUS);
this.liviews.push({
fontSize: 5 scale + 'px',
opacity: alpha + 0.5,
transition: 'all linear 0.5s',
filter: 'alpha(opacity=' + (alpha + 0.5)
100 + ')',
zIndex: parseInt(scale 100),
transformStyle: 'preserve-3d',
left: t.x + this.CX - t.ele.offsetWidth / 2 + 'px',
top: t.y + this.CY - t.ele.offsetHeight / 2 + 'px',
perspective: '2000rpx',
transformStyle: 'preserve-3d',
transform: scale(${alpha+0.5}),
});
},
animate(x) {
this.clearTimer();
this.timer = setInterval(() => {
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < x.length; i++) {
this.move(x[i], i);
}
}, 25)
},
touchstartscene(e) {
this.clickX = e.touches[0].clientX;
this.clickY = e.touches[0].clientY;
this.clearTimer();
},
touchendscene() {
this.animate(this.tags);
this.clearTimer();
},
touchmovescene(e) {
const fx = this.getDirection(this.clickX, this.clickY, e.touches[0].clientX, e.touches[0].clientY);
let x = this.clickX - e.touches[0].clientX - this.CX;
let y = this.clickY - e.touches[0].clientY - this.CY;
if (fx === 1) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY - this.clickY - this.CY;
} else if (fx === 2) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY + this.CY;
} else if (fx === 3) {
x = this.clickX + e.touches[0].clientX + this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
} else {
x = this.clickX - e.touches[0].clientX - this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
}
this.angleY = x
0.0001;
this.angleX = y 0.0001;
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < this.tags.length; i++) {
this.move(this.tags[i], i);
}
},
getDirection(startx, starty, endx, endy) {
const angx = endx - startx;
const angy = endy - starty;
let result = 0;
if (Math.abs(angx) < 2 && Math.abs(angy) < 2) {
return result;
}
const angle = Math.atan2(angy, angx)
180 / Math.PI;
if (angle >= -135 && angle <= -45) {
result = 1;
} else if (angle > 45 && angle < 135) {
result = 2;
} else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 3;
} else if (angle >= -45 && angle <= 45) {
result = 4;
}
return result;
},
initTags() {
let tagEles = [];
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
query.selectAll('.tag').boundingClientRect(rects => {
tagEles = rects.map(rect => ({
offsetWidth: rect.width,
offsetHeight: rect.height
}));
}).exec();
query.select('.tagBall').boundingClientRect(rect => {
const CX = rect.width / 2;
const CY = rect.height / 2;
this.getTags({
tagEle: tagEles,
CX,
CY
});
}).exec();
});
}
}
};
</script>
<style scoped>
.tagBall {
height: 214rpx;
position: relative;
perspective: 2000rpx;
transform-style: preserve-3d;
left: 0;
top: 0;
transition: all linear 0.1s;
margin-left: 40rpx;
}

.tag {  
    width: 150rpx;  
    position: absolute;  
    font-weight: 500;  
    font-size: 12rpx;  
    color: #000000;  
    display: flex;  
    flex-direction: column;  
    align-items: center;  
    transition: all ease-in-out 0.3s;  
}  

.dot {  
    width: 16rpx;  
    height: 16rpx;  
    background: #FCC928;  
    border-radius: 50%;  
    margin-top: 10rpx;  
}  

</style>

继续阅读 »

<template>
<view class="tagBall">
<view class="tag" v-for="(item, index) in texts" :key="index" :style="[getTagStyle(index)]">
<view>{{item.userName}}</view>
<view class="dot"></view>
</view>
</view>
</template>
<script>
export default {
props: {
speed: {
type: Number,
default: 0.5
},
texts: {
type: Array,
default: () => {
return [];
}
}
},
data() {
return {
tagEle: [],
RADIUS: 150,
fallLength: 240,
angleX: Math.PI / 400,
angleY: Math.PI / 400,
tags: [],
liviews: [],
CX: 0,
CY: 0,
timer: null,
clickX: 0,
clickY: 0
};
},
computed: {
getTagStyle() {
return (index) => {
const style = this.liviews[index] || {};
return {
fontSize: ${style.fontSize},
opacity: style.opacity,
transition: style.transition,
filter: style.filter,
zIndex: style.zIndex,
transformStyle: style.transformStyle,
left: ${style.left},
top: ${style.top},
perspective: '20000rpx',
transition: 'all linear 0.1s',
transform: style.transform,
};
};
}
},
destroyed() {
this.clearTimer();
},
mounted() {
this.initTags();
},
methods: {
clearTimer() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
getTags(option) {
this.tagEle = option.tagEle;
this.CX = option.CX;
this.CY = option.CY;
this.init();
},
rotateX() {
const cos = Math.cos(this.angleX this.speed);
const sin = Math.sin(this.angleX
this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const y1 = t.y cos + t.z sin;
const z1 = t.z cos - t.y sin;
t.y = y1;
t.z = z1;
}
},
rotateY() {
const cos = Math.cos(this.angleY this.speed);
const sin = Math.sin(this.angleY
this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const x1 = t.x cos - t.z sin;
const z1 = t.z cos + t.x sin;
t.x = x1;
t.z = z1;
}
},
init() {
this.tags = [];
this.liviews = [];
for (let i = 0; i < this.tagEle.length; i++) {
const k = (2 (i + 1) - 1) / this.tagEle.length - 1;
const a = Math.acos(k);
const b = a
Math.sqrt(this.tagEle.length Math.PI);
const x = this.RADIUS
Math.sin(a) Math.cos(b);
const y = this.RADIUS
Math.sin(a) Math.sin(b);
const z = this.RADIUS
Math.cos(a);
const t = this.tag(this.tagEle[i], x / 2, y / 2, z / 2);
this.tags.push(t);
}
this.animate(this.tags);
},
tag(ele, x, y, z) {
return {
ele,
x,
y,
z
};
},
move(t, i) {
const scale = this.fallLength / (this.fallLength - t.z 1.3);
const alpha = (t.z + this.RADIUS) / (2
this.RADIUS);
this.liviews.push({
fontSize: 5 scale + 'px',
opacity: alpha + 0.5,
transition: 'all linear 0.5s',
filter: 'alpha(opacity=' + (alpha + 0.5)
100 + ')',
zIndex: parseInt(scale 100),
transformStyle: 'preserve-3d',
left: t.x + this.CX - t.ele.offsetWidth / 2 + 'px',
top: t.y + this.CY - t.ele.offsetHeight / 2 + 'px',
perspective: '2000rpx',
transformStyle: 'preserve-3d',
transform: scale(${alpha+0.5}),
});
},
animate(x) {
this.clearTimer();
this.timer = setInterval(() => {
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < x.length; i++) {
this.move(x[i], i);
}
}, 25)
},
touchstartscene(e) {
this.clickX = e.touches[0].clientX;
this.clickY = e.touches[0].clientY;
this.clearTimer();
},
touchendscene() {
this.animate(this.tags);
this.clearTimer();
},
touchmovescene(e) {
const fx = this.getDirection(this.clickX, this.clickY, e.touches[0].clientX, e.touches[0].clientY);
let x = this.clickX - e.touches[0].clientX - this.CX;
let y = this.clickY - e.touches[0].clientY - this.CY;
if (fx === 1) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY - this.clickY - this.CY;
} else if (fx === 2) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY + this.CY;
} else if (fx === 3) {
x = this.clickX + e.touches[0].clientX + this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
} else {
x = this.clickX - e.touches[0].clientX - this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
}
this.angleY = x
0.0001;
this.angleX = y 0.0001;
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < this.tags.length; i++) {
this.move(this.tags[i], i);
}
},
getDirection(startx, starty, endx, endy) {
const angx = endx - startx;
const angy = endy - starty;
let result = 0;
if (Math.abs(angx) < 2 && Math.abs(angy) < 2) {
return result;
}
const angle = Math.atan2(angy, angx)
180 / Math.PI;
if (angle >= -135 && angle <= -45) {
result = 1;
} else if (angle > 45 && angle < 135) {
result = 2;
} else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 3;
} else if (angle >= -45 && angle <= 45) {
result = 4;
}
return result;
},
initTags() {
let tagEles = [];
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
query.selectAll('.tag').boundingClientRect(rects => {
tagEles = rects.map(rect => ({
offsetWidth: rect.width,
offsetHeight: rect.height
}));
}).exec();
query.select('.tagBall').boundingClientRect(rect => {
const CX = rect.width / 2;
const CY = rect.height / 2;
this.getTags({
tagEle: tagEles,
CX,
CY
});
}).exec();
});
}
}
};
</script>
<style scoped>
.tagBall {
height: 214rpx;
position: relative;
perspective: 2000rpx;
transform-style: preserve-3d;
left: 0;
top: 0;
transition: all linear 0.1s;
margin-left: 40rpx;
}

.tag {  
    width: 150rpx;  
    position: absolute;  
    font-weight: 500;  
    font-size: 12rpx;  
    color: #000000;  
    display: flex;  
    flex-direction: column;  
    align-items: center;  
    transition: all ease-in-out 0.3s;  
}  

.dot {  
    width: 16rpx;  
    height: 16rpx;  
    background: #FCC928;  
    border-radius: 50%;  
    margin-top: 10rpx;  
}  

</style>

收起阅读 »

【TuiPlus】简洁高效 uniapp x UI组件库重磅发布,原生uts插件加持,保障流畅体验,助力快速开发,打造优质原生应用。

原生 图表 UI uts

项目介绍

> TuiPlus UI组件库 —— 全面兼容最新设计理念。本次重构不仅着眼于功能的提升,更深入探索用户体验的无限可能。我们的组件库致力于打造一个既全面又精致的UI设计平台,强调设计的精致度和扩展性,始终将用户体验放在首位。在细节的打磨上,我们精益求精,自豪地呈现——TuiPlus UI组件库。

> 创新与差异 —— 新版增加了UTS版图表,裁剪等众多组件,我们的专注点在于提供丰富且独特的组件。每一款组件都是经过匠心独运的设计和优化,确保性能卓越。特别是我们的UTS图表组件,采用高性能的canvas技术,实现了无需webview嵌套的纯canvas绘制,带来了行业领先的流畅度和响应速度。TuiPlus UI组件库全面支持iOS、Android以及Web/H5三端,无缝适配多种屏幕尺寸和形态,包括长屏、宽屏、PC、折叠屏和横屏,满足不同场景下的多样化需求。

!视频

TuiPlus文档地址:https://life.yundie.xyz/t-uvue-ui/docs/index.html

功能特性快速了解

TuiPlus扫码下载体验

web在线体验

Image

安卓下载链接

Image

继续阅读 »

项目介绍

> TuiPlus UI组件库 —— 全面兼容最新设计理念。本次重构不仅着眼于功能的提升,更深入探索用户体验的无限可能。我们的组件库致力于打造一个既全面又精致的UI设计平台,强调设计的精致度和扩展性,始终将用户体验放在首位。在细节的打磨上,我们精益求精,自豪地呈现——TuiPlus UI组件库。

> 创新与差异 —— 新版增加了UTS版图表,裁剪等众多组件,我们的专注点在于提供丰富且独特的组件。每一款组件都是经过匠心独运的设计和优化,确保性能卓越。特别是我们的UTS图表组件,采用高性能的canvas技术,实现了无需webview嵌套的纯canvas绘制,带来了行业领先的流畅度和响应速度。TuiPlus UI组件库全面支持iOS、Android以及Web/H5三端,无缝适配多种屏幕尺寸和形态,包括长屏、宽屏、PC、折叠屏和横屏,满足不同场景下的多样化需求。

!视频

TuiPlus文档地址:https://life.yundie.xyz/t-uvue-ui/docs/index.html

功能特性快速了解

TuiPlus扫码下载体验

web在线体验

Image

安卓下载链接

Image

收起阅读 »

华为外包前端招聘

外包

岗位职责:
1、负责WEB,PC,移动端(iOS、Android)等前端js框架设计及开发、发布和维护;
2、根据产品需求,分析并做出Web 3D的页面前端结构解决方案以及与后台交互整体架构解决方案;
3、与设计、后台开发人员保持良好沟通,能快速理解、消化各方需求,并落实为具体的开发工作;
4、优化代码,应用前沿技术,不断提高页面性能和用户体验。
任职要求:
1.二本及以上学历;
2、会使用WebGL及其框架three.js、vue.js 等前端技术开发3D页面demo;
3、一年以上前端开发工作经验,代码编写规范,编程基础扎实,熟练掌握openGL、osg、ogre开发且有WebGL经验亦可;
4、精通Web前端技术,包括HTML5/CSS/Javascript等,对性能优化、解决多浏览器兼容性问题有经验;对移动设备前端开发有经验;
5、熟悉w3c标准,对表现与数据分离、web语义化等有深刻理解;
6、自动构建工具(gulp或者grunt)的基本运用;
7、熟悉Angular4以上和Node.js,理解前端MVC逻辑,对路由、模块化和依赖注入有一定的掌握;
8、熟悉业界常见的开源组件,有开源项目开发经验者优先。
待遇环境
薪资:11k-28k。实际多少依据个人能力。
弹性工作制,上下班打卡。
园区健身房、食堂等,晚饭补贴,打车补贴等。

网申通道

继续阅读 »

岗位职责:
1、负责WEB,PC,移动端(iOS、Android)等前端js框架设计及开发、发布和维护;
2、根据产品需求,分析并做出Web 3D的页面前端结构解决方案以及与后台交互整体架构解决方案;
3、与设计、后台开发人员保持良好沟通,能快速理解、消化各方需求,并落实为具体的开发工作;
4、优化代码,应用前沿技术,不断提高页面性能和用户体验。
任职要求:
1.二本及以上学历;
2、会使用WebGL及其框架three.js、vue.js 等前端技术开发3D页面demo;
3、一年以上前端开发工作经验,代码编写规范,编程基础扎实,熟练掌握openGL、osg、ogre开发且有WebGL经验亦可;
4、精通Web前端技术,包括HTML5/CSS/Javascript等,对性能优化、解决多浏览器兼容性问题有经验;对移动设备前端开发有经验;
5、熟悉w3c标准,对表现与数据分离、web语义化等有深刻理解;
6、自动构建工具(gulp或者grunt)的基本运用;
7、熟悉Angular4以上和Node.js,理解前端MVC逻辑,对路由、模块化和依赖注入有一定的掌握;
8、熟悉业界常见的开源组件,有开源项目开发经验者优先。
待遇环境
薪资:11k-28k。实际多少依据个人能力。
弹性工作制,上下班打卡。
园区健身房、食堂等,晚饭补贴,打车补贴等。

网申通道

收起阅读 »

【一个好用的css】pointer-events;当前元素可不可以被点击,实现穿透点击,穿透层级高的视图,点击层级低的视图

点击事件 css样式 css

官方文档:https://developer.mozilla.org/zh-CN/docs/Web/CSS/pointer-events

pointer-events: auto; 与pointer-events属性未指定时的表现效果相同,对于 SVG 内容,该值与visiblePainted效果相同
pointer-events: none; 元素永远不会成为鼠标事件的target。但是,当其后代元素的pointer-events属性指定其他值时,鼠标事件可以指向后代元素,在这种情况下,鼠标事件将在捕获或冒泡阶段触发父元素的事件侦听器。

下面这个例子是不会触发onTap事件的

<template>  
    <view>  
        <view class="a"></view>  
        <view class="b" @tap="onTap"></view>  
    </view>  
</template>  

<script>  
export default {  
    methods: {  
        onTap() {  
            console.log("点击了b");  
        }  
    }  
}  
</script>  

<style lang="scss" scoped>  
    .a {  
        position: fixed;  
        top: 0;  
        left: 0;  
        right: 0;  
        height: 100vh;  
        z-index: 10;  
    }  
    .b {  
        width: 100px;  
        height: 100px;  
        background-color: aqua;  
    }  
</style>

下面这个例子会触发onTap事件的,在a的样式加上pointer-events: none;,b的样式加上pointer-events: auto;其他不变

<template>  
    <view>  
        <view class="a"></view>  
        <view class="b" @tap="onTap"></view>  
    </view>  
</template>  

<script>  
export default {  
    methods: {  
        onTap() {  
            console.log("点击了b");  
        }  
    }  
}  
</script>  

<style lang="scss" scoped>  
    .a {  
        position: fixed;  
        top: 0;  
        left: 0;  
        right: 0;  
        height: 100vh;  
        z-index: 10;  
        pointer-events: none;  
    }  
    .b {  
        width: 100px;  
        height: 100px;  
        background-color: aqua;  
        pointer-events: auto;  
    }  
</style>

↓↓↓ 各位大佬点点赞

继续阅读 »

官方文档:https://developer.mozilla.org/zh-CN/docs/Web/CSS/pointer-events

pointer-events: auto; 与pointer-events属性未指定时的表现效果相同,对于 SVG 内容,该值与visiblePainted效果相同
pointer-events: none; 元素永远不会成为鼠标事件的target。但是,当其后代元素的pointer-events属性指定其他值时,鼠标事件可以指向后代元素,在这种情况下,鼠标事件将在捕获或冒泡阶段触发父元素的事件侦听器。

下面这个例子是不会触发onTap事件的

<template>  
    <view>  
        <view class="a"></view>  
        <view class="b" @tap="onTap"></view>  
    </view>  
</template>  

<script>  
export default {  
    methods: {  
        onTap() {  
            console.log("点击了b");  
        }  
    }  
}  
</script>  

<style lang="scss" scoped>  
    .a {  
        position: fixed;  
        top: 0;  
        left: 0;  
        right: 0;  
        height: 100vh;  
        z-index: 10;  
    }  
    .b {  
        width: 100px;  
        height: 100px;  
        background-color: aqua;  
    }  
</style>

下面这个例子会触发onTap事件的,在a的样式加上pointer-events: none;,b的样式加上pointer-events: auto;其他不变

<template>  
    <view>  
        <view class="a"></view>  
        <view class="b" @tap="onTap"></view>  
    </view>  
</template>  

<script>  
export default {  
    methods: {  
        onTap() {  
            console.log("点击了b");  
        }  
    }  
}  
</script>  

<style lang="scss" scoped>  
    .a {  
        position: fixed;  
        top: 0;  
        left: 0;  
        right: 0;  
        height: 100vh;  
        z-index: 10;  
        pointer-events: none;  
    }  
    .b {  
        width: 100px;  
        height: 100px;  
        background-color: aqua;  
        pointer-events: auto;  
    }  
</style>

↓↓↓ 各位大佬点点赞

收起阅读 »

vue3 + uniapp 可以直接开发鸿蒙啦!

uniapp

7 月 20 号,uniapp 官网上线了 uniapp 开发鸿蒙应用的文档,标志着 Vue3 + uniapp 开发鸿蒙应用时代的开启。

鸿蒙开发的支持与限制

鸿蒙开发仅支持 Vue3,不支持 Vue2 和 plus,
支持 nvue,nvue 编译到鸿蒙后非原生渲染。

开发环境要求

DevEco-Studio 5.0.3.400 以上 (DevEco-Studio 较大,达10G*)
鸿蒙系统版本 API 12 以上,HBuilderX-alpha-4.22 以上
Windows 系统使用模拟器需开启特定功能,且家庭版需升级。

配置鸿蒙离线 SDK 及相关操作
包括下载、解压、在 DevEco-Studio 中打开、启动模拟器或连接真机、配置签名等步骤。
启动鸿蒙模拟器
分为三步,包括下载、解压、等待 Sync 结束等,还涉及开发者申请。

<​当前缺个机会,缺份工作,想靠大厂外包过渡下的兄弟姐妹们,可以一起来共事,前、后端/测试年前捞人,待遇给的还可以,感兴趣可以来>

安装完模拟器后,点击启动按钮启动模拟器

启动模拟器成功后,如果提示需要先签名,则进行配置签名

连接鸿蒙真机

注意:真机需要鸿蒙系统版本 API 12 以上

打开鸿蒙手机开发者模式,开启USB调试,通过USB线连接电脑,在此处选择你的手机名称,再启动项目即可,如果提示需要先签名,则进行配置签名

配置 HBuilderX 吊起 DevEco-Studio:在 HBuilderX 中进行相关设置,填写 DevEco-Studio 启动路径。
创建 uni-app 工程:在 BuilderX 新建空白项目,选 vue3,在 manifest.json 文件中配置鸿蒙离线 SDK 路径。

配置签名

注意:配置签名需要先启动模拟器或连接真机后才能配置

配置 HBuilderX settings.json
需在 HBuilderX 中进行特定的设置操作

打开HBuilderX,点击上方菜单 - 工具 - 设置,在出现的弹窗右侧窗体新增如下配置
注意:值填你自己的 DevEco-Studio 启动路径

json 代码解读复制代码"harmony.devTools.path" : "D:/Huawei/DevEco Studio"

使用 uts 调用鸿蒙原生 API, 第三方API
调用鸿蒙原生 API
uni-app在Android和iOS平台,支持uts插件和App原生语言插件。目前App原生语言插件已经停止维护。uts插件是主推的扩展方式。
鸿蒙系统有很多原生API,可以通过uts插件方式接入,被uni-app调用。

uts插件介绍
uts插件鸿蒙开发专题

调用第三方 API
新增于 HBuilderX 4.25,有特定的使用流程和限制。
鸿蒙的包用法和npm包差不多,在鸿蒙项目里面用ohpm安装三方库后,在 /uni_modules/uts插件名/utssdk/app-harmony/index.uts 内即可直接 import
注意:只能在满足uts插件 /uni_modules//utssdk/app-harmony/.uts 的文件下使用,无法直接在项目的pages中使用
具体使用流程:在项目的pages引入uts插件,uts插件内再引入鸿蒙第三方库调用
发布鸿蒙应用
鸿蒙官方文档提供了如何发布鸿蒙应用,详见 文档

注意事项

移植已有的 uni-app 项目源码时,如有其他 npm 依赖,请自行安装
现阶段条件编译仅 APP-HARMONY、APP 可以命中鸿蒙平台
每次HBuilderX改动源码后,DevEco-Studio 内需要点重新运行才能生效
如果模拟器白屏了,尝试重启软件 DevEco-Studio,再重启项目
如果模拟器无法连接了,尝试重启电脑
在HBuilderX里运行后,需要再去鸿蒙 DevEco Studio里运行
在HBuilderX里修改代码后,需要去鸿蒙 DevEco Studio里重新运行
如果有多个uni-app项目要编译到鸿蒙,那么鸿蒙离线sdk需要放置多份,每个uni-app的manifest中配置不同的离线sdk地址,否则会冲突,鸿蒙设备上目前没有基座概念

总结
这样我们就有了一个初始的鸿蒙项目,并且可以在鸿蒙模拟器上运行。关于更多 uniapp 开发鸿蒙的 API,大家可以直接参考 uniapp 官方文档

——转载自作者:码上解忧铺

继续阅读 »

7 月 20 号,uniapp 官网上线了 uniapp 开发鸿蒙应用的文档,标志着 Vue3 + uniapp 开发鸿蒙应用时代的开启。

鸿蒙开发的支持与限制

鸿蒙开发仅支持 Vue3,不支持 Vue2 和 plus,
支持 nvue,nvue 编译到鸿蒙后非原生渲染。

开发环境要求

DevEco-Studio 5.0.3.400 以上 (DevEco-Studio 较大,达10G*)
鸿蒙系统版本 API 12 以上,HBuilderX-alpha-4.22 以上
Windows 系统使用模拟器需开启特定功能,且家庭版需升级。

配置鸿蒙离线 SDK 及相关操作
包括下载、解压、在 DevEco-Studio 中打开、启动模拟器或连接真机、配置签名等步骤。
启动鸿蒙模拟器
分为三步,包括下载、解压、等待 Sync 结束等,还涉及开发者申请。

<​当前缺个机会,缺份工作,想靠大厂外包过渡下的兄弟姐妹们,可以一起来共事,前、后端/测试年前捞人,待遇给的还可以,感兴趣可以来>

安装完模拟器后,点击启动按钮启动模拟器

启动模拟器成功后,如果提示需要先签名,则进行配置签名

连接鸿蒙真机

注意:真机需要鸿蒙系统版本 API 12 以上

打开鸿蒙手机开发者模式,开启USB调试,通过USB线连接电脑,在此处选择你的手机名称,再启动项目即可,如果提示需要先签名,则进行配置签名

配置 HBuilderX 吊起 DevEco-Studio:在 HBuilderX 中进行相关设置,填写 DevEco-Studio 启动路径。
创建 uni-app 工程:在 BuilderX 新建空白项目,选 vue3,在 manifest.json 文件中配置鸿蒙离线 SDK 路径。

配置签名

注意:配置签名需要先启动模拟器或连接真机后才能配置

配置 HBuilderX settings.json
需在 HBuilderX 中进行特定的设置操作

打开HBuilderX,点击上方菜单 - 工具 - 设置,在出现的弹窗右侧窗体新增如下配置
注意:值填你自己的 DevEco-Studio 启动路径

json 代码解读复制代码"harmony.devTools.path" : "D:/Huawei/DevEco Studio"

使用 uts 调用鸿蒙原生 API, 第三方API
调用鸿蒙原生 API
uni-app在Android和iOS平台,支持uts插件和App原生语言插件。目前App原生语言插件已经停止维护。uts插件是主推的扩展方式。
鸿蒙系统有很多原生API,可以通过uts插件方式接入,被uni-app调用。

uts插件介绍
uts插件鸿蒙开发专题

调用第三方 API
新增于 HBuilderX 4.25,有特定的使用流程和限制。
鸿蒙的包用法和npm包差不多,在鸿蒙项目里面用ohpm安装三方库后,在 /uni_modules/uts插件名/utssdk/app-harmony/index.uts 内即可直接 import
注意:只能在满足uts插件 /uni_modules//utssdk/app-harmony/.uts 的文件下使用,无法直接在项目的pages中使用
具体使用流程:在项目的pages引入uts插件,uts插件内再引入鸿蒙第三方库调用
发布鸿蒙应用
鸿蒙官方文档提供了如何发布鸿蒙应用,详见 文档

注意事项

移植已有的 uni-app 项目源码时,如有其他 npm 依赖,请自行安装
现阶段条件编译仅 APP-HARMONY、APP 可以命中鸿蒙平台
每次HBuilderX改动源码后,DevEco-Studio 内需要点重新运行才能生效
如果模拟器白屏了,尝试重启软件 DevEco-Studio,再重启项目
如果模拟器无法连接了,尝试重启电脑
在HBuilderX里运行后,需要再去鸿蒙 DevEco Studio里运行
在HBuilderX里修改代码后,需要去鸿蒙 DevEco Studio里重新运行
如果有多个uni-app项目要编译到鸿蒙,那么鸿蒙离线sdk需要放置多份,每个uni-app的manifest中配置不同的离线sdk地址,否则会冲突,鸿蒙设备上目前没有基座概念

总结
这样我们就有了一个初始的鸿蒙项目,并且可以在鸿蒙模拟器上运行。关于更多 uniapp 开发鸿蒙的 API,大家可以直接参考 uniapp 官方文档

——转载自作者:码上解忧铺

收起阅读 »

app打包 Error code = -5001 Error message: Error: invalid uni ID in mainfest.json!

云端打包发布常见问题

UNI4013F8F

Error code = -5001 Error message: Error: invalid uni ID in mainfest.json!

UNI4013F8F

Error code = -5001 Error message: Error: invalid uni ID in mainfest.json!

scroll-into-view使用注意事项

scroll_view

1:不能写成死值
<scroll-view :scroll-into-view="scrollIntoView" scroll-with-animation class="index-container" scroll-y="true" @scroll="handleScroll">
2:且要变化,否则再次触发返回顶部的时候 会失效

        let scrollIntoView = ref('')  

    function handleScroll(event) {  

        scrollIntoView.value = ''  

    }  

    function backToTop() {  
        scrollIntoView.value = "top"  
    }
继续阅读 »

1:不能写成死值
<scroll-view :scroll-into-view="scrollIntoView" scroll-with-animation class="index-container" scroll-y="true" @scroll="handleScroll">
2:且要变化,否则再次触发返回顶部的时候 会失效

        let scrollIntoView = ref('')  

    function handleScroll(event) {  

        scrollIntoView.value = ''  

    }  

    function backToTop() {  
        scrollIntoView.value = "top"  
    }
收起阅读 »

springboot vue uniapp 仿小红书 1:1 还原 (含源码演示)

小红书小程序 分享源码 源码分享 源码

线上预览:

移动端 http://8.146.211.120:8081/

管理端 http://8.146.211.120:8088/

小红书凭借优秀的产品体验 和超高人气 目前成为笔记类产品佼佼者

此项目将详细介绍如何使用Vue.js和Spring Boot 集合uniapp

开发一个仿小红书的城应用,凭借uniapp 可以在h5 小程序 app等多端使用

技术栈

移动端 uniapp graceui

管理端 vue element

后台 springboot springsecurity mybatisPlus tio-websocket

主要功能

  • 笔记创建 编辑 发布

  • 点赞笔记、评论

  • 回复笔记、评论

  • 私信、客服聊天

  • 后台笔记编辑 下架

  • 后台博主冻结

  • 后台客服系统

  • 系统消息维护

思维导图

项目展示

项目启动

后台运行环境

  • jdk1.8

  • mysql5.7

  • redis

启动步骤

1.下载pom文件依赖

2.导入项目中的sql

3.修改yml参数(端口号 数据库名)

4.如果 mysql 报错 order by和group by 执行sql报错sql_mode=only_full_group_by问题解决


SELECT @@GLOBAL.sql_mode;  

SELECT @@SESSION.sql_mode;  

set @@GLOBAL.sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';  

set @@SESSION.sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';  

FLUSH PRIVILEGES;  

5.打包 mvn clean package -Dmaven.test.skip=true 打包后jar文件可以正常运行

管理端前台运行环境

  • node v14.21.3

  • elementui 2.15.14

  • vue 2.6.14

启动步骤

1.cd 根目录的web文件夹下

2.执行npm install下载依赖

3.执行npm run dev启动

4.修改.env.xx修改测试环境和正式环境端口

5.npm run build进行打包

6.如果出现lemon imui 依赖报错 解压根目录下dist.rar文件夹到lemon imui根目录下即可

移动端前台

hbudiler 3.99

vue 3

移动端前台使用了graceUi6.0 需授权后找我覆盖文件

包结构说名

后端

  • 目前后端功能基本能满足我们需求

  • 后续开发建立和system同级目录 进行功能开发即可

写到最后

代码地址 https://gitee.com/ddeatrr/springboot_vue_xhs

继续阅读 »

线上预览:

移动端 http://8.146.211.120:8081/

管理端 http://8.146.211.120:8088/

小红书凭借优秀的产品体验 和超高人气 目前成为笔记类产品佼佼者

此项目将详细介绍如何使用Vue.js和Spring Boot 集合uniapp

开发一个仿小红书的城应用,凭借uniapp 可以在h5 小程序 app等多端使用

技术栈

移动端 uniapp graceui

管理端 vue element

后台 springboot springsecurity mybatisPlus tio-websocket

主要功能

  • 笔记创建 编辑 发布

  • 点赞笔记、评论

  • 回复笔记、评论

  • 私信、客服聊天

  • 后台笔记编辑 下架

  • 后台博主冻结

  • 后台客服系统

  • 系统消息维护

思维导图

项目展示

项目启动

后台运行环境

  • jdk1.8

  • mysql5.7

  • redis

启动步骤

1.下载pom文件依赖

2.导入项目中的sql

3.修改yml参数(端口号 数据库名)

4.如果 mysql 报错 order by和group by 执行sql报错sql_mode=only_full_group_by问题解决


SELECT @@GLOBAL.sql_mode;  

SELECT @@SESSION.sql_mode;  

set @@GLOBAL.sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';  

set @@SESSION.sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';  

FLUSH PRIVILEGES;  

5.打包 mvn clean package -Dmaven.test.skip=true 打包后jar文件可以正常运行

管理端前台运行环境

  • node v14.21.3

  • elementui 2.15.14

  • vue 2.6.14

启动步骤

1.cd 根目录的web文件夹下

2.执行npm install下载依赖

3.执行npm run dev启动

4.修改.env.xx修改测试环境和正式环境端口

5.npm run build进行打包

6.如果出现lemon imui 依赖报错 解压根目录下dist.rar文件夹到lemon imui根目录下即可

移动端前台

hbudiler 3.99

vue 3

移动端前台使用了graceUi6.0 需授权后找我覆盖文件

包结构说名

后端

  • 目前后端功能基本能满足我们需求

  • 后续开发建立和system同级目录 进行功能开发即可

写到最后

代码地址 https://gitee.com/ddeatrr/springboot_vue_xhs

收起阅读 »

我写了一个插件在uniapp上使用echarts-gl,支持Vue3,并配上了demo

echart uniapp

插件地址
这个就是一个对echarts的renderjs的封装。
用法也很简单。就是<uc-charts :option="option"/>
option变化自动更新。
app和web项目都是支持的。web的话你如果设置了web的根路径需要改下uc-charts.vue的文件的引用路径

继续阅读 »

插件地址
这个就是一个对echarts的renderjs的封装。
用法也很简单。就是<uc-charts :option="option"/>
option变化自动更新。
app和web项目都是支持的。web的话你如果设置了web的根路径需要改下uc-charts.vue的文件的引用路径

收起阅读 »

uni-app-x记账App完整源码&ApiFox接口实战项目

uni-app-x

简介
掌握真实项目开发技能!
本源码提供了一个完整的、已经实现的记账应用程序,使用了流行的uni-app-x框架进行前端构建,配合Apifox定义的API接口完成所有前后端交互逻辑。这不仅是一个学习如何在uni-app-x中创建高效、用户友好的界面的机会,更是一次深入了解实际项目开发流程的宝贵经验。
适合人群
无论是初学者还是有一定基础的开发者,这套源码都是提升技能的理想选择。对于那些渴望深入理解uni-app-x以及移动应用开发的人来说,这无疑是一笔值得投资的知识财富。

获取源码

继续阅读 »

简介
掌握真实项目开发技能!
本源码提供了一个完整的、已经实现的记账应用程序,使用了流行的uni-app-x框架进行前端构建,配合Apifox定义的API接口完成所有前后端交互逻辑。这不仅是一个学习如何在uni-app-x中创建高效、用户友好的界面的机会,更是一次深入了解实际项目开发流程的宝贵经验。
适合人群
无论是初学者还是有一定基础的开发者,这套源码都是提升技能的理想选择。对于那些渴望深入理解uni-app-x以及移动应用开发的人来说,这无疑是一笔值得投资的知识财富。

获取源码

收起阅读 »

Android请求对所有文件的访问权限

清单文件添加文件读写权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />  
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />  
class FileUtils {  

          static checkPermission() {  
                const Build = plus.android.importClass('android.os.Build');  
                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){  
                    const Environment =  plus.android.importClass("android.os.Environment");  
                    return Environment.isExternalStorageManager();  
                }  
                return true  
          }  

          static applyPermission(fn) {  
              const Build = plus.android.importClass('android.os.Build');  
              if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){  
                var Intent = plus.android.importClass("android.content.Intent");  
                var Settings = plus.android.importClass("android.provider.Settings");  
                var Uri = plus.android.importClass("android.net.Uri");  
                var mainActivity = plus.android.runtimeMainActivity();  

                var intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);  
                var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);  
                intent.setData(uri);  
                // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
                mainActivity.startActivity(intent);   
              }  
              fn({state:'ok'})  
          }  
    }
if(FileUtils.checkPermission()==false){  
                    that.showLoading("请稍等")  
                    FileUtils.applyPermission(res => {  
                        uni.hideLoading()  
                    })  
                    return  
                }
继续阅读 »

清单文件添加文件读写权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />  
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />  
class FileUtils {  

          static checkPermission() {  
                const Build = plus.android.importClass('android.os.Build');  
                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){  
                    const Environment =  plus.android.importClass("android.os.Environment");  
                    return Environment.isExternalStorageManager();  
                }  
                return true  
          }  

          static applyPermission(fn) {  
              const Build = plus.android.importClass('android.os.Build');  
              if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){  
                var Intent = plus.android.importClass("android.content.Intent");  
                var Settings = plus.android.importClass("android.provider.Settings");  
                var Uri = plus.android.importClass("android.net.Uri");  
                var mainActivity = plus.android.runtimeMainActivity();  

                var intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);  
                var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);  
                intent.setData(uri);  
                // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
                mainActivity.startActivity(intent);   
              }  
              fn({state:'ok'})  
          }  
    }
if(FileUtils.checkPermission()==false){  
                    that.showLoading("请稍等")  
                    FileUtils.applyPermission(res => {  
                        uni.hideLoading()  
                    })  
                    return  
                }
收起阅读 »