
升级到3.0.4.20201231并重新云打包后 uni.getStorageSync 取不到数据
升级到 3.0.4.20201231
并重新云打包后 (自定义基座)
uni.getStorageSync 取不到数据
各位是不是有这个问题???
升级到 3.0.4.20201231
并重新云打包后 (自定义基座)
uni.getStorageSync 取不到数据
各位是不是有这个问题???

类似element中select 框带搜索功能(前端实现搜索)
uniapp也是第一次接触,效果图:

上代码:
<template>
<u-form-item label="所属城市" label-width="60px">
<u-input v-model="region.name"
ref="searchInp"
@input="filterRegion"
@blur="hideSearch"
@click="flag = true"/>
<view class="popup" v-show="flag" >
<view class="sanjiao"></view>
<view class="search" v-if="searchVal.length > 0">
<u-loading class="search-loading" mode="circle" :show="search_loading"></u-loading>
<text class="o-li"
@click="handleRegion(item)"
v-for="item in searchVal"
>{{ item.region_name }}</text>
</view>
<view class="not-data" v-else>--无匹配数据--</view>
</view>
</u-form-item>
</template>
<script>
export default {
data() {
return {
flag: false,
search_loading: false,
searchVal: [],
region: {
name: '',
id: ''
},
list:[
{region_name: '池州', region_id: 10001},
{region_name: '惠州', region_id: 10001},
{region_name: '徽州', region_id: 10001},
{region_name: '凉州', region_id: 10001},
{region_name: '常州', region_id: 10001},
{region_name: '苏州', region_id: 10001},
{region_name: '京州', region_id: 10001},
{region_name: '永州', region_id: 10001}
]
}
},
props: {
},
created() {
this.searchVal = this.list;
},
methods:{
handleRegion(item) {
this.region.name = item.region_name;
this.region.id = item.region_id;
this.flag = false;
this.$emit("getRegion", this.region);
},
hideSearch() {
setTimeout(() => {
this.flag = false;
},300)
},
filterRegion() {
let ziduan = this.region.name;
let rows = [];
this.search_loading = true;
rows = this.list.filter(item => {
if(item.region_name.indexOf(ziduan) != -1){
return item
}
})
if(ziduan.length > 0 ){
this.searchVal = rows;
this.search_loading = false;
}else{
this.searchVal = this.list;
this.search_loading = false;
}
}
}
}
</script>
<style lang="scss">
.popup{
padding: 8px 0;
position: absolute;
top: 10vh;
z-index: 99;
transition: all 1s ease-in;
.sanjiao{
display: block;
width: 8px;
height: 8px;
background-color: #fff;
position: absolute;
top: -4px;
left: 30px;
transform: rotate(45deg);
}
.not-data{
width: 80vw;
border-radius: 5px;
line-height: 60rpx;
text-align: center;
background-color: #fff;
margin-top: -8px;
box-shadow: 0 -2px 12px 0 rgba(0,0,0,.1);
color: #666;
font-size: 24rpx;
}
.search{
width: 80vw;
max-height: 50vh;
overflow: hidden;
overflow-y: auto;
background-color: #fff;
border-radius: 5px;
margin-top: -8px;
box-shadow: 0 -2px 12px 0 rgba(0,0,0,.1);
.o-li{
display: block;
line-height: 60rpx;
padding: 0 5px;
&:after{
content: "";
display: block;
width: 100%;
height: 1px;
background-color: #eee;
transform: scaleY(0.5);
}
}
.search-loading{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
</style>
注意项: 如果要写成组件,在小程序中是不支持props中的值直接修改的,处理办法是 直接在组件内部增加一个对象,来替换props的值;
uniapp也是第一次接触,效果图:
上代码:
<template>
<u-form-item label="所属城市" label-width="60px">
<u-input v-model="region.name"
ref="searchInp"
@input="filterRegion"
@blur="hideSearch"
@click="flag = true"/>
<view class="popup" v-show="flag" >
<view class="sanjiao"></view>
<view class="search" v-if="searchVal.length > 0">
<u-loading class="search-loading" mode="circle" :show="search_loading"></u-loading>
<text class="o-li"
@click="handleRegion(item)"
v-for="item in searchVal"
>{{ item.region_name }}</text>
</view>
<view class="not-data" v-else>--无匹配数据--</view>
</view>
</u-form-item>
</template>
<script>
export default {
data() {
return {
flag: false,
search_loading: false,
searchVal: [],
region: {
name: '',
id: ''
},
list:[
{region_name: '池州', region_id: 10001},
{region_name: '惠州', region_id: 10001},
{region_name: '徽州', region_id: 10001},
{region_name: '凉州', region_id: 10001},
{region_name: '常州', region_id: 10001},
{region_name: '苏州', region_id: 10001},
{region_name: '京州', region_id: 10001},
{region_name: '永州', region_id: 10001}
]
}
},
props: {
},
created() {
this.searchVal = this.list;
},
methods:{
handleRegion(item) {
this.region.name = item.region_name;
this.region.id = item.region_id;
this.flag = false;
this.$emit("getRegion", this.region);
},
hideSearch() {
setTimeout(() => {
this.flag = false;
},300)
},
filterRegion() {
let ziduan = this.region.name;
let rows = [];
this.search_loading = true;
rows = this.list.filter(item => {
if(item.region_name.indexOf(ziduan) != -1){
return item
}
})
if(ziduan.length > 0 ){
this.searchVal = rows;
this.search_loading = false;
}else{
this.searchVal = this.list;
this.search_loading = false;
}
}
}
}
</script>
<style lang="scss">
.popup{
padding: 8px 0;
position: absolute;
top: 10vh;
z-index: 99;
transition: all 1s ease-in;
.sanjiao{
display: block;
width: 8px;
height: 8px;
background-color: #fff;
position: absolute;
top: -4px;
left: 30px;
transform: rotate(45deg);
}
.not-data{
width: 80vw;
border-radius: 5px;
line-height: 60rpx;
text-align: center;
background-color: #fff;
margin-top: -8px;
box-shadow: 0 -2px 12px 0 rgba(0,0,0,.1);
color: #666;
font-size: 24rpx;
}
.search{
width: 80vw;
max-height: 50vh;
overflow: hidden;
overflow-y: auto;
background-color: #fff;
border-radius: 5px;
margin-top: -8px;
box-shadow: 0 -2px 12px 0 rgba(0,0,0,.1);
.o-li{
display: block;
line-height: 60rpx;
padding: 0 5px;
&:after{
content: "";
display: block;
width: 100%;
height: 1px;
background-color: #eee;
transform: scaleY(0.5);
}
}
.search-loading{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
</style>
收起阅读 »注意项: 如果要写成组件,在小程序中是不支持props中的值直接修改的,处理办法是 直接在组件内部增加一个对象,来替换props的值;

Vue3.0仿layer全局对话框组件|vue3自定义弹层
介绍
v3layer弹层 基于vue3.0开发的仿layer.js桌面端对话框组件。支持7+弹窗动画、10+弹窗类型、30+参数配置,拥有流畅的拖拽、缩放、最大化及全屏等功能。轻松实现各种弹窗效果。
vue3.0 mobile移动端自定义弹层组件v3popup
V3Layer支持 Msg、Modal、Dialog、Message、Notification、ActionSheet、Toast、Popover
等多种弹窗类型。
引入组件
// 在main.js中全局引入组件
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 引入饿了么vue3组件库
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
// 引入弹窗组件
import Vue3Layer from './components/v3layer'
app.use(ElementPlus)
app.use(Vue3Layer)
app.mount('#app')
支持如下两种调用形式。
标签式
<v3-layer
v-model="showDialog"
title="标题内容"
content="<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>"
z-index="1011"
lockScroll="false"
xclose
resize
dragOut
:btns="[
{text: '取消', click: () => showDialog=false},
{text: '确认', style: 'color:#f90;', click: handleSure},
]"
>
<template v-slot:content>这里是自定义插槽内容信息!</template>
</v3-layer>
函数式
let $el = v3layer({
title: '标题内容',
content: '<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>',
shadeClose: false,
zIndex: 1011,
lockScroll: false,
xclose: true,
resize: true,
dragOut: true,
btns: [
{text: '取消', click: () => { $el.close() }},
{text: '确认', click: () => handleSure},
]
});
如上图:还支持 message popover nofity
三种弹窗类型。
vue2中可以通过prototype
来实现挂载全局方法。
vue3中提供了两种全新的挂载全局方法 app.config.globalProperties
和 app.provide
。
如果使用第一种方式:
// vue2.x中调用
methods: {
showDialog() {
this.$v3layer({...})
}
}
// vue3.x中调用
setup() {
// 获取上下文
const { ctx } = getCurrentInstance()
ctx.$v3layer({...})
}
如果使用第二种方式:
// vue2.x中调用
methods: {
showDialog() {
this.v3layer({...})
}
}
// vue3.x中调用
setup() {
const v3layer = inject('v3layer')
const showDialog = () => {
v3layer({...})
}
return {
v3layer,
showDialog
}
}
大家感兴趣可以去官网看看文档说明。
https://v3.cn.vuejs.org/api/application-config.html#globalproperties
https://v3.cn.vuejs.org/guide/component-provide-inject.html
编码实现
v3layer支持如下参数自定义配置。
|props参数|
v-model 是否显示弹框
id 弹窗唯一标识
title 标题
content 内容(支持String、带标签内容、自定义插槽内容)***如果content内容比较复杂,推荐使用标签式写法
type 弹框类型(toast|footer|actionsheet|actionsheetPicker|android|ios|contextmenu|drawer|iframe)
layerStyle 自定义弹窗样式
icon toast图标(loading | success | fail)
shade 是否显示遮罩层
shadeClose 是否点击遮罩时关闭弹窗
lockScroll 是否弹窗出现时将body滚动锁定
opacity 遮罩层透明度
xclose 是否显示关闭图标
xposition 关闭图标位置(left | right | top | bottom)
xcolor 关闭图标颜色
anim 弹窗动画(scaleIn | fadeIn | footer | fadeInUp | fadeInDown | fadeInLeft | fadeInRight)
position 弹出位置(auto | ['100px','50px'] | t | r | b | l | lt | rt | lb | rb)
drawer 抽屉弹窗(top | right | bottom | left)
follow 跟随元素定位弹窗(支持元素.kk #kk 或 [e.clientX, e.clientY])
time 弹窗自动关闭秒数(1、2、3)
zIndex 弹窗层叠(默认8080)
teleport 指定挂载节点(默认是挂载组件标签位置,可通过teleport自定义挂载位置) teleport="body | #xxx | .xxx"
topmost 置顶当前窗口(默认false)
area 弹窗宽高(默认auto)设置宽度area: '300px' 设置高度area:['', '200px'] 设置宽高area:['350px', '150px']
maxWidth 弹窗最大宽度(只有当area:'auto'时,maxWidth的设定才有效)
maximize 是否显示最大化按钮(默认false)
fullscreen 全屏弹窗(默认false)
fixed 弹窗是否固定
drag 拖拽元素(可定义选择器drag:'.xxx' | 禁止拖拽drag:false)
dragOut 是否允许拖拽到窗口外(默认false)
lockAxis 限制拖拽方向可选: v 垂直、h 水平,默认不限制
resize 是否允许拉伸尺寸(默认false)
btns 弹窗按钮(参数:text|style|disabled|click)
++++++++++++++++++++++++++++++++++++++++++++++
|emit事件触发|
success 层弹出后回调(@success="xxx")
end 层销毁后回调(@end="xxx")
++++++++++++++++++++++++++++++++++++++++++++++
|event事件|
onSuccess 层打开回调事件
onEnd 层关闭回调事件
v3layer组件模板
<template>
<div ref="elRef" v-show="opened" class="vui__layer" :class="{'vui__layer-closed': closeCls}" :id="id">
<!-- //蒙版 -->
<div v-if="JSON.parse(shade)" class="vlayer__overlay" @click="shadeClicked" :style="{opacity}"></div>
<div class="vlayer__wrap" :class="[''+anim, type&&'popui__'+type, tipArrow]" :style="[layerStyle]">
<div v-if="title" class="vlayer__wrap-tit" v-html="title"></div>
<div v-if="type=='toast'&&icon" class="vlayer__toast-icon" :class="['vlayer'+icon]" v-html="toastIcon[icon]"></div>
<div class="vlayer__wrap-cntbox">
<!-- 判断插槽是否存在 -->
<template v-if="$slots.content">
<div class="vlayer__wrap-cnt"><slot name="content" /></div>
</template>
<template v-else>
<template v-if="content">
<iframe v-if="type=='iframe'" scrolling="auto" allowtransparency="true" frameborder="0" :src="content"></iframe>
<!-- message|notify|popover -->
<div v-else-if="type=='message' || type=='notify' || type=='popover'" class="vlayer__wrap-cnt">
<i v-if="icon" class="vlayer-msg__icon" :class="icon" v-html="messageIcon[icon]"></i>
<div class="vlayer-msg__group"><div v-if="title" class="vlayer-msg__title" v-html="title"></div><div v-html="content"></div></div>
</div>
<div v-else class="vlayer__wrap-cnt" v-html="content"></div>
</template>
</template>
<slot />
</div>
<div v-if="btns" class="vlayer__wrap-btns">
<span v-for="(btn,index) in btns" :key="index" class="btn" :style="btn.style" @click="btnClicked($event,index)" v-html="btn.text"></span>
</div>
<span v-if="xclose" class="vlayer__xclose" :class="!maximize&&xposition" :style="{'color': xcolor}" @click="close"></span>
<span v-if="maximize" class="vlayer__maximize" @click="maximizeClicked($event)"></span>
<span v-if="resize" class="vlayer__resize"></span>
</div>
<!-- 优化拖拽卡顿 -->
<div class="vlayer__dragfix"></div>
</div>
</template>
v3layer逻辑处理
<script>
import { onMounted, onUnmounted, ref, reactive, watch, toRefs, nextTick } from 'vue'
import domUtils from './utils/dom.js'
// 索引,蒙层控制,定时器
let $index = 0, $locknum = 0, $timer = {}, $closeTimer = null
export default {
props: {
// ...
},
emits: [
'update:modelValue'
],
setup(props, context) {
const elRef = ref(null);
const data = reactive({
opened: false,
closeCls: '',
toastIcon: {
// ...
},
messageIcon: {
// ...
},
vlayerOpts: {},
tipArrow: null,
})
onMounted(() => {
if(props.modelValue) {
open();
}
window.addEventListener('resize', autopos, false);
})
onUnmounted(() => {
window.removeEventListener('resize', autopos, false);
clearTimeout($closeTimer);
})
// 监听弹层v-model
watch(() => props.modelValue, (val) => {
// console.log('V3Layer is now [%s]', val ? 'show' : 'hide')
if(val) {
open();
}else {
close();
}
})
// 打开弹窗
const open = () => {
if(data.opened) return;
data.opened = true;
typeof props.onSuccess === 'function' && props.onSuccess();
const dom = elRef.value;
// 弹层挂载位置
if(props.teleport) {
nextTick(() => {
let teleportNode = document.querySelector(props.teleport);
teleportNode.appendChild(dom);
auto();
})
}
callback();
}
// 关闭弹窗
const close = () => {
if(!data.opened) return;
let dom = elRef.value;
let vlayero = dom.querySelector('.vlayer__wrap');
let ocnt = dom.querySelector('.vlayer__wrap-cntbox');
let omax = dom.querySelector('.vlayer__maximize');
data.closeCls = true;
clearTimeout($closeTimer);
$closeTimer = setTimeout(() => {
data.opened = false;
data.closeCls = false;
if(data.vlayerOpts.lockScroll) {
$locknum--;
if(!$locknum) {
document.body.style.paddingRight = '';
document.body.classList.remove('vui__body-hidden');
}
}
if(props.time) {
$index--;
}
// 清除弹窗样式
vlayero.style.width = vlayero.style.height = vlayero.style.top = vlayero.style.left = '';
ocnt.style.height = '';
omax && omax.classList.contains('maximized') && omax.classList.remove('maximized');
data.vlayerOpts.isBodyOverflow && (document.body.style.overflow = '');
context.emit('update:modelValue', false);
typeof props.onEnd === 'function' && props.onEnd();
}, 200)
}
// 弹窗位置
const auto = () => {
// ...
autopos();
// 全屏弹窗
if(props.fullscreen) {
full();
}
// 弹窗拖动|缩放
move();
}
const autopos = () => {
if(!data.opened) return;
let oL, oT
let pos = props.position;
let isFixed = JSON.parse(props.fixed);
let dom = elRef.value;
let vlayero = dom.querySelector('.vlayer__wrap');
if(!isFixed || props.follow) {
vlayero.style.position = 'absolute';
}
let area = [domUtils.client('width'), domUtils.client('height'), vlayero.offsetWidth, vlayero.offsetHeight]
oL = (area[0] - area[2]) / 2;
oT = (area[1] - area[3]) / 2;
if(props.follow) {
offset();
}else {
typeof pos === 'object' ? (
oL = parseFloat(pos[0]) || 0, oT = parseFloat(pos[1]) || 0
) : (
pos == 't' ? oT = 0 :
pos == 'r' ? oL = area[0] - area[2] :
pos == 'b' ? oT = area[1] - area[3] :
pos == 'l' ? oL = 0 :
pos == 'lt' ? (oL = 0, oT = 0) :
pos == 'rt' ? (oL = area[0] - area[2], oT = 0) :
pos == 'lb' ? (oL = 0, oT = area[1] - area[3]) :
pos == 'rb' ? (oL = area[0] - area[2], oT = area[1] - area[3]) :
null
)
vlayero.style.left = parseFloat(isFixed ? oL : domUtils.scroll('left') + oL) + 'px';
vlayero.style.top = parseFloat(isFixed ? oT : domUtils.scroll('top') + oT) + 'px';
}
}
// 元素跟随定位
const offset = () => {
let oW, oH, pS
let dom = elRef.value
let vlayero = dom.querySelector('.vlayer__wrap');
oW = vlayero.offsetWidth;
oH = vlayero.offsetHeight;
pS = domUtils.getFollowRect(props.follow, oW, oH);
data.tipArrow = pS[2];
vlayero.style.left = pS[0] + 'px';
vlayero.style.top = pS[1] + 'px';
}
// 最大化弹窗
const full = () => {
// ...
}
// 恢复弹窗
const restore = () => {
let dom = elRef.value;
let vlayero = dom.querySelector('.vlayer__wrap');
let otit = dom.querySelector('.vlayer__wrap-tit');
let ocnt = dom.querySelector('.vlayer__wrap-cntbox');
let obtn = dom.querySelector('.vlayer__wrap-btns');
let omax = dom.querySelector('.vlayer__maximize');
let t = otit ? otit.offsetHeight : 0
let b = obtn ? obtn.offsetHeight : 0
if(!data.vlayerOpts.lockScroll) {
data.vlayerOpts.isBodyOverflow = false;
document.body.style.overflow = '';
}
props.maximize && omax.classList.remove('maximized')
vlayero.style.left = parseFloat(data.vlayerOpts.rect[0]) + 'px';
vlayero.style.top = parseFloat(data.vlayerOpts.rect[1]) + 'px';
vlayero.style.width = parseFloat(data.vlayerOpts.rect[2]) + 'px';
vlayero.style.height = parseFloat(data.vlayerOpts.rect[3]) + 'px';
}
// 拖动|缩放弹窗
const move = () => {
// ...
}
// 事件处理
const callback = () => {
// 倒计时关闭
if(props.time) {
$index++
// 防止重复点击
if($timer[$index] !== null) clearTimeout($timer[$index])
$timer[$index] = setTimeout(() => {
close();
}, parseInt(props.time) * 1000)
}
}
// 点击最大化按钮
const maximizeClicked = (e) => {
let o = e.target
if(o.classList.contains('maximized')) {
// 恢复
restore();
} else {
// 最大化
full();
}
}
// 点击遮罩层
const shadeClicked = () => {
if(JSON.parse(props.shadeClose)) {
close();
}
}
// 按钮事件
const btnClicked = (e, index) => {
let btn = props.btns[index]
if(!btn.disabled) {
typeof btn.click === 'function' && btn.click(e)
}
}
return {
...toRefs(data),
elRef,
close,
maximizeClicked,
shadeClicked,
btnClicked,
}
}
}
</script>
大家可以在此逻辑基础上自行开发一些新功能。
okay,基于vue3开发自定义pc端弹窗组件就分享这么多,希望以上分享对大家有所帮助哈~~
React+Hooks自定义pc端弹窗组件|react.js自定义对话框
链接:https://juejin.cn/post/6913710656428457991/
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
介绍
v3layer弹层 基于vue3.0开发的仿layer.js桌面端对话框组件。支持7+弹窗动画、10+弹窗类型、30+参数配置,拥有流畅的拖拽、缩放、最大化及全屏等功能。轻松实现各种弹窗效果。
vue3.0 mobile移动端自定义弹层组件v3popup
V3Layer支持 Msg、Modal、Dialog、Message、Notification、ActionSheet、Toast、Popover
等多种弹窗类型。
引入组件
// 在main.js中全局引入组件
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 引入饿了么vue3组件库
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
// 引入弹窗组件
import Vue3Layer from './components/v3layer'
app.use(ElementPlus)
app.use(Vue3Layer)
app.mount('#app')
支持如下两种调用形式。
标签式
<v3-layer
v-model="showDialog"
title="标题内容"
content="<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>"
z-index="1011"
lockScroll="false"
xclose
resize
dragOut
:btns="[
{text: '取消', click: () => showDialog=false},
{text: '确认', style: 'color:#f90;', click: handleSure},
]"
>
<template v-slot:content>这里是自定义插槽内容信息!</template>
</v3-layer>
函数式
let $el = v3layer({
title: '标题内容',
content: '<div style='color:#f57b16;padding:30px;'>这里是内容信息!</div>',
shadeClose: false,
zIndex: 1011,
lockScroll: false,
xclose: true,
resize: true,
dragOut: true,
btns: [
{text: '取消', click: () => { $el.close() }},
{text: '确认', click: () => handleSure},
]
});
如上图:还支持 message popover nofity
三种弹窗类型。
vue2中可以通过prototype
来实现挂载全局方法。
vue3中提供了两种全新的挂载全局方法 app.config.globalProperties
和 app.provide
。
如果使用第一种方式:
// vue2.x中调用
methods: {
showDialog() {
this.$v3layer({...})
}
}
// vue3.x中调用
setup() {
// 获取上下文
const { ctx } = getCurrentInstance()
ctx.$v3layer({...})
}
如果使用第二种方式:
// vue2.x中调用
methods: {
showDialog() {
this.v3layer({...})
}
}
// vue3.x中调用
setup() {
const v3layer = inject('v3layer')
const showDialog = () => {
v3layer({...})
}
return {
v3layer,
showDialog
}
}
大家感兴趣可以去官网看看文档说明。
https://v3.cn.vuejs.org/api/application-config.html#globalproperties
https://v3.cn.vuejs.org/guide/component-provide-inject.html
编码实现
v3layer支持如下参数自定义配置。
|props参数|
v-model 是否显示弹框
id 弹窗唯一标识
title 标题
content 内容(支持String、带标签内容、自定义插槽内容)***如果content内容比较复杂,推荐使用标签式写法
type 弹框类型(toast|footer|actionsheet|actionsheetPicker|android|ios|contextmenu|drawer|iframe)
layerStyle 自定义弹窗样式
icon toast图标(loading | success | fail)
shade 是否显示遮罩层
shadeClose 是否点击遮罩时关闭弹窗
lockScroll 是否弹窗出现时将body滚动锁定
opacity 遮罩层透明度
xclose 是否显示关闭图标
xposition 关闭图标位置(left | right | top | bottom)
xcolor 关闭图标颜色
anim 弹窗动画(scaleIn | fadeIn | footer | fadeInUp | fadeInDown | fadeInLeft | fadeInRight)
position 弹出位置(auto | ['100px','50px'] | t | r | b | l | lt | rt | lb | rb)
drawer 抽屉弹窗(top | right | bottom | left)
follow 跟随元素定位弹窗(支持元素.kk #kk 或 [e.clientX, e.clientY])
time 弹窗自动关闭秒数(1、2、3)
zIndex 弹窗层叠(默认8080)
teleport 指定挂载节点(默认是挂载组件标签位置,可通过teleport自定义挂载位置) teleport="body | #xxx | .xxx"
topmost 置顶当前窗口(默认false)
area 弹窗宽高(默认auto)设置宽度area: '300px' 设置高度area:['', '200px'] 设置宽高area:['350px', '150px']
maxWidth 弹窗最大宽度(只有当area:'auto'时,maxWidth的设定才有效)
maximize 是否显示最大化按钮(默认false)
fullscreen 全屏弹窗(默认false)
fixed 弹窗是否固定
drag 拖拽元素(可定义选择器drag:'.xxx' | 禁止拖拽drag:false)
dragOut 是否允许拖拽到窗口外(默认false)
lockAxis 限制拖拽方向可选: v 垂直、h 水平,默认不限制
resize 是否允许拉伸尺寸(默认false)
btns 弹窗按钮(参数:text|style|disabled|click)
++++++++++++++++++++++++++++++++++++++++++++++
|emit事件触发|
success 层弹出后回调(@success="xxx")
end 层销毁后回调(@end="xxx")
++++++++++++++++++++++++++++++++++++++++++++++
|event事件|
onSuccess 层打开回调事件
onEnd 层关闭回调事件
v3layer组件模板
<template>
<div ref="elRef" v-show="opened" class="vui__layer" :class="{'vui__layer-closed': closeCls}" :id="id">
<!-- //蒙版 -->
<div v-if="JSON.parse(shade)" class="vlayer__overlay" @click="shadeClicked" :style="{opacity}"></div>
<div class="vlayer__wrap" :class="[''+anim, type&&'popui__'+type, tipArrow]" :style="[layerStyle]">
<div v-if="title" class="vlayer__wrap-tit" v-html="title"></div>
<div v-if="type=='toast'&&icon" class="vlayer__toast-icon" :class="['vlayer'+icon]" v-html="toastIcon[icon]"></div>
<div class="vlayer__wrap-cntbox">
<!-- 判断插槽是否存在 -->
<template v-if="$slots.content">
<div class="vlayer__wrap-cnt"><slot name="content" /></div>
</template>
<template v-else>
<template v-if="content">
<iframe v-if="type=='iframe'" scrolling="auto" allowtransparency="true" frameborder="0" :src="content"></iframe>
<!-- message|notify|popover -->
<div v-else-if="type=='message' || type=='notify' || type=='popover'" class="vlayer__wrap-cnt">
<i v-if="icon" class="vlayer-msg__icon" :class="icon" v-html="messageIcon[icon]"></i>
<div class="vlayer-msg__group"><div v-if="title" class="vlayer-msg__title" v-html="title"></div><div v-html="content"></div></div>
</div>
<div v-else class="vlayer__wrap-cnt" v-html="content"></div>
</template>
</template>
<slot />
</div>
<div v-if="btns" class="vlayer__wrap-btns">
<span v-for="(btn,index) in btns" :key="index" class="btn" :style="btn.style" @click="btnClicked($event,index)" v-html="btn.text"></span>
</div>
<span v-if="xclose" class="vlayer__xclose" :class="!maximize&&xposition" :style="{'color': xcolor}" @click="close"></span>
<span v-if="maximize" class="vlayer__maximize" @click="maximizeClicked($event)"></span>
<span v-if="resize" class="vlayer__resize"></span>
</div>
<!-- 优化拖拽卡顿 -->
<div class="vlayer__dragfix"></div>
</div>
</template>
v3layer逻辑处理
<script>
import { onMounted, onUnmounted, ref, reactive, watch, toRefs, nextTick } from 'vue'
import domUtils from './utils/dom.js'
// 索引,蒙层控制,定时器
let $index = 0, $locknum = 0, $timer = {}, $closeTimer = null
export default {
props: {
// ...
},
emits: [
'update:modelValue'
],
setup(props, context) {
const elRef = ref(null);
const data = reactive({
opened: false,
closeCls: '',
toastIcon: {
// ...
},
messageIcon: {
// ...
},
vlayerOpts: {},
tipArrow: null,
})
onMounted(() => {
if(props.modelValue) {
open();
}
window.addEventListener('resize', autopos, false);
})
onUnmounted(() => {
window.removeEventListener('resize', autopos, false);
clearTimeout($closeTimer);
})
// 监听弹层v-model
watch(() => props.modelValue, (val) => {
// console.log('V3Layer is now [%s]', val ? 'show' : 'hide')
if(val) {
open();
}else {
close();
}
})
// 打开弹窗
const open = () => {
if(data.opened) return;
data.opened = true;
typeof props.onSuccess === 'function' && props.onSuccess();
const dom = elRef.value;
// 弹层挂载位置
if(props.teleport) {
nextTick(() => {
let teleportNode = document.querySelector(props.teleport);
teleportNode.appendChild(dom);
auto();
})
}
callback();
}
// 关闭弹窗
const close = () => {
if(!data.opened) return;
let dom = elRef.value;
let vlayero = dom.querySelector('.vlayer__wrap');
let ocnt = dom.querySelector('.vlayer__wrap-cntbox');
let omax = dom.querySelector('.vlayer__maximize');
data.closeCls = true;
clearTimeout($closeTimer);
$closeTimer = setTimeout(() => {
data.opened = false;
data.closeCls = false;
if(data.vlayerOpts.lockScroll) {
$locknum--;
if(!$locknum) {
document.body.style.paddingRight = '';
document.body.classList.remove('vui__body-hidden');
}
}
if(props.time) {
$index--;
}
// 清除弹窗样式
vlayero.style.width = vlayero.style.height = vlayero.style.top = vlayero.style.left = '';
ocnt.style.height = '';
omax && omax.classList.contains('maximized') && omax.classList.remove('maximized');
data.vlayerOpts.isBodyOverflow && (document.body.style.overflow = '');
context.emit('update:modelValue', false);
typeof props.onEnd === 'function' && props.onEnd();
}, 200)
}
// 弹窗位置
const auto = () => {
// ...
autopos();
// 全屏弹窗
if(props.fullscreen) {
full();
}
// 弹窗拖动|缩放
move();
}
const autopos = () => {
if(!data.opened) return;
let oL, oT
let pos = props.position;
let isFixed = JSON.parse(props.fixed);
let dom = elRef.value;
let vlayero = dom.querySelector('.vlayer__wrap');
if(!isFixed || props.follow) {
vlayero.style.position = 'absolute';
}
let area = [domUtils.client('width'), domUtils.client('height'), vlayero.offsetWidth, vlayero.offsetHeight]
oL = (area[0] - area[2]) / 2;
oT = (area[1] - area[3]) / 2;
if(props.follow) {
offset();
}else {
typeof pos === 'object' ? (
oL = parseFloat(pos[0]) || 0, oT = parseFloat(pos[1]) || 0
) : (
pos == 't' ? oT = 0 :
pos == 'r' ? oL = area[0] - area[2] :
pos == 'b' ? oT = area[1] - area[3] :
pos == 'l' ? oL = 0 :
pos == 'lt' ? (oL = 0, oT = 0) :
pos == 'rt' ? (oL = area[0] - area[2], oT = 0) :
pos == 'lb' ? (oL = 0, oT = area[1] - area[3]) :
pos == 'rb' ? (oL = area[0] - area[2], oT = area[1] - area[3]) :
null
)
vlayero.style.left = parseFloat(isFixed ? oL : domUtils.scroll('left') + oL) + 'px';
vlayero.style.top = parseFloat(isFixed ? oT : domUtils.scroll('top') + oT) + 'px';
}
}
// 元素跟随定位
const offset = () => {
let oW, oH, pS
let dom = elRef.value
let vlayero = dom.querySelector('.vlayer__wrap');
oW = vlayero.offsetWidth;
oH = vlayero.offsetHeight;
pS = domUtils.getFollowRect(props.follow, oW, oH);
data.tipArrow = pS[2];
vlayero.style.left = pS[0] + 'px';
vlayero.style.top = pS[1] + 'px';
}
// 最大化弹窗
const full = () => {
// ...
}
// 恢复弹窗
const restore = () => {
let dom = elRef.value;
let vlayero = dom.querySelector('.vlayer__wrap');
let otit = dom.querySelector('.vlayer__wrap-tit');
let ocnt = dom.querySelector('.vlayer__wrap-cntbox');
let obtn = dom.querySelector('.vlayer__wrap-btns');
let omax = dom.querySelector('.vlayer__maximize');
let t = otit ? otit.offsetHeight : 0
let b = obtn ? obtn.offsetHeight : 0
if(!data.vlayerOpts.lockScroll) {
data.vlayerOpts.isBodyOverflow = false;
document.body.style.overflow = '';
}
props.maximize && omax.classList.remove('maximized')
vlayero.style.left = parseFloat(data.vlayerOpts.rect[0]) + 'px';
vlayero.style.top = parseFloat(data.vlayerOpts.rect[1]) + 'px';
vlayero.style.width = parseFloat(data.vlayerOpts.rect[2]) + 'px';
vlayero.style.height = parseFloat(data.vlayerOpts.rect[3]) + 'px';
}
// 拖动|缩放弹窗
const move = () => {
// ...
}
// 事件处理
const callback = () => {
// 倒计时关闭
if(props.time) {
$index++
// 防止重复点击
if($timer[$index] !== null) clearTimeout($timer[$index])
$timer[$index] = setTimeout(() => {
close();
}, parseInt(props.time) * 1000)
}
}
// 点击最大化按钮
const maximizeClicked = (e) => {
let o = e.target
if(o.classList.contains('maximized')) {
// 恢复
restore();
} else {
// 最大化
full();
}
}
// 点击遮罩层
const shadeClicked = () => {
if(JSON.parse(props.shadeClose)) {
close();
}
}
// 按钮事件
const btnClicked = (e, index) => {
let btn = props.btns[index]
if(!btn.disabled) {
typeof btn.click === 'function' && btn.click(e)
}
}
return {
...toRefs(data),
elRef,
close,
maximizeClicked,
shadeClicked,
btnClicked,
}
}
}
</script>
大家可以在此逻辑基础上自行开发一些新功能。
okay,基于vue3开发自定义pc端弹窗组件就分享这么多,希望以上分享对大家有所帮助哈~~
React+Hooks自定义pc端弹窗组件|react.js自定义对话框
链接:https://juejin.cn/post/6913710656428457991/
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

写代码害人?抓!公安机关逮捕涉网络诈骗APP技术开发嫌疑人519名
12月31日10时,在公安部统一指挥下,北京、辽宁、湖南、广东等28个省区市公安机关同步开展集中收网行动,依法严厉打击从事涉电信网络诈骗APP技术开发的违法犯罪团伙。 截至当日15时,共捣毁违法犯罪窝点158个,抓获违法犯罪嫌疑人519名。
当前,随着信息技术快速发展,各类APP已成为满足人民群众获取信息、投资理财、贷款转账等各类需求的重要渠道,在给日常生产生活带来便利的同时,也被一些不法分子用于违法犯罪活动。公安机关在工作中发现,一些电信网络诈骗犯罪团伙为提高诈骗成功率,打着正规平台的旗号诱导受害人下载虚假APP,并诱骗受害人在该APP上进行投资理财、申请贷款等,进而骗取受害人钱款。
今年以来,通过虚假APP实施的电信网络诈骗案件高发多发,严重危害人民群众财产安全和社会经济秩序。公安部对此高度重视,深入研判该类案件规律特点,对涉电信网络诈骗APP的开发、封装、应用各个环节开展分析研究,部署开展专案侦办,要求全链条打击相关黑灰产业。
经专案组缜密侦查,公安机关梳理出一批违法犯罪线索,涉及全国24个省区市,并初步掌握了大量犯罪事实和证据。在此基础上,公安部部署对从事涉电信网络诈骗APP技术开发的违法犯罪团伙开展集中抓捕行动,严打团伙,捣毁窝点,斩断链条,切实从源头上遏制电信网络诈骗犯罪的高发态势。
据了解,诈骗团伙利用虚假APP实施的电信网络诈骗案件占该类案件约60%,虚假APP已成为整个犯罪链条中不可或缺的关键环节,并由此催生出一个庞大的技术开发灰色产业链,大量违法犯罪人员参与其中。
相关技术开发人员分工明确、团伙作案,围绕电信网络诈骗犯罪团伙的具体需求“量身定制”APP各种诈骗功能,有的负责编写程序代码,有的负责购买域名和租用服务器,有的负责APP的封装和分发。经各环节层层运作,最终将虚假APP贩卖给诈骗团伙。 随后,诈骗团伙根据其不法目的和APP功能特点,将其包装成极具迷惑性的“正规”应用平台,诱骗受害人点击链接或扫描二维码下载APP,进而实施诈骗。
公安部有关负责人表示,打击涉电信网络诈骗APP技术开发违法犯罪活动,是全链条打击电信网络诈骗黑灰产业的重要举措。公安机关将继续保持对此类犯罪的严打高压态势,坚决斩断为电信诈骗等违法犯罪提供技术、资金等各类非法服务的犯罪链条,切实维护人民群众财产安全和合法权益。 同时,公安机关提醒广大群众增强防范意识,对于未知来源的APP不下载、不点击、不扫码,切实守护好自己的钱袋子。
此文章转载自央视新闻,如有不当联系删除。原文链接
12月31日10时,在公安部统一指挥下,北京、辽宁、湖南、广东等28个省区市公安机关同步开展集中收网行动,依法严厉打击从事涉电信网络诈骗APP技术开发的违法犯罪团伙。 截至当日15时,共捣毁违法犯罪窝点158个,抓获违法犯罪嫌疑人519名。
当前,随着信息技术快速发展,各类APP已成为满足人民群众获取信息、投资理财、贷款转账等各类需求的重要渠道,在给日常生产生活带来便利的同时,也被一些不法分子用于违法犯罪活动。公安机关在工作中发现,一些电信网络诈骗犯罪团伙为提高诈骗成功率,打着正规平台的旗号诱导受害人下载虚假APP,并诱骗受害人在该APP上进行投资理财、申请贷款等,进而骗取受害人钱款。
今年以来,通过虚假APP实施的电信网络诈骗案件高发多发,严重危害人民群众财产安全和社会经济秩序。公安部对此高度重视,深入研判该类案件规律特点,对涉电信网络诈骗APP的开发、封装、应用各个环节开展分析研究,部署开展专案侦办,要求全链条打击相关黑灰产业。
经专案组缜密侦查,公安机关梳理出一批违法犯罪线索,涉及全国24个省区市,并初步掌握了大量犯罪事实和证据。在此基础上,公安部部署对从事涉电信网络诈骗APP技术开发的违法犯罪团伙开展集中抓捕行动,严打团伙,捣毁窝点,斩断链条,切实从源头上遏制电信网络诈骗犯罪的高发态势。
据了解,诈骗团伙利用虚假APP实施的电信网络诈骗案件占该类案件约60%,虚假APP已成为整个犯罪链条中不可或缺的关键环节,并由此催生出一个庞大的技术开发灰色产业链,大量违法犯罪人员参与其中。
相关技术开发人员分工明确、团伙作案,围绕电信网络诈骗犯罪团伙的具体需求“量身定制”APP各种诈骗功能,有的负责编写程序代码,有的负责购买域名和租用服务器,有的负责APP的封装和分发。经各环节层层运作,最终将虚假APP贩卖给诈骗团伙。 随后,诈骗团伙根据其不法目的和APP功能特点,将其包装成极具迷惑性的“正规”应用平台,诱骗受害人点击链接或扫描二维码下载APP,进而实施诈骗。
公安部有关负责人表示,打击涉电信网络诈骗APP技术开发违法犯罪活动,是全链条打击电信网络诈骗黑灰产业的重要举措。公安机关将继续保持对此类犯罪的严打高压态势,坚决斩断为电信诈骗等违法犯罪提供技术、资金等各类非法服务的犯罪链条,切实维护人民群众财产安全和合法权益。 同时,公安机关提醒广大群众增强防范意识,对于未知来源的APP不下载、不点击、不扫码,切实守护好自己的钱袋子。
此文章转载自央视新闻,如有不当联系删除。原文链接
收起阅读 »
建议权限配置像apicloud一样可视化选择,因为华为个操蛋的东西让填说明,得看半天,然后万一不对又要从新的打包看权限,导致资源浪费
建议权限配置像apicloud一样可视化选择,因为华为个操蛋的东西让填说明,得看半天,然后万一不对又要从新的打包看权限,导致资源浪费
建议权限配置像apicloud一样可视化选择,因为华为个操蛋的东西让填说明,得看半天,然后万一不对又要从新的打包看权限,导致资源浪费

云端打包 提示没有剩余空间
Caused by: com.android.tools.r8.utils.AbortException: Error: 安装包制作目录/app/build/intermediates/transforms/dexBuilder/release/59.jar, No space left on device
at com.android.tools.r8.utils.Reporter.failIfPendingErrors(Reporter.java:116)
at com.android.tools.r8.dex.ApplicationWriter.write(ApplicationWriter.java:258)
at com.android.tools.r8.D8.run(D8.java:179)
at com.android.tools.r8.D8.lambda$run$1(D8.java:93)
at com.android.tools.r8.utils.ExceptionUtils.withCompilationHandler(ExceptionUtils.java:55)
Caused by: com.android.tools.r8.utils.AbortException: Error: 安装包制作目录/app/build/intermediates/transforms/dexBuilder/release/59.jar, No space left on device
at com.android.tools.r8.utils.Reporter.failIfPendingErrors(Reporter.java:116)
at com.android.tools.r8.dex.ApplicationWriter.write(ApplicationWriter.java:258)
at com.android.tools.r8.D8.run(D8.java:179)
at com.android.tools.r8.D8.lambda$run$1(D8.java:93)
at com.android.tools.r8.utils.ExceptionUtils.withCompilationHandler(ExceptionUtils.java:55)

IOS 微信分享 -95的报错
var b = new plus.nativeObj.Bitmap('bitblmap');
b.loadBase64Data(that.sImg, function() {
var fileName = '_doc/' + randomString(10) + '.jpg';
b.save(fileName, {
overwrite: true
}, function(object) {
plus.gallery.save(fileName, function(obj) {
** vue.shareImgSrc = obj.file || obj.path;**
// mui.toast('已保存到手机相册中');
vue.showTips = false;
mui('#sheet1').popover('toggle');
shareImage()
}, function() {
mui.toast('save fail');
vue.showTips = false;
mui('#sheet1').popover('toggle');
});
}, function() {
mui.toast('fail');
vue.showTips = false;
mui('#sheet1').popover('toggle');
});
}, function() {
mui.toast('图片创建失败');
vue.showTips = false;
mui('#sheet1').popover('toggle');
});
主要是IOS保存图片后返回的字段为path,安卓下为file.设置正确后,微信好友和朋友圈均可以分享
var b = new plus.nativeObj.Bitmap('bitblmap');
b.loadBase64Data(that.sImg, function() {
var fileName = '_doc/' + randomString(10) + '.jpg';
b.save(fileName, {
overwrite: true
}, function(object) {
plus.gallery.save(fileName, function(obj) {
** vue.shareImgSrc = obj.file || obj.path;**
// mui.toast('已保存到手机相册中');
vue.showTips = false;
mui('#sheet1').popover('toggle');
shareImage()
}, function() {
mui.toast('save fail');
vue.showTips = false;
mui('#sheet1').popover('toggle');
});
}, function() {
mui.toast('fail');
vue.showTips = false;
mui('#sheet1').popover('toggle');
});
}, function() {
mui.toast('图片创建失败');
vue.showTips = false;
mui('#sheet1').popover('toggle');
});
主要是IOS保存图片后返回的字段为path,安卓下为file.设置正确后,微信好友和朋友圈均可以分享
收起阅读 »
android手机wifi调试详细教程(非root)
看了官方给的教程,有几点说的不是太详细,结合百度的一些内容,最后总算成功了。
为了让大家少走弯路,在此记录下详细的步骤:
- 电脑和手机需要连接同一个wifi
- 手机与电脑用USB数据线连接(注意手机是开发者模式)
- 进入到HbuliderX的安装目录: HBuilderX\plugins\launcher\tools\adbs后SHIFT+鼠标右击,在此处打开命令窗口执行 adb tcpip 5555
- 执行成功后提示:restarting in TCP mode port: 5555
- 断开USB数据线连接,执行 adb connect 192.168.13.104:5555(注意斜体加粗是你手机的ip)
- 手机的ip:手机连接的wifi,点开连接wifi的详细信息即可查到
看了官方给的教程,有几点说的不是太详细,结合百度的一些内容,最后总算成功了。
为了让大家少走弯路,在此记录下详细的步骤:
- 电脑和手机需要连接同一个wifi
- 手机与电脑用USB数据线连接(注意手机是开发者模式)
- 进入到HbuliderX的安装目录: HBuilderX\plugins\launcher\tools\adbs后SHIFT+鼠标右击,在此处打开命令窗口执行 adb tcpip 5555
- 执行成功后提示:restarting in TCP mode port: 5555
- 断开USB数据线连接,执行 adb connect 192.168.13.104:5555(注意斜体加粗是你手机的ip)
- 手机的ip:手机连接的wifi,点开连接wifi的详细信息即可查到

Webview历史记录返回
开发中遇到可能打开一个webview内会进行多次页面跳转的问题
1.监听onBackPress事件
onBackPress(e) {
if (e.from === 'navigateBack') {
return;
}
this.checkcanBack();
return true;
},
2.同步等待判断此处是否webview可返回,如果没有历史记录,关闭当前页面
async checkcanBack() {
let ret = await this.canBack();
if (ret) {
//wv为创建的webview对象,全局变量保存
wv.back();
} else {
uni.navigateBack()
}
},
canBack() {
return new Promise((resolve, reject) => {
//wv为创建的webview对象,全局变量保存
wv.canBack(function(e) {
if (e.canBack) {
resolve(true); //后退到上次加载的页面
} else {
resolve(false);
}
});
});
},
开发中遇到可能打开一个webview内会进行多次页面跳转的问题
1.监听onBackPress事件
onBackPress(e) {
if (e.from === 'navigateBack') {
return;
}
this.checkcanBack();
return true;
},
2.同步等待判断此处是否webview可返回,如果没有历史记录,关闭当前页面
async checkcanBack() {
let ret = await this.canBack();
if (ret) {
//wv为创建的webview对象,全局变量保存
wv.back();
} else {
uni.navigateBack()
}
},
canBack() {
return new Promise((resolve, reject) => {
//wv为创建的webview对象,全局变量保存
wv.canBack(function(e) {
if (e.canBack) {
resolve(true); //后退到上次加载的页面
} else {
resolve(false);
}
});
});
},

元旦特惠➕提成 专业开发--手机App、公众号、小程序、电商平台、购物商城、分销系统、企业官网、管理系统、等程序-
元旦特惠➕提成
专业开发--手机App、公众号、小程序、电商平台、购物商城、分销系统、企业官网、管理系统、等程序--无论你是公司、团队、个人、手中有不做,或者感觉自己做不挣钱的项目可以转包给我们,如有幸签订合同后,我们公司会拿出项目总金额的10%作为提成(10w提成1w,1w提成1000元,1000元提成100)不管大小单子,推荐给我们就有提成。VX: web9688 诚信经营
元旦特惠➕提成
专业开发--手机App、公众号、小程序、电商平台、购物商城、分销系统、企业官网、管理系统、等程序--无论你是公司、团队、个人、手中有不做,或者感觉自己做不挣钱的项目可以转包给我们,如有幸签订合同后,我们公司会拿出项目总金额的10%作为提成(10w提成1w,1w提成1000元,1000元提成100)不管大小单子,推荐给我们就有提成。VX: web9688 诚信经营