森蓝小程序

This commit is contained in:
2025-04-23 19:50:30 +08:00
parent 7776447ce3
commit 5205361ac0
111 changed files with 20504 additions and 0 deletions

16
.hbuilderx/launch.json Normal file
View File

@@ -0,0 +1,16 @@
{ // launch.json 配置了启动调试时相关设置configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtype项可配置值为local或remote, local代表前端连本地云函数remote代表前端连云端云函数
"version": "0.0",
"configurations": [{
"default" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

20
App.vue Normal file
View File

@@ -0,0 +1,20 @@
<script>
export default {
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
uni-page-body,html,body{
height: 100%;
}
</style>

View File

@@ -0,0 +1,158 @@
<template>
<view class="tabBar">
<view class="cont">
<view class="image-text_1 flex-col" @tap="switchTab(list[0].pagePath)">
<image
class="thumbnail_1"
referrerpolicy="no-referrer"
:src="selected == 0? list[0].selectedIconPath:list[0].iconPath"
/>
<text :class="selected == 0?'text-group_1':'text-group_2'">
{{list[0].text}}
</text>
</view>
<!-- <image
class="label_1"
referrerpolicy="no-referrer"
src="https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/icon_camera%403x.png"
@tap="jumpVlog"
/> -->
<view class="image-text_2 flex-col" @tap="switchTab(list[1].pagePath)">
<image
class="thumbnail_2"
referrerpolicy="no-referrer"
:src="selected == 1? list[1].selectedIconPath:list[1].iconPath"
/>
<text :class="selected == 1?'text-group_3':'text-group_4'">
{{list[1].text}}
</text>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
selected: Number
},
data() {
return {
list: [
{
pagePath: "/pages/home/home",
text: "首页",
iconPath: "/static/icon/icon_Home_n@3x_da.png",
selectedIconPath: "/static/icon/icon_Home_n@3x_a.png",
},
{
pagePath: "/pages/mine/mine",
text: "我的",
iconPath: "/static/icon/icon_我的_n@3x_da.png",
selectedIconPath: "/static/icon/icon_我的_n@3x_a.png",
}
]
}
},
methods: {
switchTab(url) {
uni.switchTab({
url
})
}
}
}
</script>
<style lang="scss">
.tabBar {
z-index: 100;
width: 100%;
position: fixed;
bottom: 0;
font-size: 28rpx;
background-color: #fff;
color: #636363;
left: 0;
height: 150rpx;
background: none;
}
.cont {
z-index: 0;
height: 130rpx;
margin-top: 30rpx;
display: flex;
justify-content: space-around;
.image-text_1 {
margin-top: 5px;
margin-right: 50px;
}
.thumbnail_1 {
width: 18px;
height: 18px;
align-self: center;
}
.text-group_1 {
overflow-wrap: break-word;
color: rgba(22, 140, 248, 1);
font-size: 12px;
font-weight: normal;
text-align: center;
white-space: nowrap;
line-height: 12px;
margin-top: 9px;
}
.text-group_2 {
overflow-wrap: break-word;
color: rgba(80, 92, 115, 1);
font-size: 12px;
font-weight: normal;
text-align: center;
white-space: nowrap;
line-height: 12px;
margin-top: 9px;
}
.label_1 {
width: 43px;
height: 42px;
margin-bottom: 2px;
}
.image-text_2 {
margin-top: 5px;
margin-left: 50px;
}
.thumbnail_2 {
width: 18px;
height: 18px;
align-self: center;
}
.text-group_3 {
overflow-wrap: break-word;
color: rgba(22, 140, 248, 1);
font-size: 12px;
font-weight: normal;
text-align: center;
white-space: nowrap;
line-height: 12px;
margin-top: 9px;
}
.text-group_4 {
overflow-wrap: break-word;
color: rgba(80, 92, 115, 1);
font-size: 12px;
font-weight: normal;
text-align: center;
white-space: nowrap;
line-height: 12px;
margin-top: 9px;
}
}
</style>

View File

@@ -0,0 +1,188 @@
<template>
<view class="compress" v-if="canvasId">
<canvas :canvas-id="canvasId" :style="{ width: canvasSize.width,height: canvasSize.height}"></canvas>
</view>
</template>
<script>
export default {
data() {
return {
pic:'',
canvasSize: {
width: 0,
height: 0
},
canvasId:""
}
},
mounted() {
if(!uni || !uni._helang_compress_canvas){
uni._helang_compress_canvas = 1;
}else{
uni._helang_compress_canvas++;
}
this.canvasId = `compress-canvas${uni._helang_compress_canvas}`;
},
methods: {
// 压缩
compressFun(params) {
return new Promise(async (resolve, reject) => {
// 等待图片信息
let info = await this.getImageInfo(params.src).then(info=>info).catch(()=>null);
if(!info){
reject('获取图片信息异常');
return;
}
// 设置最大 & 最小 尺寸
const maxSize = params.maxSize || 1080;
const minSize = params.minSize || 540;
// 当前图片尺寸
let {width,height} = info;
let oldWidth = width;
// 最小尺寸校验
if(width == maxSize && height == minSize){
resolve(params.src);
return;
}
//新尺寸
width = maxSize;
height = minSize;
// 设置画布尺寸
this.$set(this,"canvasSize",{
width: `${width}px`,
height: `${height}px`
});
// Vue.nextTick 回调在 App 有异常,则使用 setTimeout 等待DOM更新
setTimeout(() => {
const ctx = uni.createCanvasContext(this.canvasId, this);
ctx.clearRect(0,0,width, height)
ctx.drawImage(info.path, 0, 0, width, height);
ctx.draw(false, () => {
uni.canvasToTempFilePath({
x: 0,
y: 0,
width: width,
height: height,
destWidth: width,
destHeight: height,
canvasId: this.canvasId,
fileType: params.fileType || 'png',
quality: params.quality || 0.9,
success: (res) => {
resolve(res.tempFilePath);
},
fail:(err)=>{
reject(null);
}
},this);
});
}, 300);
});
},
// 获取图片信息
getImageInfo(src){
return new Promise((resolve, reject)=>{
uni.getImageInfo({
src,
success: (info)=> {
resolve(info);
},
fail: () => {
reject(null);
}
});
});
},
// 批量压缩
compress(params){
// index:进度done:成功fail:失败
let [index,done,fail] = [0,0,0];
// 压缩完成的路径集合
let paths = [];
// 待压缩的图片
let waitList = [];
if(typeof params.src == 'string'){
waitList = [params.src];
}else{
waitList = params.src;
}
// 批量压缩方法
let batch = ()=>{
return new Promise((resolve, reject)=>{
// 开始
let start = async ()=>{
// 等待图片压缩方法返回
let path = await next().catch(()=>null);
if(path){
done++;
paths.push(path);
}else{
fail++;
}
params.progress && params.progress({
done,
fail,
count:waitList.length
});
index++;
// 压缩完成
if(index >= waitList.length){
resolve(true);
}else{
start();
}
}
start();
});
}
// 依次调用压缩方法
let next = ()=>{
return this.compressFun({
src:waitList[index],
maxSize:params.maxSize,
fileType:params.fileType,
quality:params.quality,
minSize:params.minSize
})
}
// 全部压缩完成后调用
return new Promise(async (resolve, reject)=>{
// 批量压缩方法回调
let res = await batch();
if(res){
if(typeof params.src == 'string'){
resolve(paths[0]);
}else{
resolve(paths);
}
}else{
reject(null);
}
});
}
}
}
</script>
<style lang="scss" scoped>
.compress{
position: fixed;
width: 12px;
height: 12px;
overflow: hidden;
top: -99999px;
left: 0;
}
</style>

View File

@@ -0,0 +1,249 @@
<template>
<view class="filter-wrapper" :style="{ height: height + 'rpx', top: top,'border-top':border?'1rpx solid #f2f2f2':'none' }" @touchmove.stop.prevent="discard">
<view class="inner-wrapper">
<view class="mask" :class="showMask ? 'show' : 'hide'" @tap="tapMask"></view>
<view class="navs">
<view class="c-flex-align" :class="{ 'c-flex-center': index > 0, actNav: index === actNav }" v-for="(item, index) in navData" :key="index" @click="navClick(index)">
<view v-for="(child, childx) in item" :key="childx" v-if="child.select">{{ child.text }}</view>
<image src="https://i.loli.net/2020/07/15/QsHxlr1gbSImvWt.png" mode="" class="icon-triangle" v-if="index === actNav"></image>
<image src="https://i.loli.net/2020/07/15/xjVSvzWcH9NO7al.png" mode="" class="icon-triangle" v-else></image>
</view>
<!-- <view class="date-wrapper">
<picker mode="date" @change="handleDate">
<view class="date c-flex-align" :style="{ height: height + 'rpx' }" @click="dateClick">
<view>{{ selDate }}</view>
<image src="https://i.loli.net/2020/07/15/xjVSvzWcH9NO7al.png" mode="" class="icon-triangle"></image>
</view>
</picker>
</view> -->
</view>
<scroll-view scroll-y="true" class="popup" :class="popupShow ? 'popupShow' : ''">
<view class="item-opt c-flex-align" :class="item.select ? 'actOpt' : ''" v-for="(item, index) in navData[actNav]" :key="index" @click="handleOpt(index)">
{{ item.text }}
</view>
</scroll-view>
</view>
</view>
</template>
<script>
// import { getCurDateTime } from '@/libs/utils.js';
export default {
props: {
height: {
type: Number,
default: 108
},
top: {
type: String,
default: 'calc(var(--window-statsu-bar) + 44px)'
},
border: {
type: Boolean,
default: false
},
filterData: {
//必填
type: Array,
default: () => {
return [];
}
// default: () => {
// return [
// [{ text: '全部状态', value: '' }, { text: '状态1', value: 1 }, { text: '状态2', value: 2 }, { text: '状态3', value: 3 }],
// [{ text: '全部类型', value: '' }, { text: '类型1', value: 1 }, { text: '类型2', value: 2 }, { text: '类型3', value: 3 }]
// ];
// }
},
defaultIndex: {
//默认选中条件索引,超出一类时必填
type: Array,
default: () => {
return [0];
}
}
},
data() {
return {
navData: [],
popupShow: false,
showMask: false,
actNav: null,
selDate: '选择日期',
selIndex: [] //选中条件索引
};
},
created() {
this.navData = this.filterData;
this.selIndex = this.defaultIndex;
this.keepStatus();
},
mounted() {
// this.selDate = getCurDateTime().formatDate;
},
methods: {
updateFilterData(data) {
this.navData = data;
this.keepStatus();
},
keepStatus() {
this.navData.forEach(itemnavData => {
itemnavData.map(child => {
child.select = false;
});
return itemnavData;
});
for (let i = 0; i < this.selIndex.length; i++) {
let selindex = this.selIndex[i];
this.navData[i][selindex].select = true;
}
},
navClick(index) {
if (index === this.actNav) {
this.tapMask();
return;
}
this.popupShow = true;
this.showMask = true;
this.actNav = index;
},
handleOpt(index) {
this.selIndex[this.actNav] = index;
this.keepStatus();
setTimeout(() => {
this.tapMask();
}, 100);
let data = [];
let res = this.navData.forEach(item => {
let sel = item.filter(child => child.select);
data.push(sel);
});
// console.log(data);
this.$emit('onSelected', data);
},
dateClick() {
this.tapMask();
},
tapMask() {
this.showMask = false;
this.popupShow = false;
this.actNav = null;
},
handleDate(e) {
let d = e.detail.value;
this.selDate = d;
this.$emit('dateChange', d);
},
discard() {}
}
};
</script>
<style lang="scss" scoped>
page {
font-size: 28rpx;
}
.c-flex-align {
display: flex;
align-items: center;
}
.c-flex-center {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.filter-wrapper {
position: fixed;
left: 0;
width: 750rpx;
z-index: 999;
.inner-wrapper {
// position: relative;
.navs {
position: relative;
height: 90rpx;
padding: 0 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
background-color: rgba(255, 255, 255, 0.6);
border-bottom: 2rpx solid #f5f6f9;
color: #8b9aae;
z-index: 999;
box-sizing: border-box;
& > view {
flex: 1;
height: 100%;
flex-direction: row;
z-index: 999;
}
.date {
justify-content: flex-end;
}
.actNav {
color: #4d7df9;
font-weight: bold;
}
}
.mask {
z-index: 666;
position: fixed;
// top: calc(var(--status-bar-height) + 44px);
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0);
transition: background-color 0.15s linear;
&.show {
background-color: rgba(0, 0, 0, 0.4);
}
&.hide {
display: none;
}
}
.popup {
position: relative;
max-height: 700rpx;
background-color: #fff;
border-bottom-left-radius: 20rpx;
border-bottom-right-radius: 20rpx;
overflow: scroll;
z-index: 999;
transition: all 1s linear;
opacity: 0;
display: none;
.item-opt {
height: 100rpx;
padding: 0 40rpx;
color: #8b9aae;
border-bottom: 2rpx solid #f5f6f9;
}
.actOpt {
color: #4d7df9;
font-weight: bold;
position: relative;
&::after {
content: '✓';
font-weight: bold;
font-size: 36rpx;
position: absolute;
right: 40rpx;
}
}
}
.popupShow {
display: block;
opacity: 1;
}
}
.icon-triangle {
width: 16rpx;
height: 16rpx;
margin-left: 10rpx;
}
}
</style>

View File

@@ -0,0 +1,627 @@
<template>
<view class="cropper" id="cropper" :class="{ show: show }">
<view class="cropper-head">
<!-- <view class="cropper-btn cropper-reset" @tap="resetCrop">重做</view> -->
</view>
<view class="cropper-body">
<image id="image" class="cropper-image" :src="imagePath" mode="aspectFit"></image>
<view :style="{ width: stageWidth + 'px', height: stageHeight + 'px', left: stageLeft + 'px', top: stageTop + 'px' }" class="cropper-stage" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="touchMove">
<view id="box" class="cropper-box" :style="{ width: boxWidth + 'px', height: boxHeight + 'px', left: boxLeft + 'px', top: boxTop + 'px' }">
<view id="lt" class="lt"></view>
<view id="lb" class="lb"></view>
<view id="rt" class="rt"></view>
<view id="rb" class="rb"></view>
<view class="line-v" style="left:33.3%;"></view>
<view class="line-v" style="left:66.6%;"></view>
<view class="line-h" style="top:33.3%;"></view>
<view class="line-h" style="top:66.6%;"></view>
</view>
</view>
<canvas class="cropper-canvas" canvas-id="canvas" :style="{ height: canvasHeight + 'px', width: canvasWidth + 'px' }"></canvas>
</view>
<view class="cropper-bottom">
<view class="cropper-btn cropper-cancel" @tap="cancelCrop">取消</view>
<view class="cropper-btn cropper-ok" @tap="completeCrop">裁剪</view>
</view>
</view>
</template>
<script>
//无须渲染的变量
let layoutLeft = 0;
let layoutTop = 0;
let layoutWidth = 0;
let layoutHeight = 0;
let stageLeft = 0;
let stageTop = 0;
let stageWidth = 0;
let stageHeight = 0;
let imageWidth = 0;
let imageHeight = 0;
let pixelRatio = 1; //todo设备像素密度//暂不使用//
let imageStageRatio = 1; //图片实际尺寸与剪裁舞台大小的比值,用于尺寸换算。
let minBoxWidth = 0;
let minBoxHeight = 0;
let touchStartBoxLeft = 0;
let touchStartBoxTop = 0;
let touchStartBoxWidth = 0;
let touchStartBoxHeight = 0;
let touchStartX = 0;
let touchStartY = 0;
export default {
name: 'cropper',
props: {
quality: {
type: Number,
default: 1
},
//目标文件的类型。默认值为jpgjpg输出jpg格式图片png输出png格式图片
outputFileType: {
type: String,
default: 'jpg'
},
//目标图片的宽高比默认null即不限制剪裁宽高比。aspectRatio需大于0
aspectRatio: {
type: [Number, null],
default: null
},
//最小剪裁尺寸与原图尺寸的比率默认0.15即宽度最小剪裁到原图的0.15宽。
minBoxWidthRatio: {
type: Number,
default: 0.15
},
//同minBoxWidthRatio当设置aspectRatio时minBoxHeight值设置无效。minBoxHeight值由minBoxWidth 和 aspectRatio自动计算得到。
minBoxHeightRatio: {
type: Number,
default: 0.15
},
//剪裁框初始大小比率。默认值0.8即剪裁框默认宽度为图片宽度的0.8倍。
initialBoxWidthRatio: {
type: Number,
default: 0.8
},
//同initialBoxWidthRatio当设置aspectRatio时initialBoxHeightRatio值设置无效。initialBoxHeightRatio值由initialBoxWidthRatio 和 aspectRatio自动计算得到。
initialBoxHeightRatio: {
type: Number,
default: 0.8
}
},
data() {
return {
//data
stageLeft: 0,
stageTop: 0,
stageWidth: 0,
stageHeight: 0,
boxWidth: 0,
boxHeight: 0,
boxLeft: 0,
boxTop: 0,
canvasWidth: 0,
canvasHeight: 0,
show: false,
imagePath: ''
};
},
mounted() {
// setTimeout(() => {
// this.init();
// }, 150);
},
methods: {
resetCrop() {
this.$emit('reset');
this.init(this.imagePath);
},
cancelCrop() {
this.$emit('cancel');
},
completeCrop() {
uni.showLoading({
mask: true,
title: '图片处理中'
});
let imagePath = this.imagePath;
let canvasContext = uni.createCanvasContext('canvas', this);
let boxLeft = this.boxLeft;
let boxTop = this.boxTop;
let boxWidth = this.boxWidth;
let boxHeight = this.boxHeight;
let sx = Math.ceil(boxLeft * imageStageRatio);
let sy = Math.ceil(boxTop * imageStageRatio);
let sWidth = Math.ceil(boxWidth * imageStageRatio);
let sHeight = Math.ceil(boxHeight * imageStageRatio);
let dx = 0;
let dy = 0;
let dWidth = Math.ceil(sWidth * pixelRatio);
let dHeight = Math.ceil(sHeight * pixelRatio);
const param = {
x: sx,
y: sy,
width: dWidth,
height: dHeight,
rotate: 0,
scaleX: 1,
scaleY: 1
};
canvasContext.drawImage(imagePath, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
canvasContext.draw(false, () => {
uni.canvasToTempFilePath(
{
x: dx,
y: dy,
width: dWidth,
height: dHeight,
destWidth: sWidth,
destHeight: sHeight,
canvasId: 'canvas',
fileType: this.outputFileType,
quality: this.quality,
success: res => {
this.$emit('complete', { param, path: res.tempFilePath,source:this.imagePath });
}
},
this
);
});
},
touchMove(e) {
let targetId = e.target.id;
let touch = e.touches[0];
let pageX = touch.pageX;
let pageY = touch.pageY;
let offsetX = pageX - touchStartX;
let offsetY = pageY - touchStartY;
if (targetId == 'box') {
let newBoxLeft = touchStartBoxLeft + offsetX;
let newBoxTop = touchStartBoxTop + offsetY;
if (newBoxLeft < 0) {
newBoxLeft = 0;
}
if (newBoxTop < 0) {
newBoxTop = 0;
}
if (newBoxLeft + touchStartBoxWidth > stageWidth) {
newBoxLeft = stageWidth - touchStartBoxWidth;
}
if (newBoxTop + touchStartBoxHeight > stageHeight) {
newBoxTop = stageHeight - touchStartBoxHeight;
}
this.boxLeft = newBoxLeft;
this.boxTop = newBoxTop;
} else if (targetId == 'lt') {
if (this.aspectRatio) {
offsetY = offsetX / this.aspectRatio;
}
let newBoxLeft = touchStartBoxLeft + offsetX;
let newBoxTop = touchStartBoxTop + offsetY;
if (newBoxLeft < 0) {
newBoxLeft = 0;
}
if (newBoxTop < 0) {
newBoxTop = 0;
}
if (touchStartBoxLeft + touchStartBoxWidth - newBoxLeft < minBoxWidth) {
newBoxLeft = touchStartBoxLeft + touchStartBoxWidth - minBoxWidth;
}
if (touchStartBoxTop + touchStartBoxHeight - newBoxTop < minBoxHeight) {
newBoxTop = touchStartBoxTop + touchStartBoxHeight - minBoxHeight;
}
let newBoxWidth = touchStartBoxWidth - (newBoxLeft - touchStartBoxLeft);
let newBoxHeight = touchStartBoxHeight - (newBoxTop - touchStartBoxTop);
//约束比例
if (newBoxTop == 0 && this.aspectRatio && newBoxLeft != 0) {
newBoxWidth = newBoxHeight * this.aspectRatio;
newBoxLeft = touchStartBoxWidth - newBoxWidth + touchStartBoxLeft;
}
if (newBoxLeft == 0 && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
}
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
}
this.boxTop = newBoxTop;
this.boxLeft = newBoxLeft;
this.boxWidth = newBoxWidth;
this.boxHeight = newBoxHeight;
} else if (targetId == 'rt') {
if (this.aspectRatio) {
offsetY = -offsetX / this.aspectRatio;
}
let newBoxWidth = touchStartBoxWidth + offsetX;
if (newBoxWidth < minBoxWidth) {
newBoxWidth = minBoxWidth;
}
if (touchStartBoxLeft + newBoxWidth > stageWidth) {
newBoxWidth = stageWidth - touchStartBoxLeft;
}
let newBoxTop = touchStartBoxTop + offsetY;
if (newBoxTop < 0) {
newBoxTop = 0;
}
if (touchStartBoxTop + touchStartBoxHeight - newBoxTop < minBoxHeight) {
newBoxTop = touchStartBoxTop + touchStartBoxHeight - minBoxHeight;
}
let newBoxHeight = touchStartBoxHeight - (newBoxTop - touchStartBoxTop);
//约束比例
if (newBoxTop == 0 && this.aspectRatio && newBoxWidth != stageWidth - touchStartBoxLeft) {
newBoxWidth = newBoxHeight * this.aspectRatio;
}
if (newBoxWidth == stageWidth - touchStartBoxLeft && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
}
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
}
this.boxTop = newBoxTop;
this.boxHeight = newBoxHeight;
this.boxWidth = newBoxWidth;
} else if (targetId == 'lb') {
if (this.aspectRatio) {
offsetY = -offsetX / this.aspectRatio;
}
let newBoxLeft = touchStartBoxLeft + offsetX;
if (newBoxLeft < 0) {
newBoxLeft = 0;
}
if (touchStartBoxLeft + touchStartBoxWidth - newBoxLeft < minBoxWidth) {
newBoxLeft = touchStartBoxLeft + touchStartBoxWidth - minBoxWidth;
}
let newBoxWidth = touchStartBoxWidth - (newBoxLeft - touchStartBoxLeft);
let newBoxHeight = touchStartBoxHeight + offsetY;
if (newBoxHeight < minBoxHeight) {
newBoxHeight = minBoxHeight;
}
if (touchStartBoxTop + newBoxHeight > stageHeight) {
newBoxHeight = stageHeight - touchStartBoxTop;
}
//约束比例
if (newBoxHeight == stageHeight - touchStartBoxTop && this.aspectRatio && newBoxLeft != 0) {
newBoxWidth = newBoxHeight * this.aspectRatio;
newBoxLeft = touchStartBoxWidth - newBoxWidth + touchStartBoxLeft;
}
if (newBoxLeft == 0 && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
}
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
}
this.boxLeft = newBoxLeft;
this.boxWidth = newBoxWidth;
this.boxHeight = newBoxHeight;
} else if (targetId == 'rb') {
if (this.aspectRatio) {
offsetY = offsetX / this.aspectRatio;
}
let newBoxWidth = touchStartBoxWidth + offsetX;
if (newBoxWidth < minBoxWidth) {
newBoxWidth = minBoxWidth;
}
if (touchStartBoxLeft + newBoxWidth > stageWidth) {
newBoxWidth = stageWidth - touchStartBoxLeft;
}
let newBoxHeight = touchStartBoxHeight + offsetY;
if (newBoxHeight < minBoxHeight) {
newBoxHeight = minBoxHeight;
}
if (touchStartBoxTop + newBoxHeight > stageHeight) {
newBoxHeight = stageHeight - touchStartBoxTop;
}
//约束比例
if (newBoxHeight == stageHeight - touchStartBoxTop && this.aspectRatio && newBoxWidth != stageWidth - touchStartBoxLeft) {
newBoxWidth = newBoxHeight * this.aspectRatio;
}
if (newBoxWidth == stageWidth - touchStartBoxLeft && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
}
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
}
this.boxWidth = newBoxWidth;
this.boxHeight = newBoxHeight;
}
},
touchStart(e) {
let touch = e.touches[0];
let pageX = touch.pageX;
let pageY = touch.pageY;
touchStartX = pageX;
touchStartY = pageY;
touchStartBoxLeft = this.boxLeft;
touchStartBoxTop = this.boxTop;
touchStartBoxWidth = this.boxWidth;
touchStartBoxHeight = this.boxHeight;
},
close(force=true) {
this.show = false;
if(force){
this.imagePath = ''
}
},
init(src) {
if (!src) {
return '';
}
this.imagePath = src;
uni.showLoading({
mask: true,
title: '载入图片中'
});
uni.createSelectorQuery()
.in(this)
.select('.cropper-body')
.boundingClientRect(rect => {
layoutLeft = rect.left;
layoutTop = rect.top;
layoutWidth = rect.width;
layoutHeight = rect.height;
uni.getImageInfo({
src: this.imagePath,
success: imageInfo => {
imageWidth = imageInfo.width;
imageHeight = imageInfo.height;
let imageWH = imageWidth / imageHeight;
let layoutWH = layoutWidth / layoutHeight;
if (imageWH >= layoutWH) {
stageWidth = layoutWidth;
stageHeight = stageWidth / imageWH;
imageStageRatio = imageHeight / stageHeight;
} else {
stageHeight = layoutHeight;
stageWidth = layoutHeight * imageWH;
imageStageRatio = imageWidth / stageWidth;
}
stageLeft = (layoutWidth - stageWidth) / 2;
stageTop = (layoutHeight - stageHeight) / 2;
minBoxWidth = stageWidth * this.minBoxWidthRatio;
minBoxHeight = stageHeight * this.minBoxHeightRatio;
let boxWidth = stageWidth * this.initialBoxWidthRatio;
let boxHeight = stageHeight * this.initialBoxHeightRatio;
if (this.aspectRatio) {
boxHeight = boxWidth / this.aspectRatio;
}
if (boxHeight > stageHeight) {
boxHeight = stageHeight;
boxWidth = boxHeight * this.aspectRatio;
}
let boxLeft = (stageWidth - boxWidth) / 2;
let boxTop = (stageHeight - boxHeight) / 2;
this.canvasWidth = imageWidth * pixelRatio;
this.canvasHeight = imageHeight * pixelRatio;
this.stageLeft = stageLeft;
this.stageTop = stageTop;
this.stageWidth = stageWidth;
this.stageHeight = stageHeight;
this.boxWidth = boxWidth;
this.boxHeight = boxHeight;
this.boxLeft = boxLeft;
this.boxTop = boxTop;
setTimeout(() => {
uni.hideLoading();
this.show = true;
}, 100);
},
fail: () => {
uni.showToast({
icon: 'none',
title: '图片载入失败'
});
}
});
})
.exec();
}
}
};
</script>
<style lang="scss">
.cropper {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #000;
z-index: -1000000;
opacity: 0;
&.show {
z-index: 999;
opacity: 1;
}
.cropper-head {
position: fixed;
top: 0;
width: 750rpx;
z-index: 6;
height: calc(var(--status-bar-height) + 88rpx);
padding-top: var(--status-bar-height);
display: flex;
justify-content: flex-end;
align-items: center;
}
.cropper-btn {
height: 64rpx;
margin: 0 20rpx;
padding: 0 30rpx;
line-height: 64rpx;
color: #fff;
font-size: 26rpx;
}
.cropper-body {
margin: calc(var(--status-bar-height) + 88rpx) 30rpx 0 30rpx;
height: calc(100vh - var(--status-bar-height) - 88rpx - 100rpx - var(--safe-area-inset-bottom));
position: relative;
}
.cropper-bottom {
height: calc(var(--safe-area-inset-bottom) + 100rpx);
padding-top: var(--safe-area-inset-bottom);
display: flex;
align-items: center;
justify-content: space-between;
position: fixed;
z-index: 6;
width: 750rpx;
bottom: 0;
}
.cropper-ok {
color: #39f;
}
.cropper-image {
position: absolute;
width: 100%;
height: 100%;
}
.cropper-stage {
position: absolute;
.cropper-box {
position: absolute;
border: 4rpx solid #ddd;
box-sizing: border-box;
box-shadow: 0 0 0 2000rpx rgba(0, 0, 0, 0.5);
.lt {
position: absolute;
height: 48rpx;
width: 48rpx;
left: -6rpx;
top: -6rpx;
border-left: 12rpx solid #ffffff;
border-top: 12rpx solid #ffffff;
}
.lb {
position: absolute;
height: 48rpx;
width: 48rpx;
left: -6rpx;
bottom: -6rpx;
border-left: 12rpx solid #ffffff;
border-bottom: 12rpx solid #ffffff;
}
.rt {
position: absolute;
height: 48rpx;
width: 48rpx;
right: -6rpx;
top: -6rpx;
border-right: 12rpx solid #ffffff;
border-top: 12rpx solid #ffffff;
}
.rb {
position: absolute;
height: 48rpx;
width: 48rpx;
right: -6rpx;
bottom: -6rpx;
border-right: 12rpx solid #ffffff;
border-bottom: 12rpx solid #ffffff;
}
.line-v,
.line-h {
position: absolute;
opacity: 0.5;
}
.line-v {
width: 2rpx;
border-left: 2rpx dashed #fff;
height: 100%;
}
.line-h {
height: 2rpx;
border-bottom: 2rpx dashed #fff;
width: 100%;
}
}
}
.cropper-canvas {
position: fixed;
background-color: red;
left: 5000rpx;
}
}
// 安全域兼容样式
// page {
// --safe-area-inset-top: 0px;
// --safe-area-inset-right: 0px;
// --safe-area-inset-bottom: 0px;
// --safe-area-inset-left: 0px;
// @supports (top: constant(safe-area-inset-top)) {
// --safe-area-inset-top: constant(safe-area-inset-top);
// --safe-area-inset-right: constant(safe-area-inset-right);
// --safe-area-inset-bottom: constant(safe-area-inset-bottom);
// --safe-area-inset-left: constant(safe-area-inset-left);
// }
// @supports (top: env(safe-area-inset-top)) {
// --safe-area-inset-top: env(safe-area-inset-top);
// --safe-area-inset-right: env(safe-area-inset-right);
// //--safe-area-inset-bottom: 12px;
// --safe-area-inset-bottom: env(safe-area-inset-bottom);
// --safe-area-inset-left: env(safe-area-inset-left);
// }
// }
</style>

78
css/common.css Normal file
View File

@@ -0,0 +1,78 @@
body *,
page view {
box-sizing: border-box;
flex-shrink: 0;
}
body {
font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma,
Arial, PingFang SC-Light, Microsoft YaHei;
margin: 0;
}
button {
margin: 0;
padding: 0;
border: 1px solid transparent;
outline: none;
background-color: transparent;
}
button:active {
opacity: 0.6;
}
.flex-col {
display: flex;
flex-direction: column;
}
.flex-row {
display: flex;
flex-direction: row;
}
.justify-start {
display: flex;
justify-content: flex-start;
}
.justify-center {
display: flex;
justify-content: center;
}
.justify-end {
display: flex;
justify-content: flex-end;
}
.justify-evenly {
display: flex;
justify-content: space-evenly;
}
.justify-around {
display: flex;
justify-content: space-around;
}
.justify-between {
display: flex;
justify-content: space-between;
}
.align-start {
display: flex;
align-items: flex-start;
}
.align-center {
display: flex;
align-items: center;
}
.align-end {
display: flex;
align-items: flex-end;
}
.uni-bg-red {
background-color: #ff5a5f;
}
.uni-bg-green {
background-color: #09bb07;
}
.uni-bg-blue {
background-color: #007aff;
}

20
index.html Normal file
View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

29
main.js Normal file
View File

@@ -0,0 +1,29 @@
import App from './App'
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif
if( __wxConfig.envVersion == 'release' ) {
Vue.prototype.java_http_url = 'https://t-zhipai-server.humengfilms.com:8101'
Vue.prototype.pay_http_url = 'https://pay.humengfilms.com:9007'
} else {
Vue.prototype.java_http_url = 'https://s101.magichairai.com:9080'
Vue.prototype.pay_http_url = 'https://s101.magichairai.com:9475'
}

72
manifest.json Normal file
View File

@@ -0,0 +1,72 @@
{
"name" : "smart-camera",
"appid" : "__UNI__A50597F",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx9d720897d2a84948",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2"
}

12
node_modules/.package-lock.json generated vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "smart-camera",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/decimal.js": {
"version": "10.4.3",
"resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.4.3.tgz",
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
}
}
}

23
node_modules/decimal.js/LICENCE.md generated vendored Normal file
View File

@@ -0,0 +1,23 @@
The MIT Licence.
Copyright (c) 2022 Michael Mclaughlin
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

246
node_modules/decimal.js/README.md generated vendored Normal file
View File

@@ -0,0 +1,246 @@
![decimal.js](https://raw.githubusercontent.com/MikeMcl/decimal.js/gh-pages/decimaljs.png)
An arbitrary-precision Decimal type for JavaScript.
[![npm version](https://img.shields.io/npm/v/decimal.js.svg)](https://www.npmjs.com/package/decimal.js)
[![npm downloads](https://img.shields.io/npm/dw/decimal.js)](https://www.npmjs.com/package/decimal.js)
[![Build Status](https://travis-ci.org/MikeMcl/decimal.js.svg)](https://travis-ci.org/MikeMcl/decimal.js)
[![CDNJS](https://img.shields.io/cdnjs/v/decimal.js.svg)](https://cdnjs.com/libraries/decimal.js)
<br>
## Features
- Integers and floats
- Simple but full-featured API
- Replicates many of the methods of JavaScript's `Number.prototype` and `Math` objects
- Also handles hexadecimal, binary and octal values
- Faster, smaller, and perhaps easier to use than JavaScript versions of Java's BigDecimal
- No dependencies
- Wide platform compatibility: uses JavaScript 1.5 (ECMAScript 3) features only
- Comprehensive [documentation](https://mikemcl.github.io/decimal.js/) and test set
- Used under the hood by [math.js](https://github.com/josdejong/mathjs)
- Includes a TypeScript declaration file: *decimal.d.ts*
![API](https://raw.githubusercontent.com/MikeMcl/decimal.js/gh-pages/API.png)
The library is similar to [bignumber.js](https://github.com/MikeMcl/bignumber.js/), but here
precision is specified in terms of significant digits rather than decimal places, and all
calculations are rounded to the precision (similar to Python's decimal module) rather than just
those involving division.
This library also adds the trigonometric functions, among others, and supports non-integer powers,
which makes it a significantly larger library than *bignumber.js* and the even smaller
[big.js](https://github.com/MikeMcl/big.js/).
For a lighter version of this library without the trigonometric functions see
[decimal.js-light](https://github.com/MikeMcl/decimal.js-light/).
## Load
The library is the single JavaScript file *decimal.js* or ES module *decimal.mjs*.
Browser:
```html
<script src='path/to/decimal.js'></script>
<script type="module">
import Decimal from './path/to/decimal.mjs';
...
</script>
```
[Node.js](https://nodejs.org):
```bash
npm install decimal.js
```
```js
const Decimal = require('decimal.js');
import Decimal from 'decimal.js';
import {Decimal} from 'decimal.js';
```
## Use
*In all examples below, semicolons and `toString` calls are not shown.
If a commented-out value is in quotes it means `toString` has been called on the preceding expression.*
The library exports a single constructor function, `Decimal`, which expects a single argument that is a number, string or Decimal instance.
```js
x = new Decimal(123.4567)
y = new Decimal('123456.7e-3')
z = new Decimal(x)
x.equals(y) && y.equals(z) && x.equals(z) // true
```
If using values with more than a few digits, it is recommended to pass strings rather than numbers to avoid a potential loss of precision.
```js
// Precision loss from using numeric literals with more than 15 significant digits.
new Decimal(1.0000000000000001) // '1'
new Decimal(88259496234518.57) // '88259496234518.56'
new Decimal(99999999999999999999) // '100000000000000000000'
// Precision loss from using numeric literals outside the range of Number values.
new Decimal(2e+308) // 'Infinity'
new Decimal(1e-324) // '0'
// Precision loss from the unexpected result of arithmetic with Number values.
new Decimal(0.7 + 0.1) // '0.7999999999999999'
```
As with JavaScript numbers, strings can contain underscores as separators to improve readability.
```js
x = new Decimal('2_147_483_647')
```
String values in binary, hexadecimal or octal notation are also accepted if the appropriate prefix is included.
```js
x = new Decimal('0xff.f') // '255.9375'
y = new Decimal('0b10101100') // '172'
z = x.plus(y) // '427.9375'
z.toBinary() // '0b110101011.1111'
z.toBinary(13) // '0b1.101010111111p+8'
// Using binary exponential notation to create a Decimal with the value of `Number.MAX_VALUE`.
x = new Decimal('0b1.1111111111111111111111111111111111111111111111111111p+1023')
// '1.7976931348623157081e+308'
```
Decimal instances are immutable in the sense that they are not changed by their methods.
```js
0.3 - 0.1 // 0.19999999999999998
x = new Decimal(0.3)
x.minus(0.1) // '0.2'
x // '0.3'
```
The methods that return a Decimal can be chained.
```js
x.dividedBy(y).plus(z).times(9).floor()
x.times('1.23456780123456789e+9').plus(9876.5432321).dividedBy('4444562598.111772').ceil()
```
Many method names have a shorter alias.
```js
x.squareRoot().dividedBy(y).toPower(3).equals(x.sqrt().div(y).pow(3)) // true
x.comparedTo(y.modulo(z).negated() === x.cmp(y.mod(z).neg()) // true
```
Most of the methods of JavaScript's `Number.prototype` and `Math` objects are replicated.
```js
x = new Decimal(255.5)
x.toExponential(5) // '2.55500e+2'
x.toFixed(5) // '255.50000'
x.toPrecision(5) // '255.50'
Decimal.sqrt('6.98372465832e+9823') // '8.3568682281821340204e+4911'
Decimal.pow(2, 0.0979843) // '1.0702770511687781839'
// Using `toFixed()` to avoid exponential notation:
x = new Decimal('0.0000001')
x.toString() // '1e-7'
x.toFixed() // '0.0000001'
```
And there are `isNaN` and `isFinite` methods, as `NaN` and `Infinity` are valid `Decimal` values.
```js
x = new Decimal(NaN) // 'NaN'
y = new Decimal(Infinity) // 'Infinity'
x.isNaN() && !y.isNaN() && !x.isFinite() && !y.isFinite() // true
```
There is also a `toFraction` method with an optional *maximum denominator* argument.
```js
z = new Decimal(355)
pi = z.dividedBy(113) // '3.1415929204'
pi.toFraction() // [ '7853982301', '2500000000' ]
pi.toFraction(1000) // [ '355', '113' ]
```
All calculations are rounded according to the number of significant digits and rounding mode specified
by the `precision` and `rounding` properties of the Decimal constructor.
For advanced usage, multiple Decimal constructors can be created, each with their own independent
configuration which applies to all Decimal numbers created from it.
```js
// Set the precision and rounding of the default Decimal constructor
Decimal.set({ precision: 5, rounding: 4 })
// Create another Decimal constructor, optionally passing in a configuration object
Dec = Decimal.clone({ precision: 9, rounding: 1 })
x = new Decimal(5)
y = new Dec(5)
x.div(3) // '1.6667'
y.div(3) // '1.66666666'
```
The value of a Decimal is stored in a floating point format in terms of its digits, exponent and sign, but these properties should be considered read-only.
```js
x = new Decimal(-12345.67);
x.d // [ 12345, 6700000 ] digits (base 10000000)
x.e // 4 exponent (base 10)
x.s // -1 sign
```
For further information see the [API](http://mikemcl.github.io/decimal.js/) reference in the *doc* directory.
## Test
To run the tests using Node.js from the root directory:
```bash
npm test
```
Each separate test module can also be executed individually, for example:
```bash
node test/modules/toFraction
```
To run the tests in a browser, open *test/test.html*.
## Minify
Two minification examples:
Using [uglify-js](https://github.com/mishoo/UglifyJS) to minify the *decimal.js* file:
```bash
npm install uglify-js -g
uglifyjs decimal.js --source-map url=decimal.min.js.map -c -m -o decimal.min.js
```
Using [terser](https://github.com/terser/terser) to minify the ES module version, *decimal.mjs*:
```bash
npm install terser -g
terser decimal.mjs --source-map url=decimal.min.mjs.map -c -m --toplevel -o decimal.min.mjs
```
```js
import Decimal from './decimal.min.mjs';
```
## Licence
[The MIT Licence (Expat).](LICENCE.md)

299
node_modules/decimal.js/decimal.d.ts generated vendored Normal file
View File

@@ -0,0 +1,299 @@
// Type definitions for decimal.js >=7.0.0
// Project: https://github.com/MikeMcl/decimal.js
// Definitions by: Michael Mclaughlin <https://github.com/MikeMcl>
// Definitions: https://github.com/MikeMcl/decimal.js
//
// Documentation: http://mikemcl.github.io/decimal.js/
//
// Exports:
//
// class Decimal (default export)
// type Decimal.Constructor
// type Decimal.Instance
// type Decimal.Modulo
// type Decimal.Rounding
// type Decimal.Value
// interface Decimal.Config
//
// Example (alternative syntax commented-out):
//
// import {Decimal} from "decimal.js"
// //import Decimal from "decimal.js"
//
// let r: Decimal.Rounding = Decimal.ROUND_UP;
// let c: Decimal.Configuration = {precision: 4, rounding: r};
// Decimal.set(c);
// let v: Decimal.Value = '12345.6789';
// let d: Decimal = new Decimal(v);
// //let d: Decimal.Instance = new Decimal(v);
//
// The use of compiler option `--strictNullChecks` is recommended.
export default Decimal;
export namespace Decimal {
export type Constructor = typeof Decimal;
export type Instance = Decimal;
export type Rounding = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
export type Modulo = Rounding | 9;
export type Value = string | number | Decimal;
// http://mikemcl.github.io/decimal.js/#constructor-properties
export interface Config {
precision?: number;
rounding?: Rounding;
toExpNeg?: number;
toExpPos?: number;
minE?: number;
maxE?: number;
crypto?: boolean;
modulo?: Modulo;
defaults?: boolean;
}
}
export declare class Decimal {
readonly d: number[];
readonly e: number;
readonly s: number;
constructor(n: Decimal.Value);
absoluteValue(): Decimal;
abs(): Decimal;
ceil(): Decimal;
clampedTo(min: Decimal.Value, max: Decimal.Value): Decimal;
clamp(min: Decimal.Value, max: Decimal.Value): Decimal;
comparedTo(n: Decimal.Value): number;
cmp(n: Decimal.Value): number;
cosine(): Decimal;
cos(): Decimal;
cubeRoot(): Decimal;
cbrt(): Decimal;
decimalPlaces(): number;
dp(): number;
dividedBy(n: Decimal.Value): Decimal;
div(n: Decimal.Value): Decimal;
dividedToIntegerBy(n: Decimal.Value): Decimal;
divToInt(n: Decimal.Value): Decimal;
equals(n: Decimal.Value): boolean;
eq(n: Decimal.Value): boolean;
floor(): Decimal;
greaterThan(n: Decimal.Value): boolean;
gt(n: Decimal.Value): boolean;
greaterThanOrEqualTo(n: Decimal.Value): boolean;
gte(n: Decimal.Value): boolean;
hyperbolicCosine(): Decimal;
cosh(): Decimal;
hyperbolicSine(): Decimal;
sinh(): Decimal;
hyperbolicTangent(): Decimal;
tanh(): Decimal;
inverseCosine(): Decimal;
acos(): Decimal;
inverseHyperbolicCosine(): Decimal;
acosh(): Decimal;
inverseHyperbolicSine(): Decimal;
asinh(): Decimal;
inverseHyperbolicTangent(): Decimal;
atanh(): Decimal;
inverseSine(): Decimal;
asin(): Decimal;
inverseTangent(): Decimal;
atan(): Decimal;
isFinite(): boolean;
isInteger(): boolean;
isInt(): boolean;
isNaN(): boolean;
isNegative(): boolean;
isNeg(): boolean;
isPositive(): boolean;
isPos(): boolean;
isZero(): boolean;
lessThan(n: Decimal.Value): boolean;
lt(n: Decimal.Value): boolean;
lessThanOrEqualTo(n: Decimal.Value): boolean;
lte(n: Decimal.Value): boolean;
logarithm(n?: Decimal.Value): Decimal;
log(n?: Decimal.Value): Decimal;
minus(n: Decimal.Value): Decimal;
sub(n: Decimal.Value): Decimal;
modulo(n: Decimal.Value): Decimal;
mod(n: Decimal.Value): Decimal;
naturalExponential(): Decimal;
exp(): Decimal;
naturalLogarithm(): Decimal;
ln(): Decimal;
negated(): Decimal;
neg(): Decimal;
plus(n: Decimal.Value): Decimal;
add(n: Decimal.Value): Decimal;
precision(includeZeros?: boolean): number;
sd(includeZeros?: boolean): number;
round(): Decimal;
sine() : Decimal;
sin() : Decimal;
squareRoot(): Decimal;
sqrt(): Decimal;
tangent() : Decimal;
tan() : Decimal;
times(n: Decimal.Value): Decimal;
mul(n: Decimal.Value) : Decimal;
toBinary(significantDigits?: number): string;
toBinary(significantDigits: number, rounding: Decimal.Rounding): string;
toDecimalPlaces(decimalPlaces?: number): Decimal;
toDecimalPlaces(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
toDP(decimalPlaces?: number): Decimal;
toDP(decimalPlaces: number, rounding: Decimal.Rounding): Decimal;
toExponential(decimalPlaces?: number): string;
toExponential(decimalPlaces: number, rounding: Decimal.Rounding): string;
toFixed(decimalPlaces?: number): string;
toFixed(decimalPlaces: number, rounding: Decimal.Rounding): string;
toFraction(max_denominator?: Decimal.Value): Decimal[];
toHexadecimal(significantDigits?: number): string;
toHexadecimal(significantDigits: number, rounding: Decimal.Rounding): string;
toHex(significantDigits?: number): string;
toHex(significantDigits: number, rounding?: Decimal.Rounding): string;
toJSON(): string;
toNearest(n: Decimal.Value, rounding?: Decimal.Rounding): Decimal;
toNumber(): number;
toOctal(significantDigits?: number): string;
toOctal(significantDigits: number, rounding: Decimal.Rounding): string;
toPower(n: Decimal.Value): Decimal;
pow(n: Decimal.Value): Decimal;
toPrecision(significantDigits?: number): string;
toPrecision(significantDigits: number, rounding: Decimal.Rounding): string;
toSignificantDigits(significantDigits?: number): Decimal;
toSignificantDigits(significantDigits: number, rounding: Decimal.Rounding): Decimal;
toSD(significantDigits?: number): Decimal;
toSD(significantDigits: number, rounding: Decimal.Rounding): Decimal;
toString(): string;
truncated(): Decimal;
trunc(): Decimal;
valueOf(): string;
static abs(n: Decimal.Value): Decimal;
static acos(n: Decimal.Value): Decimal;
static acosh(n: Decimal.Value): Decimal;
static add(x: Decimal.Value, y: Decimal.Value): Decimal;
static asin(n: Decimal.Value): Decimal;
static asinh(n: Decimal.Value): Decimal;
static atan(n: Decimal.Value): Decimal;
static atanh(n: Decimal.Value): Decimal;
static atan2(y: Decimal.Value, x: Decimal.Value): Decimal;
static cbrt(n: Decimal.Value): Decimal;
static ceil(n: Decimal.Value): Decimal;
static clamp(n: Decimal.Value, min: Decimal.Value, max: Decimal.Value): Decimal;
static clone(object?: Decimal.Config): Decimal.Constructor;
static config(object: Decimal.Config): Decimal.Constructor;
static cos(n: Decimal.Value): Decimal;
static cosh(n: Decimal.Value): Decimal;
static div(x: Decimal.Value, y: Decimal.Value): Decimal;
static exp(n: Decimal.Value): Decimal;
static floor(n: Decimal.Value): Decimal;
static hypot(...n: Decimal.Value[]): Decimal;
static isDecimal(object: any): object is Decimal;
static ln(n: Decimal.Value): Decimal;
static log(n: Decimal.Value, base?: Decimal.Value): Decimal;
static log2(n: Decimal.Value): Decimal;
static log10(n: Decimal.Value): Decimal;
static max(...n: Decimal.Value[]): Decimal;
static min(...n: Decimal.Value[]): Decimal;
static mod(x: Decimal.Value, y: Decimal.Value): Decimal;
static mul(x: Decimal.Value, y: Decimal.Value): Decimal;
static noConflict(): Decimal.Constructor; // Browser only
static pow(base: Decimal.Value, exponent: Decimal.Value): Decimal;
static random(significantDigits?: number): Decimal;
static round(n: Decimal.Value): Decimal;
static set(object: Decimal.Config): Decimal.Constructor;
static sign(n: Decimal.Value): number;
static sin(n: Decimal.Value): Decimal;
static sinh(n: Decimal.Value): Decimal;
static sqrt(n: Decimal.Value): Decimal;
static sub(x: Decimal.Value, y: Decimal.Value): Decimal;
static sum(...n: Decimal.Value[]): Decimal;
static tan(n: Decimal.Value): Decimal;
static tanh(n: Decimal.Value): Decimal;
static trunc(n: Decimal.Value): Decimal;
static readonly default?: Decimal.Constructor;
static readonly Decimal?: Decimal.Constructor;
static readonly precision: number;
static readonly rounding: Decimal.Rounding;
static readonly toExpNeg: number;
static readonly toExpPos: number;
static readonly minE: number;
static readonly maxE: number;
static readonly crypto: boolean;
static readonly modulo: Decimal.Modulo;
static readonly ROUND_UP: 0;
static readonly ROUND_DOWN: 1;
static readonly ROUND_CEIL: 2;
static readonly ROUND_FLOOR: 3;
static readonly ROUND_HALF_UP: 4;
static readonly ROUND_HALF_DOWN: 5;
static readonly ROUND_HALF_EVEN: 6;
static readonly ROUND_HALF_CEIL: 7;
static readonly ROUND_HALF_FLOOR: 8;
static readonly EUCLID: 9;
}

4934
node_modules/decimal.js/decimal.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

4898
node_modules/decimal.js/decimal.mjs generated vendored Normal file

File diff suppressed because it is too large Load Diff

55
node_modules/decimal.js/package.json generated vendored Normal file
View File

@@ -0,0 +1,55 @@
{
"name": "decimal.js",
"description": "An arbitrary-precision Decimal type for JavaScript.",
"version": "10.4.3",
"keywords": [
"arbitrary",
"precision",
"arithmetic",
"big",
"number",
"decimal",
"float",
"biginteger",
"bigdecimal",
"bignumber",
"bigint",
"bignum"
],
"repository" : {
"type": "git",
"url": "https://github.com/MikeMcl/decimal.js.git"
},
"main": "decimal",
"module": "decimal.mjs",
"browser": "decimal.js",
"exports": {
".": {
"types": "./decimal.d.ts",
"import": "./decimal.mjs",
"require": "./decimal.js"
},
"./decimal.mjs": "./decimal.mjs",
"./decimal.js": "./decimal.js",
"./package.json": "./package.json",
"./decimal": {
"types": "./decimal.d.ts",
"import": "./decimal.mjs",
"require": "./decimal.js"
}
},
"author": {
"name": "Michael Mclaughlin",
"email": "M8ch88l@gmail.com"
},
"license": "MIT",
"scripts": {
"test": "node ./test/test.js"
},
"types": "decimal.d.ts",
"files": [
"decimal.js",
"decimal.mjs",
"decimal.d.ts"
]
}

17
package-lock.json generated Normal file
View File

@@ -0,0 +1,17 @@
{
"name": "smart-camera",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"decimal.js": "^10.4.3"
}
},
"node_modules/decimal.js": {
"version": "10.4.3",
"resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.4.3.tgz",
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
}
}
}

5
package.json Normal file
View File

@@ -0,0 +1,5 @@
{
"dependencies": {
"decimal.js": "^10.4.3"
}
}

80
pages.json Normal file
View File

@@ -0,0 +1,80 @@
{
"pages": [ //pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/home/home",
"style" :
{
"enablePullDownRefresh": false, //设置为true表示当前页面开启下拉刷新
"backgroundTextStyle": "dark",
"navigationStyle": "custom",
"disableScroll": true
},
"lazyCodeLoading": "requiredComponents"
}
,{
"path" : "pages/display/display",
"style" :
{
"enablePullDownRefresh": false,
"navigationStyle": "custom",
"disableScroll": true
},
"lazyCodeLoading": "requiredComponents"
}
,{
"path" : "pages/mine/mine",
"style" :
{
"enablePullDownRefresh": false,
"navigationStyle": "custom",
"disableScroll": true
},
"lazyCodeLoading": "requiredComponents"
}
,{
"path" : "pages/myPhoto/myPhoto",
"style" :
{
"enablePullDownRefresh": false,
"navigationStyle": "custom",
"disableScroll": true
},
"lazyCodeLoading": "requiredComponents"
}
,{
"path" : "pages/selectPhoto/selectPhoto",
"style" :
{
"enablePullDownRefresh": false,
"navigationStyle": "custom",
"disableScroll": true
},
"lazyCodeLoading": "requiredComponents"
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"tabBar": {
"color": "#ffffff",
"selectedColor": "#6777FD",
"custom": true,
"list": [
{
"pagePath": "pages/home/home",
"iconPath": "static/icon/icon_Home_n@3x_da.png",
"selectedIconPath": "static/icon/icon_Home_n@3x_a.png",
"text": "首页"
},
{
"pagePath": "pages/mine/mine",
"iconPath": "static/icon/icon_我的_n@3x_da.png",
"selectedIconPath": "static/icon/icon_我的_n@3x_a.png",
"text": "我的"
}
]
}
}

286
pages/display/display.vue Normal file
View File

@@ -0,0 +1,286 @@
<template>
<view class="page flex-col">
<view class="title" :style="{'top': uniMenuInfo.top + 6 + 'px'}">照片展示</view>
<view class="back" @tap="goBack()" :style="{'top' : uniMenuInfo.top - 12 + 'px'}"></view>
<view class="content" :style="{'top': uniMenuInfo.top + 50 + 'px'}" >
<view v-if="wait">
<view class="img_loading">
<image src="https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/img_%E5%8A%A0%E8%BD%BD%402x.png" mode="aspectFit" class="img_1" />
</view>
<view class="loading">
<text class="tip">请稍候照片生成中...</text>
<zero-loading type="atom" position="absolute"></zero-loading>
</view>
<!-- <view class="progressBar">
<liu-progressbar :progress="jdt" color="#FFFFFF" :height="'25rpx'" :borderRadius="'40rpx'" />
</view> -->
<view class="wait_box">
<text class="wait_1">当前第&nbsp;{{waitIdx}}&nbsp;/&nbsp;{{waitTotal}}&nbsp;</text>
<text class="wait_2">预计需要等候&nbsp;{{waitTime}}&nbsp;分钟</text>
</view>
</view>
<view class="box_show" v-if="!wait">
<image class="showImage" :src="showImage" mode="aspectFill" @tap="previewImage(showImage)"/>
<view class="printTip" v-if="buyType == 2">{{ printScreenTip }}</view>
<view class="upload-btn" @tap="saveImage()">保存到手机</view>
</view>
</view>
</view>
</template>
<script>
const getNowTime = require('../../utils/timeUtil/timeUtil.js')
let pageNumber = 4;
let startTime = '';
let destroyTime = '';
export default {
data() {
return {
uniMenuInfo: {},
wait: true,
showImage: '',
code: '',
waitIdx: 0,
waitTotal: 0,
waitTime: 0,
jdt: 0,
templateId: 0,
mchId: 0,
downloading: false,
buyType: 0,
printCode: '',
printScreenTip: ''
}
},
onLoad(option) {
var _this = this;
uni.showLoading({
title: '正在加载...',
mask: true
})
this.uniMenuInfo = uni.getStorageSync('uniMenuInfo');
this.code = option.code;
this.templateId = Number(option.templateId);
this.mchId = Number(option.mchId);
this.buyType = Number(option.buyType);
this.firstGetPhoto(_this.code);
this.getPhoto(this.code);
},
onShow() {
startTime = getNowTime();
},
onHide() {
if(uni.getStorageSync('userInfo').id) {
this.logRecord();
}
},
onUnload() {
if(uni.getStorageSync('userInfo').id) {
this.logRecord();
}
},
methods: {
logRecord() {
var _this = this;
destroyTime = getNowTime();
let reqData = {
"startTime": startTime,
"destroyTime": destroyTime,
"pageNumber": pageNumber,
"qrcodeScenicId": uni.getStorageSync('scanQrcodeInfo').scenicId,
"qrcodeMchId": uni.getStorageSync('scanQrcodeInfo').mchId,
"qrcodeId": uni.getStorageSync('scanQrcodeInfo').qrcodeId,
"userId": uni.getStorageSync('userInfo').id,
"appType": 5,
"photoId": _this.templateId,
"mchId": _this.mchId,
};
uni.request({
url: _this.java_http_url + '/applet/smart_camera/logRecord',
method: 'POST',
data: reqData,
success(res) {}
})
},
uploadImage () {
var _this = this;
uni.getImageInfo({
src: _this.showImage,
success: function(image) {
uni.saveImageToPhotosAlbum({
filePath: image.path,
success: function() {
uni.showToast({
title: '照片保存成功',
icon: 'success',
duration: 2000
});
},
fail: (err) => {
console.log(err)
},
complete: (env) => {
uni.hideLoading()
_this.downloading = false;
}
});
},
fail: (err) => {
console.log('err', err)
uni.hideLoading()
_this.downloading = false;
}
});
},
saveImage() {
var _this = this;
if(!_this.downloading) {
_this.downloading = true;
uni.showLoading({
title: '请稍候',
mark: true
})
wx.getSetting({
success: (res) => {
if (!res.authSetting['scope.writePhotosAlbum']) {
// 用户未授权,需要请求授权
wx.authorize({
scope: 'scope.writePhotosAlbum',
success: () => {
// 用户已授权,可以进行保存图片操作
console.log('已授权')
_this.uploadImage();
},
fail: () => {
// 用户拒绝授权,需要引导用户去设置页面手动授权
uni.hideLoading()
_this.downloading = false;
wx.showModal({
title: '需要保存图片权限',
content: '请在设置中打开保存图片到相册权限',
success: (res) => {
if (res.confirm) {
// 用户点击确定,跳转到设置页面
wx.openSetting({
success: (res) => {
// 检查是否授权
// if (res.authSetting['scope.writePhotosAlbum']) {
// // 授权成功,可以进行保存图片操作
// _this.uploadImage();
// }
}
})
}
}
})
}
})
} else {
_this.uploadImage();
}
},
fail(err) {
console.log(err)
uni.showToast({
title: '获取设置失败'
})
uni.hideLoading()
_this.downloading = false;
}
})
}
},
getPhoto(code) {
var _this = this;
// 获取照片制作状态
var stateInterval = setInterval(function() {
uni.request({
url: _this.java_http_url + '/applet/smart_camera/getSingleTemplateSynthesisPhoto',
method: 'POST',
data: {
"code": code,
"mchId": _this.mchId,
"print": _this.buyType
},
success(res) {
console.log('res', res)
if(res.data.code == 200) {
let resData = res.data.data;
_this.waitIdx = resData.waitIdx == -1?1:resData.waitIdx;
_this.waitTotal = resData.waitTotal;
_this.waitTime = resData.waitTotal * 1;
console.log('url', resData.url)
// 判断是否拿到照片路径
if(resData.url) {
clearInterval(stateInterval)
let url = resData.url.replace(/\[|\]/g, "");
_this.showImage = 'https://oss.humengfilms.com/' + url;
// 判断是否有打印码
if(resData.printCode != null) {
_this.printCode = resData.printCode;
let text = uni.getStorageSync('aiPhotoTemplate').printScreenTip;
_this.printScreenTip = text.replace("*", resData.printCode);
}
pageNumber = 5;
_this.wait = false;
}
}
}
})
}, 5000);
},
firstGetPhoto(code) {
var _this = this;
uni.request({
url: _this.java_http_url + '/applet/smart_camera/getSingleTemplateSynthesisPhoto',
method: 'POST',
data: {
"code": code
},
success(res) {
console.log('res', res)
if(res.data.code == 200) {
let resData = res.data.data;
_this.waitIdx = resData.waitIdx == -1?1:resData.waitIdx;
_this.waitTotal = resData.waitTotal == 0?1:resData.waitTotal;
_this.waitTime = resData.waitTotal * 1;
if(res.data.url) {
let url = res.data.data.replace(/\[|\]/g, "");
_this.showImage = 'https://oss.humengfilms.com/' + url;
_this.wait = false;
}
}
},
complete() {
uni.hideLoading();
}
})
},
previewImage(url) {
// 预览图片
uni.previewImage({
urls: [url],
longPressActions: {
itemList: ['发送给朋友', '保存图片', '收藏'],
success: function(data) {
console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
},
fail: function(err) {
console.log(err.errMsg);
}
}
});
},
goBack() {
uni.navigateBack({
delta: 1
})
},
}
}
</script>
<style lang="scss">
@import '@/css/common.css';
@import './index.css';
</style>

137
pages/display/index.css Normal file
View File

@@ -0,0 +1,137 @@
.page {
position: absolute;
height: 100vh;
width: 100vw;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/bg_%E4%B8%BB%E8%83%8C%E6%99%AF%402x.png)
100% no-repeat;
background-size: 100% 100%;
}
.back {
position: absolute;
width: 56px;
height: 56px;
left: 0;
background-image: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/humeng/vlog/lv2_icon_backBlack%402x.png);
background-size: 100% 100%;
}
.title {
position: absolute;
font-size: 16px;
line-height: 22px;
color: #454F63;
z-index: 999;
font-weight: 500;
}
.content {
position: absolute;
width: 100%;
}
.img_loading {
display: flex;
justify-content: center;
margin-top: 30rpx;
}
.img_1 {
width: 300rpx;
height: 300rpx;
}
.loading {
position: relative;
width: 100%;
height: 300rpx;
display: flex;
justify-content: center;
margin-top: 60rpx;
}
.tip{
text-align: center;
font-family: '苹方';
font-size:25rpx;
color:#006d9f;
animation: opp 2.7s infinite linear;
}
@keyframes opp{
0%{
opacity: 1;
}
40%{
opacity: 1;
}
60%{
opacity: 0;
}
100%{
opacity: 1;
}
}
.box_show {
display: flex;
/* justify-content: center; */
align-items: center;
flex-direction: column;
margin-top: 50rpx;
height: 75vh;
}
.showImage {
border: #fff 1px solid;
height: 60vh;
width: 80%;
border-radius: 20px;
}
.printTip {
width: 80%;
margin-top: 20rpx;
}
.upload-btn {
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/btn%403x.png)
100% no-repeat;
background-size: 100% 100%;
height: 100rpx;
line-height: 100rpx;
width: 80%;
margin-top: 20rpx;
overflow-wrap: break-word;
color: rgba(255, 255, 255, 1);
font-size: 16px;
font-family: 'Microsoft YaHei', 'Microsoft Yahei', Arial, sans-serif;
text-align: center;
white-space: nowrap;
position: absolute;
bottom: 0;
}
.wait_box {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.wait_1, .wait_2 {
color: #505C73 ;
font-weight: 700;
font-size: 14px;
font-family: "Microsoft YaHei", sans-serif;
margin-top: 8rpx;
}
.progressBar {
width: 80%;
margin-top: 40rpx;
}

444
pages/home/home.vue Normal file
View File

@@ -0,0 +1,444 @@
<template>
<view class="page flex-col">
<view class="title" :style="{'top': uniMenuInfo.top + 6 + 'px'}">{{ headline }}</view>
<!-- <view class="back" v-if="!scanQRCodes" @tap="goBack()" :style="{'top' : uniMenuInfo.top - 12 + 'px'}"></view> -->
<!-- 切换商户 -->
<view class="root" v-if="!scanQRCodes" :style="{'top': uniMenuInfo.top + 40 + 'px'}">
<ren-dropdown-filter ref="root" :filterData='filterData' :defaultIndex='defaultIndex'
@onSelected='onSelected' @dateChange='dateChange'></ren-dropdown-filter>
</view>
<view class="box_1 flex-col" :style="{'top': uniMenuInfo.top + boxTop + 'px'}">
<view class="box_3 flex-col" v-for="item1 in templateSet" :key="item1.id">
<view class="text-wrapper_2 flex-row">
<text class="text_4">{{item1.name}}</text>
</view>
<scroll-view class="scroll-view_H" scroll-x="true" :show-scrollbar="false" :enhanced="true">
<view id="demo1" class="scroll-view-item_H" @tap="shareToggle(item2, item1)" v-for="(item2, idx) in item1.templates" :key="item2.id">
<image class="img_1" :src="item2.coverPicture" mode="aspectFill"></image>
<view :class="item2.paymentType == 1? 'text-wrapper_5 flex-col':item2.paymentType == 2? 'text-wrapper_6 flex-col':'text-wrapper_3 flex-col'">
<text class="text_5" >{{item2.paymentType == 1?'付费':item2.paymentType == 2?'已购':'限免'}}</text>
</view>
<view class="text-wrapper_4 flex-col" >
<text class="text_6">{{item2.templateName}}</text>
</view>
</view>
</scroll-view>
</view>
</view>
<!-- 隐私授权弹窗 -->
<zero-privacy ref="privacy" @agree='handleAgree' @disagree='handleDisagree'></zero-privacy>
<!-- 底部导航 -->
<view class="section_8 flex-row justify-between">
<custom-tab-bar :selected="0" />
</view>
</view>
</template>
<script>
const uploadFile= require('../../utils/uploadToAliOss/uploadAliyun.js');
const getNowTime = require('../../utils/timeUtil/timeUtil.js')
import CustomTabBar from "@/components/customTabBar/customTabBar.vue"
import RenDropdownFilter from '@/components/ren-dropdown-filter/ren-dropdown-filter.vue'
let pageNumber = 2;
let startTime = '';
let destroyTime = '';
export default {
components: {
CustomTabBar,
RenDropdownFilter
},
data() {
return {
uniMenuInfo: {},
selectTemplateImg: '',
templateSet: [],
selectTemplateId: 0,
headline: '森蓝富拉格',
scanQRCodes: false,
qrcodeId: 0,
userInfo: {},
init: true,
filterData:[
[
{ text: '选择景区', value: 0, select: false}
]
],
defaultIndex:[0],
boxTop: 90
};
},
onLoad(option) {
console.log(option)
var _this = this;
uni.showLoading({
title: '正在加载...',
mask: true
});
this.uniMenuInfo = uni.getMenuButtonBoundingClientRect();
uni.setStorageSync('uniMenuInfo', this.uniMenuInfo);
if (option.scene) {
let data_arr = decodeURIComponent(option.scene).split('&');
let _data = {};
for (let i = 0; i < data_arr.length; i++) {
let temp_arr = data_arr[i].split("=");
_data[temp_arr[0]] = temp_arr[1]
}
_this.qrcodeId = Number(_data['qrcodeId']);
_this.scanQRCodes = true;
_this.boxTop = 40;
}
uni.removeStorageSync('mchId');
if(_this.qrcodeId != 0) {
_this.init = false;
uni.removeStorageSync('userInfo');
new Promise(function(resolve,reject) {
_this.getQrcodeInfo(resolve, _this.qrcodeId);
}).then(function() {
_this.getPrivacySetting();
})
} else {
_this.getMchList();
_this.getPrivacySetting();
}
},
onShow() {
if(this.init && uni.getStorageSync('mchId')) {
this.initialize();
}
startTime = getNowTime();
},
onHide() {
if(uni.getStorageSync('userInfo').id) {
this.logRecord();
}
},
onUnload() {
if(uni.getStorageSync('userInfo').id) {
this.logRecord();
}
},
methods: {
getMchList() {
var _this = this;
uni.request({
url: _this.java_http_url + '/applet/smart_camera/getMchList',
method: 'POST',
data: {
appletType: 2
},
success(res) {
console.log('商户列表', res.data.data)
let mchList = res.data.data;
_this.filterData = [
[
{ text: '选择景区', value: 0, select: false}
]
]
mchList.forEach((el)=>{
_this.filterData[0].push({
text: el.mchName,
value: el.id,
select: false
})
})
_this.$refs.root.updateFilterData(_this.filterData);
}
})
},
onSelected(res){
console.log(res[0][0].value)
uni.setStorageSync('mchId', res[0][0].value)
if(res[0][0].value == 0) {
this.headline = '森蓝富拉格'
} else {
this.headline = res[0][0].text
}
this.initialize()
},
dateChange(d){
uni.showToast({
icon:'none',
title:d
})
},
logRecord() {
var _this = this;
destroyTime = getNowTime();
let reqData = {
"startTime": startTime,
"destroyTime": destroyTime,
"pageNumber": pageNumber,
"qrcodeScenicId": uni.getStorageSync('scanQrcodeInfo').scenicId,
"qrcodeMchId": uni.getStorageSync('scanQrcodeInfo').mchId,
"qrcodeId": uni.getStorageSync('scanQrcodeInfo').qrcodeId,
"userId": uni.getStorageSync('userInfo').id,
"appType": 5,
"mchId": uni.getStorageSync('scanQrcodeInfo').mchId
};
uni.request({
url: _this.java_http_url + '/applet/smart_camera/logRecord',
method: 'POST',
data: reqData,
success(res) {}
})
},
handleDisagree() {
console.log('拒绝隐私授权');
var _this = this;
uni.showModal({
title: '提示',
content: '拒绝授权部分功能将无法使用,是否退出小程序',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
wx.exitMiniProgram({
success: (res) => {
console.log('退出小程序', res)
}})
} else if (res.cancel) {
console.log('用户点击取消');
_this.getPrivacySetting();
}
}
});
},
handleAgree() {
console.log('同意隐私授权');
var _this = this;
uni.showLoading({
title: '正在加载...',
mask: true
});
_this.getUserInfo();
},
getQrcodeInfo(resolve, qrcodeId) {
var _this = this;
uni.request({
url: _this.java_http_url + '/applet/smart_camera/getQrcodeInfo',
method: 'POST',
data: {
id: qrcodeId
},
success(res) {
if(res.data.data) {
let mchId = res.data.data.mchId;
_this.headline = res.data.data.mchName;
uni.setStorageSync('mchId', mchId);
let scanQrcodeInfo = {
qrcodeId: qrcodeId,
mchId: mchId,
scenicId: 0
}
uni.setStorageSync('scanQrcodeInfo', scanQrcodeInfo)
resolve('ok')
}
}
})
},
getUserInfo() {
var _this = this;
uni.getUserInfo({
desc: '用于完善会员资料',
lang: 'zh_CN',
success: (user) => {
// >>当用户同意授权后, 获取到用户信息user
uni.login({
provider: 'weixin',
success: loginRes => {
let userInfo = JSON.parse(user.rawData);
userInfo.code = loginRes.code;
userInfo.appletType = 2;
uni.getSystemInfo({
success: function(res) {
userInfo.model = res.model
userInfo.edition = res.version
uni.request({
url: _this.java_http_url + '/applet/smart_camera/codeGetUserInfo',
method: "POST",
data: userInfo,
success(res) {
if(res.data.code == 200) {
userInfo = res.data.data;
_this.userInfo = userInfo;
uni.setStorageSync('userInfo', userInfo);
if(uni.getStorageSync('mchId')) {
_this.initialize();
}
}
},
complete() {
uni.hideLoading();
}
})
},
})
}
})
},
fail(err) {
console.log(err);
uni.hideLoading();
}
})
},
getPrivacySetting() {
var _this = this;
wx.getPrivacySetting({
success: res => {
console.log(res)
if (res.needAuthorization) {
// 需要弹出隐私协议
console.log('需要弹出隐私协议', res)
_this.$refs.privacy.open(res.privacyContractName);
uni.hideLoading();
} else {
_this.getUserInfo();
}
}
})
},
getToday() {
var now = new Date();
var year = now.getFullYear();
var month = now.getMonth() + 1;
var day = now.getDate();
month = month.toString().padStart(2, '0');
day = day.toString().padStart(2, '0');
return `${year}-${month}-${day}`;
},
syntheticImage(url) {
var _this = this;
let dir = 'smartcamera/' + _this.getToday() + '/';
var match = url[0].match(/\.([^.]+)$/);
let promise = Promise.all(
url.map((tempFilePath, index) => {
return new Promise(function(resolve, reject) {
uploadFile({
filePath: tempFilePath,
dir: dir,
type: match[0],
success: function(res) {
// console.log('上传成功---', res);
resolve(res);
},
fail: function(res) {
// console.log('上传失败---', res);
uni.hideLoading();
uni.showToast({
title: '上传失败,请重试!',
icon: 'none',
duration: 4000
})
}
});
});
})
).then((res) => {
let code = _this.generateCode(32);
uni.request({
url: _this.java_http_url + '/applet/smart_camera/synthesisAiPhoto',
method: 'POST',
data: {
'code': code,
'templateId': _this.selectTemplateId,
'photo': res[0],
'userId': uni.getStorageSync('userInfo').id
},
success: (res) => {
console.log('aiphotoRes', res)
if(res.data.code == 200) {
uni.hideLoading();
_this.$refs.popup.close();
uni.navigateTo({
url: "../display/display?code=" + code
})
}
},
fail(err) {
console.log('err', err)
}
});
});
},
generateCode(length) {
let code = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
code += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return code;
},
initialize() {
uni.showLoading({
title: '正在加载...',
mask: true
});
var _this = this;
_this.init = true;
uni.request({
url: _this.java_http_url + '/applet/smart_camera/getTemplateSetList',
method: 'POST',
data: {
mchId: uni.getStorageSync('mchId'),
userId: uni.getStorageSync('userInfo').id
},
success(res) {
console.log('getTemplateSetAll', res)
if(res.data.code == 200) {
_this.templateSet = res.data.data;
uni.setStorageSync('templateSets', _this.templateSet);
setTimeout(()=> {
uni.hideLoading();
}, 500)
} else {
uni.hideLoading();
}
},
fail(err) {
console.log('error', err)
uni.hideLoading();
}
})
},
selectImage(data) {
var _this = this;
let img = data.item;
uni.showModal({
title: '温馨提示',
content: '是否开始制作',
confirmColor: '#6b99fb',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
uni.showLoading({
title: '上传中',
mask: true
});
_this.syntheticImage(img);
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
scroll(e) {
// console.log(e)
},
shareToggle(template, templateSet) {
uni.navigateTo({
url: '../selectPhoto/selectPhoto?templateId=' + template.id + '&templateSetId=' + templateSet.id
})
},
goBack() {
uni.navigateBack({
delta: 1
})
},
},
};
</script>
<style lang="scss">
@import '@/css/common.css';
@import './index.css';
</style>

172
pages/home/index.css Normal file
View File

@@ -0,0 +1,172 @@
.page {
position: absolute;
height: 100vh;
width: 100vw;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/bg_%E4%B8%BB%E8%83%8C%E6%99%AF%402x.png)
100% no-repeat;
background-size: 100% 100%;
}
.back {
position: absolute;
width: 56px;
height: 56px;
left: 0;
background-image: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/humeng/vlog/lv2_icon_backBlack%402x.png);
background-size: 100% 100%;
}
.title {
position: absolute;
font-size: 16px;
line-height: 22px;
color: #454F63;
z-index: 999;
font-weight: 500;
}
.root {
position: absolute;
}
.box_1 {
position: absolute;
width: 100%;
height: 100%;
padding-bottom: 30%;
overflow: auto;
}
.box_3 {
padding: 20rpx 0 10rpx 24rpx;
}
.text-wrapper_2 {
margin: 10rpx 10rpx 0 0;
}
.text_4 {
overflow-wrap: break-word;
color: rgba(38, 47, 64, 1);
font-size: 32rpx;
font-family: PingFangSC-Medium;
font-weight: 900;
text-align: left;
white-space: nowrap;
line-height: 32rpx;
}
.text-wrapper_3 {
position: absolute;
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/img_%E5%85%8D%E8%B4%B9%402x.png)
100% no-repeat;
background-size: 100% 100%;
margin-left: 168rpx;
padding: 8rpx 12rpx 8rpx 24rpx;
top: 0;
right: 0;
}
.text-wrapper_5 {
position: absolute;
background: url(https://res.oss.humengfilms.com/hm_aigc/applet/img_%E4%BB%98%E8%B4%B9%402x.png)
100% no-repeat;
background-size: 100% 100%;
margin-left: 168rpx;
padding: 8rpx 12rpx 8rpx 24rpx;
top: 0;
right: 0;
}
.text-wrapper_6 {
position: absolute;
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/img_%E5%B7%B2%E8%B4%AD%402x.png)
100% no-repeat;
background-size: 100% 100%;
margin-left: 168rpx;
padding: 8rpx 12rpx 8rpx 24rpx;
top: 0;
right: 0;
}
.text_5 {
overflow-wrap: break-word;
color: rgba(255, 255, 255, 1);
font-size: 24rpx;
font-family: PingFangSC-Medium;
font-weight: 500;
text-align: left;
white-space: nowrap;
line-height: 24rpx;
}
.text-wrapper_4 {
position: absolute;
bottom: 5rpx;
background-size: 100% 100%;
/* margin-top: 208rpx; */
/* padding: 40rpx 144rpx 24rpx 24rpx; */
height: 40rpx;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.text_6 {
overflow-wrap: break-word;
color: rgba(255, 255, 255, 1);
font-size: 28rpx;
font-weight: normal;
text-align: center;
white-space: nowrap;
line-height: 40rpx;
width: 95%;
overflow: hidden;
/* margin-left: 5%; */
}
.img_1 {
position: relative;
width: 100%;
height: 100%;
border-radius: 11px;
}
.scroll-view_H {
white-space: nowrap;
width: 100%;
margin-top: 25rpx;
}
.scroll-view-item_H {
display: inline-block;
width: 249rpx;
height: 330rpx;
line-height: 300rpx;
text-align: center;
font-size: 36rpx;
margin-right: 20px;
border-radius: 10px;
position: relative;
}
.section_8 {
position: absolute;
left: 0;
width: 100%;
height: 130rpx;
background: #ffffff
100% no-repeat;
background-size: 100% 100%;
padding: 14px 72px 4px 72px;
bottom: 0;
}
.box_3:last-child {
margin-bottom: 220rpx;
}

173
pages/mine/index.css Normal file
View File

@@ -0,0 +1,173 @@
.page {
position: absolute;
height: 100vh;
width: 100vw;
overflow: hidden;
display: flex;
align-items: center; /* 垂直居中 */
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/bg_%E4%B8%BB%E8%83%8C%E6%99%AF%402x.png)
100% no-repeat;
background-size: 100% 100%;
}
.title {
position: absolute;
font-size: 16px;
line-height: 22px;
color: #454F63;
z-index: 999;
font-weight: 500;
}
.section_8 {
position: absolute;
left: 0;
width: 100%;
height: 130rpx;
background: #ffffff
100% no-repeat;
background-size: 100% 100%;
padding: 14px 72px 4px 72px;
bottom: 0;
}
.content {
position: relative;
width: 100%;
}
.box_1 {
position: relative;
border-radius: 40rpx 40rpx 0 0;
width: 85%;
height: 240rpx;
overflow: hidden;
background: linear-gradient(to bottom, rgba(42, 156, 249, 0.6) 10%, rgba(42, 156, 249, 0.5) 30%, rgba(42, 156, 249, 0.4) 60%);
display: block;
align-items: center;
margin-left: auto;
margin-right: auto;
}
.head-img {
width: 48px;
height: 48px;
position: relative;
z-index: 10;
border-radius: 50%;
overflow: hidden;
left: 40rpx;
top: 50rpx;
}
.nickname {
position: absolute;
left: 180rpx;
top: 80rpx;
font-size: 14px;
font-weight: 500;
color: #30334B;
opacity: 1;
font-family: 'Microsoft YaHei', Arial, sans-serif;
}
.box_2 {
position: relative;
background-color: #ffffff;
/* height: 530rpx; */
width: 85%;
border-radius: 40rpx;
margin-top: -40rpx;
margin-left: auto;
margin-right: auto;
}
.orders{
width: 90%;
height: 100rpx;
border-bottom: 1px solid rgba(192, 196, 202, 0.3);
position: relative;
left: 5%;
}
.orders-name{
text-align: left;
line-height: 100rpx;
font-size: 16px;
font-weight: 300;
color: #333333;
opacity: 1;
font-family: 'Microsoft YaHei', Arial, sans-serif;
}
.coupons {
width: 90%;
height: 100rpx;
border-bottom: 1px solid rgba(192, 196, 202, 0.3);
position: relative;
left: 5%;
}
.coupons-name {
text-align: left;
line-height: 100rpx;
font-size: 16px;
font-weight: 300;
color: #333333;
opacity: 1;
font-family: 'Microsoft YaHei', Arial, sans-serif;
}
.yjfk {
width: 90%;
height: 100rpx;
position: relative;
left: 5%;
border-bottom: 1px solid rgba(192, 196, 202, 0.3);;
}
.yjfk-name {
text-align: left;
line-height: 100rpx;
font-size: 16px;
font-weight: 300;
color: #333333;
opacity: 1;
font-family: 'Microsoft YaHei', Arial, sans-serif;
}
.kfhh::after{
background:none;
border:none;
}
.kfhh {
width: 90%;
height: 100rpx;
position: relative;
left: 5%;
padding: 0;
margin: 0;
background:none;
border:none;
}
.kfhh-name {
text-align: left;
line-height: 100rpx;
font-size: 16px;
font-weight: 300;
color: #333333;
opacity: 1;
font-family: 'Microsoft YaHei', Arial, sans-serif;
}
.coupons-icon,.yjfk-icon,.kfhh-icon,.myOrders-icon {
position: absolute;
right: 0px;
width: 30rpx;
top: 34rpx;
height: 30rpx;
background-image: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/humeng/vlog/ly_icon_listNext%402x.png);
background-size: 100% 100%;
}

181
pages/mine/mine.vue Normal file
View File

@@ -0,0 +1,181 @@
<template>
<view class="page flex-col">
<view class="title" :style="{'top': uniMenuInfo.top + 6 + 'px'}">个人中心</view>
<view class="content" :style="{'top' : uniMenuInfo.top + 60 + 'px'}">
<view class="box_1">
<view>
<image class="head-img" src="/static/center/user_img2.png"></image>
<view class="nickname">
<text>用户编号: {{ userNumber }}</text>
</view>
</view>
</view>
<view class="box_2">
<view class="orders" @tap="menuClick(1)">
<view class="orders-name">我的写真</view>
<view class="myOrders-icon"></view>
</view>
<view class="yjfk" @tap="menuClick(3)">
<view class="yjfk-name">意见反馈</view>
<view class="yjfk-icon"></view>
</view>
<button class="kfhh" open-type="contact">
<view class="kfhh-name">联系微信客服</view>
<view class="kfhh-icon"></view>
</button>
</view>
</view>
<view class="section_8 flex-row justify-between">
<custom-tab-bar :selected="1" />
</view>
<!-- 隐私授权弹窗 -->
<zero-privacy ref="privacy" @agree='handleAgree' @disagree='handleDisagree'></zero-privacy>
</view>
</template>
<script>
import CustomTabBar from "@/components/customTabBar/customTabBar.vue"
export default {
components: {
CustomTabBar
},
data() {
return {
uniMenuInfo: {},
userNumber: '',
userInfo: {},
}
},
methods: {
getUserInfo() {
var _this = this;
uni.getUserInfo({
desc: '用于完善会员资料',
lang: 'zh_CN',
success: (user) => {
// >>当用户同意授权后, 获取到用户信息user
uni.login({
provider: 'weixin',
success: loginRes => {
let userInfo = JSON.parse(user.rawData);
userInfo.code = loginRes.code;
uni.getSystemInfo({
success: function(res) {
userInfo.model = res.model
userInfo.edition = res.version
uni.request({
url: _this.java_http_url + '/applet/smart_camera/codeGetUserInfo',
method: "POST",
data: userInfo,
success(res) {
if(res.data.code == 200) {
userInfo = res.data.data;
_this.userInfo = userInfo;
_this.userNumber = _this.userInfo.openId.slice(-6);
uni.setStorageSync('userInfo', userInfo);
}
},
complete() {
uni.hideLoading();
}
})
},
})
}
})
},
fail(err) {
console.log(err);
uni.hideLoading();
}
})
},
handleDisagree() {
console.log('拒绝隐私授权');
uni.showModal({
title: '提示',
content: '拒绝授权部分功能将无法使用,是否退出小程序',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
wx.exitMiniProgram({
success: (res) => {
console.log('退出小程序', res)
}})
} else if (res.cancel) {
console.log('用户点击取消');
uni.switchTab({
url: '../home/home'
})
}
}
});
},
handleAgree() {
var _this = this;
uni.showLoading({
title: '正在加载...',
mask: true
});
_this.getUserInfo();
},
getPrivacySetting() {
var _this = this;
wx.getPrivacySetting({
success: res => {
if (res.needAuthorization) {
_this.$refs.privacy.open(res.privacyContractName);
}
}
})
},
menuClick(e){
console.log(e);
if(e == 1) {
uni.navigateTo({
url: '../myPhoto/myPhoto'
})
} else if(e ==2) {
uni.navigateTo({
url: '../myVideo/myVideo'
})
} else {
uni.showToast({
title: '该功能正在完善中!',
icon: 'none',
duration: 2000
})
}
},
goHome() {
uni.navigateTo({
url: '../home/home'
})
}
},
onShow() {
var _this = this;
if (typeof this.$mp.page.getTabBar === 'function' && this.$mp.page.getTabBar()) {
this.$mp.page.getTabBar().setData({
selected: 1
})
}
if(!uni.getStorageSync('userInfo')) {
_this.getPrivacySetting();
} else {
_this.userInfo = uni.getStorageSync('userInfo');
_this.userNumber = _this.userInfo.openId.slice(-6);
}
},
onLoad(option) {
var _this = this;
_this.uniMenuInfo = uni.getStorageSync('uniMenuInfo');
},
}
</script>
<style lang="scss">
@import '@/css/common.css';
@import './index.css';
</style>

62
pages/myPhoto/index.css Normal file
View File

@@ -0,0 +1,62 @@
.page {
position: absolute;
height: 100vh;
width: 100vw;
overflow: hidden;
display: flex;
/* justify-content: center; */
align-items: center;
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/bg_%E4%B8%BB%E8%83%8C%E6%99%AF%402x.png)
100% no-repeat;
background-size: 100% 100%;
}
.title {
position: absolute;
font-size: 16px;
line-height: 22px;
color: #454F63;
z-index: 999;
font-weight: 500;
}
.back {
position: absolute;
width: 56px;
height: 56px;
left: 0;
background-image: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/humeng/vlog/lv2_icon_backBlack%402x.png);
background-size: 100% 100%;
}
.center {
display: flex;
flex-wrap: wrap;
overflow-y: scroll;
position: absolute;
bottom: 93px;
width: 100vw;
align-content: baseline;
}
.img {
position: relative;
width: 45%;
left: calc((100vw - 96%)/2);
height: 420rpx;
background: #FFFFFF;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.02);
margin: 20rpx 10rpx 0 10rpx;
border-radius: 12px;
}
image {
width: 100%;
height: 100%;
border-radius: 12px;
}
.center :last-child {
margin-bottom: 50rpx;
}

72
pages/myPhoto/myPhoto.vue Normal file
View File

@@ -0,0 +1,72 @@
<template>
<view class="page flex-col">
<view class="title" :style="{'top': uniMenuInfo.top + 6 + 'px'}">我的写真</view>
<view class="back" @tap="goBack()" :style="{'top' : uniMenuInfo.top - 12 + 'px'}"></view>
<view class="center" v-if="uniMenuInfo.bottom"
:style="{'height': 'calc(100vh - ' + (uniMenuInfo.bottom + 10) + 'px)', 'top': uniMenuInfo.top + 35 + 'px'}">
<view class="img" v-for="(item, idx) in imgUrlList" @tap="previewImage(item.aiPhoto)" :key="item.id">
<image :src="item.aiPhoto+'?x-oss-process=image/resize,m_lfit,h_800,w_800'" mode="aspectFill" />
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
uniMenuInfo: {},
imgUrlList: [],
}
},
onLoad() {
uni.showLoading({
title: '正在加载...',
mask: true
});
this.uniMenuInfo = uni.getStorageSync('uniMenuInfo');
this.initializeLoading();
},
methods: {
previewImage(url) {
let previewImageList = [];
previewImageList.push(url);
uni.previewImage({
urls: previewImageList,
});
},
goBack() {
uni.navigateBack({
delta: 1
})
},
initializeLoading() {
var _this = this;
uni.request({
url: _this.java_http_url + '/applet/smart_camera/getMyAiPhoto',
method: 'POST',
data: {
userId: uni.getStorageSync('userInfo').id
},
success(res) {
console.log('用户生成ai照片', res)
if(res.data.code == 200) {
_this.imgUrlList = res.data.data;
_this.imgUrlList.forEach((el, idx)=> {
el.aiPhoto = 'https://oss.humengfilms.com/' + el.aiPhoto
})
}
},
complete() {
uni.hideLoading();
}
})
}
}
}
</script>
<style>
@import url("@/pages/myPhoto/index.css");
@import url("@/css/common.css");
</style>

119
pages/selectPhoto/index.css Normal file
View File

@@ -0,0 +1,119 @@
.page {
position: absolute;
height: 100vh;
width: 100vw;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/bg_%E4%B8%BB%E8%83%8C%E6%99%AF%402x.png) 100% no-repeat;
background-size: 100% 100%;
}
.back {
position: absolute;
width: 56px;
height: 56px;
left: 0;
background-image: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/humeng/vlog/lv2_icon_backBlack%402x.png);
background-size: 100% 100%;
}
.title {
position: absolute;
font-size: 16px;
line-height: 22px;
color: #454F63;
z-index: 999;
font-weight: 500;
}
.content {
position: absolute;
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 100%;
height: 95%;
align-items: center;
}
.img_1 {
height: 55%;
width: 80%;
margin-top: 40rpx;
border-radius: 11px;
}
.tries-limit {
margin-top: 20rpx;
font-size: 18px;
color: rgba(134, 137, 139, 0.8);
}
.section_11 {
align-self: center;
margin-top: 50rpx;
}
.section_8 {
background-color: rgba(244, 249, 253, 1);
border-radius: 10px;
padding: 12px 30px 12px 32px;
margin-right: 20rpx;
height: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.image-text_7 {}
.label_1 {
width: 40px;
height: 40px;
margin: 0 23px 0 21px;
}
.text-group_3 {
overflow-wrap: break-word;
color: rgba(80, 92, 115, 1);
font-size: 14px;
font-family: PingFangSC-Medium;
font-weight: 500;
text-align: left;
white-space: nowrap;
line-height: 14px;
margin-top: 12px;
}
.section_9 {
background-color: rgba(244, 249, 253, 1);
border-radius: 10px;
padding: 17px 45px 12px 45px;
margin-left: 20rpx;
height: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.image-text_8 {}
.label_2 {
width: 40px;
height: 40px;
align-self: center;
}
.text-group_4 {
overflow-wrap: break-word;
color: rgba(80, 92, 115, 1);
font-size: 14px;
font-family: PingFangSC-Medium;
font-weight: 500;
text-align: left;
white-space: nowrap;
line-height: 14px;
margin-top: 17px;
}

View File

@@ -0,0 +1,368 @@
<template>
<view class="page flex-col">
<view class="title" :style="{'top': uniMenuInfo.top + 6 + 'px'}">{{template.templateName}}</view>
<view class="back" @tap="goBack()" :style="{'top' : uniMenuInfo.top - 12 + 'px'}"></view>
<view class="content" :style="{'top' : uniMenuInfo.top + 40 + 'px'}">
<image class="img_1" mode="aspectFill" :src="template.coverPicture" />
<view class="tries-limit" v-if="template.paymentType == 2 && residues != 0">
此模板剩余使用次数:&nbsp;&nbsp;{{ residues }}
</view>
<view class="tries-limit" v-if="template.paymentType == 1">
付费 {{ template.fee }} &nbsp;&nbsp;体验{{ template.triesLimit }}
</view>
<view class="section_11 flex-row">
<view class="section_8 flex-row" @tap="chooseImage('album')">
<view class="image-text_7 flex-col">
<image
class="label_1"
referrerpolicy="no-referrer"
src="https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/icon_%E6%8B%8D%E6%91%84%E7%85%A7%E7%89%87%402x.png"
/>
<text class="text-group_3">选择相册图片</text>
</view>
</view>
<view class="section_9 flex-row" @tap="chooseImage('camera')">
<view class="image-text_8 flex-col">
<image
class="label_2"
referrerpolicy="no-referrer"
src="https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/icon_%E7%9B%B8%E5%86%8C%E7%85%A7%E7%89%87%402x.png"
/>
<text class="text-group_4">拍摄照片</text>
</view>
</view>
</view>
</view>
<!-- 支付弹窗 -->
<view>
<uni-popup ref="share" type="share" safeArea backgroundColor="#fff">
<uni-popup-pay ref="pay" @select="toPay"></uni-popup-pay>
</uni-popup>
</view>
</view>
</template>
<script>
const uploadFile= require('../../utils/uploadToAliOss/uploadAliyun.js');
import { mul } from "@/utils/calculate/calculate.js"
const getNowTime = require('../../utils/timeUtil/timeUtil.js')
let pageNumber = 3;
let startTime = '';
let destroyTime = '';
export default {
data() {
return {
uniMenuInfo: {},
templateSets: [],
templateSet: {},
template: {},
residues: 0,
imgs: '',
templateId: '',
payId: -1,
buyType: 0,
}
},
methods: {
logRecord() {
var _this = this;
destroyTime = getNowTime();
let reqData = {
"startTime": startTime,
"destroyTime": destroyTime,
"pageNumber": pageNumber,
"qrcodeScenicId": uni.getStorageSync('scanQrcodeInfo').scenicId,
"qrcodeMchId": uni.getStorageSync('scanQrcodeInfo').mchId,
"qrcodeId": uni.getStorageSync('scanQrcodeInfo').qrcodeId,
"userId": uni.getStorageSync('userInfo').id,
"appType": 5,
"photoId": _this.template.id,
"mchId": _this.template.mchId,
};
uni.request({
url: _this.java_http_url + '/applet/smart_camera/logRecord',
method: 'POST',
data: reqData,
success(res) {}
})
},
getPayment() {
var _this = this;
uni.showLoading({
title: '加载中',
mask: true
})
uni.request({
url: _this.java_http_url + '/applet/smart_camera/getUserIsPay',
method: 'POST',
data: {
user_id: uni.getStorageSync('userInfo').id,
template_id: _this.templateId
},
success(res) {
console.log('res', res)
if(res.data.data.code == 200) {
_this.residues = res.data.data.residualUse;
_this.payId = res.data.data.payId;
if(_this.residues <= 0 && _this.template.paymentType == 2) {
_this.template.paymentType = 1;
}
}
},
complete() {
uni.hideLoading();
}
})
},
toPay(e) {
uni.showLoading({
title: '请稍候',
mask: true
})
var _this = this;
console.log('支付类型', e)
_this.buyType = e;
let totalFee = e == 2?mul(_this.template.printFee,100):mul(_this.template.fee,100);
let triesLimit = e == 2?1:_this.template.triesLimit;
if(totalFee == 0) {
_this.syntheticImage(_this.imgs);
return
}
let data = {
open_id: uni.getStorageSync('userInfo').openId,
user_id: uni.getStorageSync('userInfo').id,
total_fee: totalFee,
merchant_id: _this.templateSet.mchId,
template_id: _this.template.id,
template_name: _this.template.templateName,
payment_type: _this.template.paymentType,
tries_limit: triesLimit
};
console.log('支付参数', data)
if (e != 0) {
console.log('data', data)
uni.request({
url: _this.pay_http_url + '/wxpay/pay',
method: 'POST',
data: data,
success: res => {
console.log('pay request success', res);
if (res.statusCode !== 200) {
uni.showModal({
content: '支付失败,请重试!',
showCancel: false,
success() {
uni.hideLoading();
}
});
} else {
if (res.data.code == 'pay') {
console.log('res', res.data)
let payId = res.data.order_id;
uni.requestPayment({
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.package,
signType: 'MD5',
paySign: res.data.paySign,
success: res => {
console.log('res', res)
_this.payId = payId;
_this.$refs.share.close();
_this.template.paymentType = e!=2?2:1;
_this.residues = _this.template.triesLimit;
_this.syntheticImage(_this.imgs);
},
fail() {
uni.hideLoading()
}
})
}
}
},
fail() {
uni.hideLoading();
uni.showToast({
title: '支付失败',
icon: "error",
mask: true
})
}
})
}
},
generateCode(length) {
let code = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
code += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return code;
},
getToday() {
var now = new Date();
var year = now.getFullYear();
var month = now.getMonth() + 1;
var day = now.getDate();
month = month.toString().padStart(2, '0');
day = day.toString().padStart(2, '0');
return `${year}-${month}-${day}`;
},
syntheticImage(url) {
var _this = this;
let dir = 'smartcamera/' + _this.getToday() + '/';
var match = url[0].match(/\.([^.]+)$/);
let promise = Promise.all(
url.map((tempFilePath, index) => {
return new Promise(function(resolve, reject) {
uploadFile({
filePath: tempFilePath,
dir: dir,
type: match[0],
success: function(res) {
// console.log('上传成功---', res);
resolve(res);
},
fail: function(res) {
// console.log('上传失败---', res);
uni.hideLoading();
uni.showToast({
title: '上传失败,请重试!',
icon: 'none',
duration: 4000
})
}
});
});
})
).then((res) => {
let code = _this.generateCode(24);
uni.request({
url: _this.java_http_url + '/applet/smart_camera/synthesisAiPhoto',
method: 'POST',
data: {
'code': code,
'templateId': _this.template.id,
'photo': res[0],
'userId': uni.getStorageSync('userInfo').id,
'payId': _this.payId,
'mchId': _this.templateSet.mchId,
'buyType': _this.buyType,
'appletType': 2
},
success: (res) => {
console.log('aiphotoRes', res)
if(res.data.code == 200) {
uni.navigateTo({
url: "../display/display?code=" + code + "&templateId=" + _this.template.id
+ "&mchId=" + _this.template.mchId +"&buyType=" + _this.buyType,
success() {
uni.hideLoading();
}
})
} else {
uni.hideLoading();
uni.showToast({
title: '图片含有多个人脸或无人脸,请重新上传!',
icon: 'none',
duration: 5000
})
}
},
fail(err) {
console.log('err', err)
uni.hideLoading()
}
});
});
},
selectImage(img) {
var _this = this;
uni.showModal({
title: '温馨提示',
content: '是否开始制作',
confirmColor: '#6b99fb',
success: function (res) {
if (res.confirm) {
console.log('用户点击确定');
if((_this.template.paymentType == 1 || _this.template.paymentType == 2) && _this.residues <= 0) {
_this.$refs.pay.setTemplate(_this.template);
_this.$refs.share.open();
_this.imgs = img;
} else {
uni.showLoading({
title: '上传中',
mask: true
});
_this.syntheticImage(img);
}
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
chooseImage(type) {
var _this = this
uni.chooseMedia({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: [type],
camera: 'front',
success: function (res) {
let img = [res.tempFiles[0].tempFilePath];
_this.selectImage(img);
}
});
},
getTemplate(setId, templateId) {
var _this = this;
_this.templateSets.forEach((set, idx)=> {
if(set.id == setId) {
_this.templateSet = set;
set.templates.forEach((tem, idx)=> {
if(tem.id == templateId) {
_this.template = tem;
uni.setStorageSync('aiPhotoTemplate', tem)
}
})
}
})
},
goBack() {
uni.navigateBack({
delta: 1
})
},
},
onLoad(option) {
var _this = this;
_this.uniMenuInfo = uni.getStorageSync('uniMenuInfo');
_this.templateSets = uni.getStorageSync('templateSets');
_this.templateId = option.templateId;
_this.getTemplate(option.templateSetId, option.templateId);
},
onShow() {
if(this.template.paymentType == 1 || this.template.paymentType == 2) {
this.getPayment();
}
startTime = getNowTime();
},
onHide() {
if(uni.getStorageSync('userInfo').id) {
this.logRecord();
}
},
onUnload() {
if(uni.getStorageSync('userInfo').id) {
this.logRecord();
}
},
}
</script>
<style>
@import url("@/css/common.css");
@import url("@/pages/selectPhoto/index.css");
</style>

BIN
static/btn_down_140@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
static/btn_share_140@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

BIN
static/center/user_img2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
static/icon_swiper.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
static/img_video_nor@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

BIN
static/img_video_sel@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

76
uni.scss Normal file
View File

@@ -0,0 +1,76 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;

View File

@@ -0,0 +1,8 @@
## 1.0.32023-06-10
增加插件预览二维码
## 1.0.22023-04-14
增加示例
## 1.0.12023-03-30
优化功能
## 1.0.02023-03-14
初版发布

View File

@@ -0,0 +1,139 @@
<template>
<view>
<view class="progress-bar" :style="{height: height,backgroundColor:dsColor}" v-if="textInside">
<view class="progress" :style="{width: progressWidth,
borderTopLeftRadius: borderRadius, borderBottomLeftRadius: borderRadius}">
</view>
<span class="percentage" :style="{color:color,fontSize:fontSize,left:percentage +'%' }">
{{ percentage }}%
</span>
</view>
<view class="card-item" v-else>
<view class="progress-bar1" :style="{height: height,backgroundColor:dsColor}">
<view class="progress1"
:style="{width: progressWidth, backgroundColor: bgColor,borderRadius: borderRadius}">
</view>
</view>
<view class="percentage1" :style="{color:color,fontSize:fontSize }">{{ percentage }}%</view>
</view>
</view>
</template>
<script>
export default {
props: {
//文字是否内显
textInside: {
type: Boolean,
default: true
},
//当前进度
progress: {
type: Number,
required: true,
validator: (value) => value >= 0 && value <= 100,
default: 50
},
//文字颜色
color: {
type: String,
default: '#FFFFFF'
},
//文字大小
fontSize: {
type: String,
default: '24rpx'
},
//进度条颜色
bgColor: {
type: String,
default: '#5cb85c'
},
//进度条底色颜色
dsColor: {
type: String,
default: '#f2f2f2'
},
//进度条高度
height: {
type: String,
default: '28rpx'
},
//进度条圆角弧度
borderRadius: {
type: String,
default: '8rpx'
}
},
computed: {
progressWidth() {
return `${this.progress}%`
},
percentage() {
return Math.round(this.progress)
}
}
};
</script>
<style scoped>
.progress-bar {
position: relative;
width: 100%;
background-color: #f2f2f2;
border-radius: 16rpx;
}
.percentage {
position: absolute;
height: 150%;
top: 50%;
/* left: 50%; */
color: #666;
transform: translate(-50%, -50%);
font-size: 22rpx;
user-select: none;
z-index: 1;
background-color: #168cf8;
text-align: center;
line-height: 150%;
border-radius: 10px;
width: 12%;
border-color: azure;
border: #ffffff solid 1px;
}
.progress {
position: absolute;
top: 0;
left: 0;
height: 100%;
border: #ffffff solid 1px;
background: linear-gradient(to left, rgba(75, 205, 255, 1.0) 10%, rgba(100, 198, 255, 1.0) 30%, rgba(118, 192, 255, 1.0) 60%);
}
.card-item {
display: flex;
align-items: center;
justify-content: space-between;
}
.progress-bar1 {
position: relative;
width: 90%;
background-color: #f2f2f2;
border-radius: 16rpx;
}
.percentage1 {
color: #666;
font-size: 24rpx;
}
.progress1 {
position: absolute;
top: 0;
left: 0;
height: 100%;
}
</style>

View File

@@ -0,0 +1,6 @@
### 1、本插件可免费下载使用
### 2、未经许可严禁复制本插件派生同类插件上传插件市场
### 3、未经许可严禁在插件市场恶意复制抄袭本插件进行违规获利;
### 4、对本软件的任何使用都必须遵守这些条款违反这些条款的个人或组织将面临法律追究。

View File

@@ -0,0 +1,83 @@
{
"id": "liu-progressbar",
"displayName": "进度条组件",
"version": "1.0.3",
"description": "自定义进度条组件,支持设置文字内显、当前进度、文字颜色、文字大小、进度条颜色、进度条底色、进度条高度、进度条圆角弧度",
"keywords": [
"进度条",
"百分比",
"进度"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "u",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@@ -0,0 +1,44 @@
### liu-progressbar适用于uni-app项目的进度条组件
### 本组件目前兼容微信小程序、H5
### 本组件支持自定义,支持设置文字内显、当前进度、文字颜色、文字大小、进度条颜色、进度条底色、进度条高度、进度条圆角弧度
# --- 扫码预览、关注我们 ---
## 扫码关注公众号,查看更多插件信息,预览插件效果!
![](https://uni.ckapi.pro/uniapp/publicize.png)
### 使用方式
``` html
<view class="title">文字内显</view>
<liu-progressbar :progress="70" color="#FFFFFF" :height="'40rpx'" />
<view class="title">文字外显</view>
<liu-progressbar :textInside="false" :progress="70" color="#333333" :height="'40rpx'" />
<view class="title">自定义高度</view>
<liu-progressbar :progress="70" color="#FFFFFF" :height="'25rpx'" />
<view class="title">自定义圆角弧度</view>
<liu-progressbar :progress="70" color="#FFFFFF" :height="'40rpx'" :borderRadius="'40rpx'" />
<view class="title">自定义进度条颜色</view>
<liu-progressbar :progress="70" bgColor="red" color="#FFFFFF" :height="'40rpx'" />
<view class="title">自定义底色</view>
<liu-progressbar :progress="70" dsColor="red" color="#FFFFFF" :height="'40rpx'" />
```
### 属性说明
| 名称 | 类型 | 默认值 | 描述 |
| ----------------------------|---------------- | ---------------------- | ---------------|
| textInside | Boolean | true | 文字是否内显
| progress | Number | 50 | 当前进度
| color | String | #FFFFFF | 文字颜色
| fontSize | String | 24rpx | 文字大小
| bgColor | String | #5cb85c | 进度条颜色
| dsColor | String | #f2f2f2 | 进度条底色颜色
| height | String | 28rpx | 进度条高度
| borderRadius | String | 8rpx | 进度条圆角弧度

View File

@@ -0,0 +1,84 @@
## 1.9.12024-04-02
- 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法)
## 1.9.02024-03-28
- 修复 uni-popup-dialog 双向绑定时初始化逻辑修正
## 1.8.92024-03-20
- 修复 uni-popup-dialog 数据输入时修正为双向绑定
## 1.8.82024-02-20
- 修复 uni-popup 在微信小程序下出现文字向上闪动的bug
## 1.8.72024-02-02
- 新增 uni-popup-dialog 新增属性focusinput模式下是否自动自动聚焦
## 1.8.62024-01-30
- 新增 uni-popup-dialog 新增属性maxLength:限制输入框字数
## 1.8.52024-01-26
- 新增 uni-popup-dialog 新增属性showClose:控制关闭按钮的显示
## 1.8.42023-11-15
- 新增 uni-popup 支持uni-app-x 注意暂时仅支持 `maskClick` `@open` `@close`
## 1.8.32023-04-17
- 修复 uni-popup 重复打开时的 bug
## 1.8.22023-02-02
- uni-popup-dialog 组件新增 inputType 属性
## 1.8.12022-12-01
- 修复 nvue 下 v-show 报错
## 1.8.02022-11-29
- 优化 主题样式
## 1.7.92022-04-02
- 修复 弹出层内部无法滚动的bug
## 1.7.82022-03-28
- 修复 小程序中高度错误的bug
## 1.7.72022-03-17
- 修复 快速调用open出现问题的Bug
## 1.7.62022-02-14
- 修复 safeArea 属性不能设置为false的bug
## 1.7.52022-01-19
- 修复 isMaskClick 失效的bug
## 1.7.42022-01-19
- 新增 cancelText \ confirmText 属性 ,可自定义文本
- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色
- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题
## 1.7.32022-01-13
- 修复 设置 safeArea 属性不生效的bug
## 1.7.22021-11-26
- 优化 组件示例
## 1.7.12021-11-26
- 修复 vuedoc 文字错误
## 1.7.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup)
## 1.6.22021-08-24
- 新增 支持国际化
## 1.6.12021-07-30
- 优化 vue3下事件警告的问题
## 1.6.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.5.02021-06-23
- 新增 mask-click 遮罩层点击事件
## 1.4.52021-06-22
- 修复 nvue 平台中间弹出后点击内容再点击遮罩无法关闭的Bug
## 1.4.42021-06-18
- 修复 H5平台中间弹出后点击内容再点击遮罩无法关闭的Bug
## 1.4.32021-06-08
- 修复 错误的 watch 字段
- 修复 safeArea 属性不生效的问题
- 修复 点击内容再点击遮罩无法关闭的Bug
## 1.4.22021-05-12
- 新增 组件示例地址
## 1.4.12021-04-29
- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题
## 1.4.0 2021-04-29
- 新增 type 属性的 left\right 值,支持左右弹出
- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗
- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色
- 新增 safeArea 属性,是否适配底部安全区
- 修复 App\h5\微信小程序底部安全区占位不对的Bug
- 修复 App 端弹出等待的Bug
- 优化 提升低配设备性能,优化动画卡顿问题
- 优化 更简单的组件自定义方式
## 1.2.92021-02-05
- 优化 组件引用关系通过uni_modules引用组件
## 1.2.82021-02-05
- 调整为uni_modules目录规范
## 1.2.72021-02-05
- 调整为uni_modules目录规范
- 新增 支持 PC 端
- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端

View File

@@ -0,0 +1,45 @@
// #ifdef H5
export default {
name: 'Keypress',
props: {
disable: {
type: Boolean,
default: false
}
},
mounted () {
const keyNames = {
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
space: [' ', 'Spacebar'],
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete', 'Del']
}
const listener = ($event) => {
if (this.disable) {
return
}
const keyName = Object.keys(keyNames).find(key => {
const keyName = $event.key
const value = keyNames[key]
return value === keyName || (Array.isArray(value) && value.includes(keyName))
})
if (keyName) {
// 避免和其他按键事件冲突
setTimeout(() => {
this.$emit(keyName, {})
}, 0)
}
}
document.addEventListener('keyup', listener)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('keyup', listener)
})
},
render: () => {}
}
// #endif

View File

@@ -0,0 +1,316 @@
<template>
<view class="uni-popup-dialog">
<view class="uni-dialog-title">
<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text>
</view>
<view v-if="mode === 'base'" class="uni-dialog-content">
<slot>
<text class="uni-dialog-content-text">{{content}}</text>
</slot>
</view>
<view v-else class="uni-dialog-content">
<slot>
<input class="uni-dialog-input" :maxlength="maxlength" v-model="val" :type="inputType"
:placeholder="placeholderText" :focus="focus">
</slot>
</view>
<view class="uni-dialog-button-group">
<view class="uni-dialog-button" v-if="showClose" @click="closeDialog">
<text class="uni-dialog-button-text">{{closeText}}</text>
</view>
<view class="uni-dialog-button" :class="showClose?'uni-border-left':''" @click="onOk">
<text class="uni-dialog-button-text uni-button-color">{{okText}}</text>
</view>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
const {
t
} = initVueI18n(messages)
/**
* PopUp 弹出层-对话框样式
* @description 弹出层-对话框样式
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} value input 模式下的默认值
* @property {String} placeholder input 模式下输入提示
* @property {Boolean} focus input模式下是否自动聚焦默认为true
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} mode = [base|input] 模式、
* @value base 基础对话框
* @value input 可输入对话框
* @showClose {Boolean} 是否显示关闭按钮
* @property {String} content 对话框内容
* @property {Boolean} beforeClose 是否拦截取消事件
* @property {Number} maxlength 输入
* @event {Function} confirm 点击确认按钮触发
* @event {Function} close 点击取消按钮触发
*/
export default {
name: "uniPopupDialog",
mixins: [popup],
emits: ['confirm', 'close', 'update:modelValue', 'input'],
props: {
inputType: {
type: String,
default: 'text'
},
showClose: {
type: Boolean,
default: true
},
// #ifdef VUE2
value: {
type: [String, Number],
default: ''
},
// #endif
// #ifdef VUE3
modelValue: {
type: [Number, String],
default: ''
},
// #endif
placeholder: {
type: [String, Number],
default: ''
},
type: {
type: String,
default: 'error'
},
mode: {
type: String,
default: 'base'
},
title: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
beforeClose: {
type: Boolean,
default: false
},
cancelText: {
type: String,
default: ''
},
confirmText: {
type: String,
default: ''
},
maxlength: {
type: Number,
default: -1,
},
focus: {
type: Boolean,
default: true,
}
},
data() {
return {
dialogType: 'error',
val: ""
}
},
computed: {
okText() {
return this.confirmText || t("uni-popup.ok")
},
closeText() {
return this.cancelText || t("uni-popup.cancel")
},
placeholderText() {
return this.placeholder || t("uni-popup.placeholder")
},
titleText() {
return this.title || t("uni-popup.title")
}
},
watch: {
type(val) {
this.dialogType = val
},
mode(val) {
if (val === 'input') {
this.dialogType = 'info'
}
},
value(val) {
if (this.maxlength != -1 && this.mode === 'input') {
this.val = val.slice(0, this.maxlength);
} else {
this.val = val
}
},
val(val) {
// #ifdef VUE2
// TODO 兼容 vue2
this.$emit('input', val);
// #endif
// #ifdef VUE3
// TODO 兼容 vue3
this.$emit('update:modelValue', val);
// #endif
}
},
created() {
// 对话框遮罩不可点击
this.popup.disableMask()
// this.popup.closeMask()
if (this.mode === 'input') {
this.dialogType = 'info'
this.val = this.value;
// #ifdef VUE3
this.val = this.modelValue;
// #endif
} else {
this.dialogType = this.type
}
},
methods: {
/**
* 点击确认按钮
*/
onOk() {
if (this.mode === 'input') {
this.$emit('confirm', this.val)
} else {
this.$emit('confirm')
}
if (this.beforeClose) return
this.popup.close()
},
/**
* 点击取消按钮
*/
closeDialog() {
this.$emit('close')
if (this.beforeClose) return
this.popup.close()
},
close() {
this.popup.close()
}
}
}
</script>
<style lang="scss">
.uni-popup-dialog {
width: 300px;
border-radius: 11px;
background-color: #fff;
}
.uni-dialog-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 25px;
}
.uni-dialog-title-text {
font-size: 16px;
font-weight: 500;
}
.uni-dialog-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
padding: 20px;
}
.uni-dialog-content-text {
font-size: 14px;
color: #6C6C6C;
}
.uni-dialog-button-group {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
border-top-color: #f5f5f5;
border-top-style: solid;
border-top-width: 1px;
}
.uni-dialog-button {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: center;
align-items: center;
height: 45px;
}
.uni-border-left {
border-left-color: #f0f0f0;
border-left-style: solid;
border-left-width: 1px;
}
.uni-dialog-button-text {
font-size: 16px;
color: #333;
}
.uni-button-color {
color: #007aff;
}
.uni-dialog-input {
flex: 1;
font-size: 14px;
border: 1px #eee solid;
height: 40px;
padding: 0 10px;
border-radius: 5px;
color: #555;
}
.uni-popup__success {
color: #4cd964;
}
.uni-popup__warn {
color: #f0ad4e;
}
.uni-popup__error {
color: #dd524d;
}
.uni-popup__info {
color: #909399;
}
</style>

View File

@@ -0,0 +1,143 @@
<template>
<view class="uni-popup-message">
<view class="uni-popup-message__box fixforpc-width" :class="'uni-popup__'+type">
<slot>
<text class="uni-popup-message-text" :class="'uni-popup__'+type+'-text'">{{message}}</text>
</slot>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
/**
* PopUp 弹出层-消息提示
* @description 弹出层-消息提示
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} message 消息提示文字
* @property {String} duration 显示时间,设置为 0 则不会自动关闭
*/
export default {
name: 'uniPopupMessage',
mixins:[popup],
props: {
/**
* 主题 success/warning/info/error 默认 success
*/
type: {
type: String,
default: 'success'
},
/**
* 消息文字
*/
message: {
type: String,
default: ''
},
/**
* 显示时间,设置为 0 则不会自动关闭
*/
duration: {
type: Number,
default: 3000
},
maskShow:{
type:Boolean,
default:false
}
},
data() {
return {}
},
created() {
this.popup.maskShow = this.maskShow
this.popup.messageChild = this
},
methods: {
timerClose(){
if(this.duration === 0) return
clearTimeout(this.timer)
this.timer = setTimeout(()=>{
this.popup.close()
},this.duration)
}
}
}
</script>
<style lang="scss" >
.uni-popup-message {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
}
.uni-popup-message__box {
background-color: #e1f3d8;
padding: 10px 15px;
border-color: #eee;
border-style: solid;
border-width: 1px;
flex: 1;
}
@media screen and (min-width: 500px) {
.fixforpc-width {
margin-top: 20px;
border-radius: 4px;
flex: none;
min-width: 380px;
/* #ifndef APP-NVUE */
max-width: 50%;
/* #endif */
/* #ifdef APP-NVUE */
max-width: 500px;
/* #endif */
}
}
.uni-popup-message-text {
font-size: 14px;
padding: 0;
}
.uni-popup__success {
background-color: #e1f3d8;
}
.uni-popup__success-text {
color: #67C23A;
}
.uni-popup__warn {
background-color: #faecd8;
}
.uni-popup__warn-text {
color: #E6A23C;
}
.uni-popup__error {
background-color: #fde2e2;
}
.uni-popup__error-text {
color: #F56C6C;
}
.uni-popup__info {
background-color: #F2F6FC;
}
.uni-popup__info-text {
color: #909399;
}
</style>

View File

@@ -0,0 +1,279 @@
<template>
<view class="uni-popup-share">
<view class="uni-share-title">
<text class="uni-share-title-text">{{shareTitleText}}</text>
</view>
<view class="uni-share-content">
<view class="money-content">
<view class="pay-money"></view>
<view class="money-size" v-if="selectType == 1">{{ template.fee }}</view>
<view class="money-size" v-if="selectType == 2">{{ template.printFee }}</view>
</view>
<view class="pay-content">
<view class="name">购买商品&nbsp {{ template.templateName }}</view>
</view>
<view class="select-content">
<view class="box_2">
<view class="orders" @tap="menuClick(1)">
<view class="orders-name">{{ template.triesLimit }} 生成次数</view>
<view :class="selectType == 1? 'orders-selected-icon':'perpetual-icon'"></view>
</view>
<view class="perpetual" v-if="template.buyType == 2" @tap="menuClick(2)">
<view class="perpetual-name">{{ template.printTip }}</view>
<view :class="selectType == 2? 'orders-selected-icon':'perpetual-icon'"></view>
</view>
<view class="perpetual" @tap="menuClick(0)">
<view class="perpetual-name">永久使用该模板</view>
<view class="perpetual-icon"></view>
</view>
</view>
</view>
<view class="pay-btn" @tap="payClick()">
确认支付
</view>
</view>
<view class="uni-share-button-box" @click="close">
<view class="uni-share-button" >&times;</view>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
const { t } = initVueI18n(messages)
export default {
name: 'UniPopupShare',
mixins:[popup],
emits:['select'],
props: {
title: {
type: String,
default: '选择支付内容'
},
beforeClose: {
type: Boolean,
default: false
},
},
data() {
return {
template: {},
selectType: 1
}
},
created() {},
computed: {
cancelText() {
return t("uni-popup.cancel")
},
shareTitleText() {
return this.title
}
},
methods: {
menuClick(e){
if(e != 0) {
this.selectType = e;
} else {
uni.showToast({
title: '此模版暂不支持购买此类型!',
icon: 'none',
duration: 2000
})
}
},
setTemplate(data) {
this.template = data;
},
payClick() {
this.$emit('select', this.selectType)
// this.close()
},
/**
* 关闭窗口
*/
close() {
if(this.beforeClose) return
this.popup.close()
}
}
}
</script>
<style lang="scss" >
.uni-popup-share {
background-color: #fff;
border-top-left-radius: 40rpx;
border-top-right-radius: 40rpx;
// height: 750rpx;
}
.uni-share-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
height: 110rpx;
}
.uni-share-title-text {
font-size: 16px;
color: #666;
font-weight: 700;
}
.uni-share-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
// justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}
.uni-share-button-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 15px 15px;
position: absolute;
top: 0;
right: 0;
}
.uni-share-button {
flex: 1;
border-radius: 50px;
color: #666;
font-size: 20px;
}
.uni-share-button::after {
border-radius: 50px;
}
.img_1 {
height: 720rpx;
width: 460rpx;
border-radius: 11px;
}
.model_title {
position: absolute;
top: 750rpx;
left: 170rpx;
color: #fff;
}
.money-content {
display: flex;
flex-wrap: wrap;
.pay-money {
position: relative;
height: 70px;
font-size: 30px;
font-weight: 600;
color: #30334B;
opacity: 1;
line-height: 85px;
}
.money-size {
position: relative;
height: 70px;
font-size: 50px;
font-weight: 600;
color: #30334B;
opacity: 1;
line-height: 70px;
}
}
.select-content {
width: 80%;
// height: 240rpx;
border: rgba(192, 196, 202, 0.7) solid 1px;
margin-top: 30rpx;
border-radius: 20px;
}
.pay-btn {
width: 80%;
height: 100rpx;
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/btn%403x.png)
100% no-repeat;
background-size: 100% 100%;
margin-top: 35rpx;
margin-bottom: 35rpx;
border-radius: 20px;
text-align: center;
line-height: 100rpx;
color: #fff;
}
.box_2 {
width: 85%;
border-radius: 40rpx;
margin-left: auto;
margin-right: auto;
}
.orders{
width: 90%;
height: 120rpx;
border-bottom: 1px solid rgba(192, 196, 202, 0.4);
position: relative;
left: 5%;
}
.orders-name{
text-align: left;
line-height: 120rpx;
font-size: 16px;
color: #333333;
opacity: 1;
}
.perpetual{
width: 90%;
height: 120rpx;
border-bottom: 1px solid rgba(192, 196, 202, 0.4);
position: relative;
left: 5%;
}
.perpetual-name{
text-align: left;
line-height: 120rpx;
font-size: 16px;
color: #333333;
opacity: 1;
}
.orders-icon, .perpetual-icon {
position: absolute;
right: 0px;
width: 35rpx;
top: 40rpx;
height: 35rpx;
background-size: 100% 100%;
background-image: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/unselect_icon.png);
}
.orders-selected-icon, .perpetual-selected-icon {
position: absolute;
right: 0px;
width: 35rpx;
top: 40rpx;
height: 35rpx;
background-size: 100% 100%;
background-image: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/select_icon.png);
}
</style>

View File

@@ -0,0 +1,338 @@
<template>
<view class="uni-popup-share">
<view class="uni-share-title"><text class="uni-share-title-text">{{shareTitleText}}</text></view>
<view class="uni-share-content">
<view class="uni-share-content-box">
<view class="section_11 flex-row justify-between">
<scroll-view scroll-y="true" class="scroll-Y" >
<view class="scroll-view-item" v-for="scenic in scenicData" :key="scenic.id">
<view class="region">
<image class="img_1" :src="'https://res.oss.humengfilms.com/' + scenic.scenicSpotImg" mode="aspectFill" />
<text class="text_8">{{scenic.scenicSpotName}}</text>
<text class="text_9" @tap="select(scenic)">选择</text>
</view>
<view class="tableLine"></view>
</view>
</scroll-view>
</view>
</view>
</view>
<view class="uni-share-button-box" @click="close">
<view class="uni-share-button" >&times;</view>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
const { t } = initVueI18n(messages)
export default {
name: 'UniPopupShare',
mixins:[popup],
emits:['select'],
props: {
title: {
type: String,
default: '选择景区'
},
beforeClose: {
type: Boolean,
default: false
},
},
data() {
return {
scenicData: [],
}
},
created() {},
computed: {
cancelText() {
return t("uni-popup.cancel")
},
shareTitleText() {
return this.title
}
},
methods: {
setScenicData(data) {
this.scenicData = data;
},
/**
* 选择内容
*/
select(item, index) {
this.$emit('select', {
item
})
},
/**
* 关闭窗口
*/
close() {
if(this.beforeClose) return
this.popup.close()
}
}
}
</script>
<style lang="scss" >
.uni-popup-share {
background-color: #fff;
border-top-left-radius: 40rpx;
border-top-right-radius: 40rpx;
height: 900rpx;
}
.uni-share-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
height: 80rpx;
}
.uni-share-title-text {
font-size: 16px;
color: #666;
}
.uni-share-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
height: 95%;
width: 100%;
}
.uni-share-content-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
width: 100%;
height: 100%;
// align-items: center;
justify-content: center;
}
.uni-share-content-item {
width: 90px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
padding: 10px 0;
align-items: center;
}
.uni-share-content-item:active {
background-color: #f5f5f5;
}
.uni-share-image {
width: 30px;
height: 30px;
}
.uni-share-text {
margin-top: 10px;
font-size: 14px;
color: #3B4144;
}
.uni-share-button-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 10px 15px;
position: absolute;
top: 0;
right: 0;
}
.uni-share-button {
flex: 1;
border-radius: 50px;
color: #666;
font-size: 20px;
}
.uni-share-button::after {
border-radius: 50px;
}
.img_1 {
position: relative;
height: 160rpx;
width: 280rpx;
border-radius: 11px;
}
.model_title {
position: absolute;
top: 750rpx;
left: 170rpx;
color: #fff;
}
.section_11 {
position: relative;
align-self: center;
width: 100vw;
height: 95%;
}
.section_8 {
background-color: rgba(244, 249, 253, 1);
border-radius: 10px;
padding: 12px 30px 12px 32px;
margin-right: 20rpx;
height: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.image-text_7 {
}
.label_1 {
width: 40px;
height: 40px;
margin: 0 23px 0 21px;
}
.text-group_3 {
overflow-wrap: break-word;
color: rgba(80, 92, 115, 1);
font-size: 14px;
font-family: PingFangSC-Medium;
font-weight: 500;
text-align: left;
white-space: nowrap;
line-height: 14px;
margin-top: 12px;
}
.section_9 {
background-color: rgba(244, 249, 253, 1);
border-radius: 10px;
padding: 17px 45px 12px 45px;
margin-left: 20rpx;
height: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.image-text_8 {
}
.label_2 {
width: 40px;
height: 30px;
align-self: center;
}
.text-group_4 {
overflow-wrap: break-word;
color: rgba(80, 92, 115, 1);
font-size: 14px;
font-family: PingFangSC-Medium;
font-weight: 500;
text-align: left;
white-space: nowrap;
line-height: 14px;
margin-top: 17px;
}
.scroll-Y {
width: 100vw;
display: flex;
align-items: center;
}
.scroll-view-item {
height: 200rpx;
width: 95%;
line-height: 200rpx;
text-align: center;
font-size: 36rpx;
margin: 20rpx;
display: grid;
align-items: center;
padding-top: 10rpx;
}
.scroll-Y view:last-child {
padding-bottom: 40rpx;
}
.box_11 {
width: 351px;
align-self: center;
margin-top: 12px;
}
.box_3 {
border-radius: 8px;
background-image: url(https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/5d5a5509867b4d69b9e1b388ead39c20_mergeImage.png);
width: 96px;
height: 54px;
border: 1px solid rgba(151, 151, 151, 1);
}
.text_8 {
overflow-wrap: break-word;
color: rgba(80, 92, 115, 1);
font-size: 16px;
font-weight: normal;
text-align: left;
white-space: nowrap;
line-height: 14px;
margin: 0 0 0 30rpx;
width: 200rpx;
}
.text_9 {
overflow-wrap: break-word;
color: rgba(255, 255, 255, 1);
font-size: 14px;
font-weight: normal;
text-align: center;
white-space: nowrap;
line-height: 90rpx;
background: url(https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/btn_%E5%B0%8F%402x.png);
background-size: 100% 100%;
height: 100rpx;
width: 150rpx;
margin-top: 10rpx;
margin-left: 50rpx;
}
.tableLine {
position: relative;
margin: 28rpx 0 0 0;
width: 98%;
height: 1rpx;
text-align: center;
font-size: 16px;
border-top: 1px solid;
border-color: rgba(101, 101, 101, 0.2);
}
.region {
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -0,0 +1,286 @@
<template>
<view class="uni-popup-share">
<view class="uni-share-title"><text class="uni-share-title-text">{{shareTitleText}}</text></view>
<view class="uni-share-content">
<view class="uni-share-content-box">
<image class="img_1" mode="aspectFill" :src="imgUrl" />
<text class="model_title">{{name}}</text>
<view class="section_11 flex-row justify-between">
<view class="section_8 flex-row" @tap="chooseImage('album')">
<view class="image-text_7 flex-col">
<image
class="label_1"
referrerpolicy="no-referrer"
src="https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/icon_%E6%8B%8D%E6%91%84%E7%85%A7%E7%89%87%402x.png"
/>
<text class="text-group_3">选择相册图片</text>
</view>
</view>
<view class="section_9 flex-row" @tap="chooseImage('camera')">
<view class="image-text_8 flex-col">
<image
class="label_2"
referrerpolicy="no-referrer"
src="https://humeng-res.oss-cn-beijing.aliyuncs.com/hm_aigc/applet/icon_%E7%9B%B8%E5%86%8C%E7%85%A7%E7%89%87%402x.png"
/>
<text class="text-group_4">拍摄照片</text>
</view>
</view>
</view>
</view>
</view>
<view class="uni-share-button-box" @click="close">
<view class="uni-share-button" >&times;</view>
</view>
</view>
</template>
<script>
import popup from '../uni-popup/popup.js'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
const { t } = initVueI18n(messages)
export default {
name: 'UniPopupShare',
mixins:[popup],
emits:['select'],
props: {
title: {
type: String,
default: '照片选择'
},
beforeClose: {
type: Boolean,
default: false
},
},
data() {
return {
bottomData: [],
imgUrl: '',
name: ''
}
},
created() {},
computed: {
cancelText() {
return t("uni-popup.cancel")
},
shareTitleText() {
return this.title
}
},
methods: {
setImageAndName(url, name) {
this.imgUrl = url;
this.name = name;
},
/**
* 上传照片
*/
chooseImage(type) {
var _this = this
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: [type],
success: function (res) {
_this.select(res.tempFilePaths);
}
});
},
/**
* 选择内容
*/
select(item, index) {
this.$emit('select', {
item
})
// this.close()
},
/**
* 关闭窗口
*/
close() {
if(this.beforeClose) return
this.popup.close()
}
}
}
</script>
<style lang="scss" >
.uni-popup-share {
background-color: #fff;
border-top-left-radius: 40rpx;
border-top-right-radius: 40rpx;
height: 70vh;
}
.uni-share-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
height: 40px;
}
.uni-share-title-text {
font-size: 16px;
color: #666;
}
.uni-share-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 10px;
height: 100%;
width: 100%;
}
.uni-share-content-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
width: 100%;
height: 100%;
// align-items: center;
justify-content: center;
}
.uni-share-content-item {
width: 90px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
padding: 10px 0;
align-items: center;
}
.uni-share-content-item:active {
background-color: #f5f5f5;
}
.uni-share-image {
width: 30px;
height: 30px;
}
.uni-share-text {
margin-top: 10px;
font-size: 14px;
color: #3B4144;
}
.uni-share-button-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 10px 15px;
position: absolute;
top: 0;
right: 0;
}
.uni-share-button {
flex: 1;
border-radius: 50px;
color: #666;
font-size: 20px;
}
.uni-share-button::after {
border-radius: 50px;
}
.img_1 {
height: 720rpx;
width: 460rpx;
border-radius: 11px;
}
.model_title {
position: absolute;
top: 750rpx;
left: 170rpx;
color: #fff;
}
.section_11 {
position: relative;
align-self: center;
margin-top: -300rpx;
}
.section_8 {
background-color: rgba(244, 249, 253, 1);
border-radius: 10px;
padding: 12px 30px 12px 32px;
margin-right: 20rpx;
height: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.image-text_7 {
}
.label_1 {
width: 40px;
height: 40px;
margin: 0 23px 0 21px;
}
.text-group_3 {
overflow-wrap: break-word;
color: rgba(80, 92, 115, 1);
font-size: 14px;
font-family: PingFangSC-Medium;
font-weight: 500;
text-align: left;
white-space: nowrap;
line-height: 14px;
margin-top: 12px;
}
.section_9 {
background-color: rgba(244, 249, 253, 1);
border-radius: 10px;
padding: 17px 45px 12px 45px;
margin-left: 20rpx;
height: 200rpx;
display: flex;
justify-content: center;
align-items: center;
}
.image-text_8 {
}
.label_2 {
width: 40px;
height: 30px;
align-self: center;
}
.text-group_4 {
overflow-wrap: break-word;
color: rgba(80, 92, 115, 1);
font-size: 14px;
font-family: PingFangSC-Medium;
font-weight: 500;
text-align: left;
white-space: nowrap;
line-height: 14px;
margin-top: 17px;
}
</style>

View File

@@ -0,0 +1,7 @@
{
"uni-popup.cancel": "cancel",
"uni-popup.ok": "ok",
"uni-popup.placeholder": "pleace enter",
"uni-popup.title": "Hint",
"uni-popup.shareTitle": "Share to"
}

View File

@@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

View File

@@ -0,0 +1,7 @@
{
"uni-popup.cancel": "取消",
"uni-popup.ok": "确定",
"uni-popup.placeholder": "请输入",
"uni-popup.title": "提示",
"uni-popup.shareTitle": "分享到"
}

View File

@@ -0,0 +1,7 @@
{
"uni-popup.cancel": "取消",
"uni-popup.ok": "確定",
"uni-popup.placeholder": "請輸入",
"uni-popup.title": "提示",
"uni-popup.shareTitle": "分享到"
}

View File

@@ -0,0 +1,45 @@
// #ifdef H5
export default {
name: 'Keypress',
props: {
disable: {
type: Boolean,
default: false
}
},
mounted () {
const keyNames = {
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
space: [' ', 'Spacebar'],
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete', 'Del']
}
const listener = ($event) => {
if (this.disable) {
return
}
const keyName = Object.keys(keyNames).find(key => {
const keyName = $event.key
const value = keyNames[key]
return value === keyName || (Array.isArray(value) && value.includes(keyName))
})
if (keyName) {
// 避免和其他按键事件冲突
setTimeout(() => {
this.$emit(keyName, {})
}, 0)
}
}
document.addEventListener('keyup', listener)
// this.$once('hook:beforeDestroy', () => {
// document.removeEventListener('keyup', listener)
// })
},
render: () => {}
}
// #endif

View File

@@ -0,0 +1,26 @@
export default {
data() {
return {
}
},
created(){
this.popup = this.getParent()
},
methods:{
/**
* 获取父元素实例
*/
getParent(name = 'uniPopup') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
}
}

View File

@@ -0,0 +1,90 @@
<template>
<view class="popup-root" v-if="isOpen" v-show="isShow" @click="clickMask">
<view @click.stop>
<slot></slot>
</view>
</view>
</template>
<script>
type CloseCallBack = ()=> void;
let closeCallBack:CloseCallBack = () :void => {};
export default {
emits:["close","clickMask"],
data() {
return {
isShow:false,
isOpen:false
}
},
props: {
maskClick: {
type: Boolean,
default: true
},
},
watch: {
// 设置show = true 时,如果没有 open 需要设置为 open
isShow:{
handler(isShow) {
// console.log("isShow",isShow)
if(isShow && this.isOpen == false){
this.isOpen = true
}
},
immediate:true
},
// 设置isOpen = true 时,如果没有 isShow 需要设置为 isShow
isOpen:{
handler(isOpen) {
// console.log("isOpen",isOpen)
if(isOpen && this.isShow == false){
this.isShow = true
}
},
immediate:true
}
},
methods:{
open(){
// ...funs : CloseCallBack[]
// if(funs.length > 0){
// closeCallBack = funs[0]
// }
this.isOpen = true;
},
clickMask(){
if(this.maskClick == true){
this.$emit('clickMask')
this.close()
}
},
close(): void{
this.isOpen = false;
this.$emit('close')
closeCallBack()
},
hiden(){
this.isShow = false
},
show(){
this.isShow = true
}
}
}
</script>
<style>
.popup-root {
position: fixed;
top: 0;
left: 0;
width: 750rpx;
height: 100%;
flex: 1;
background-color: rgba(0, 0, 0, 0.3);
justify-content: center;
align-items: center;
z-index: 99;
}
</style>

View File

@@ -0,0 +1,504 @@
<template>
<view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']">
<view @touchstart="touchstart">
<uni-transition key="1" v-if="maskShow" name="mask" mode-class="fade" :styles="maskClass"
:duration="duration" :show="showTrans" @click="onTap" />
<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration"
:show="showTrans" @click="onTap">
<view class="uni-popup__wrapper" :style="getStyles" :class="[popupstyle]" @click="clear">
<slot />
</view>
</uni-transition>
</view>
<!-- #ifdef H5 -->
<keypress v-if="maskShow" @esc="onTap" />
<!-- #endif -->
</view>
</template>
<script>
// #ifdef H5
import keypress from './keypress.js'
// #endif
/**
* PopUp 弹出层
* @description 弹出层组件,为了解决遮罩弹层的问题
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
* @value top 顶部弹出
* @value center 中间弹出
* @value bottom 底部弹出
* @value left 左侧弹出
* @value right 右侧弹出
* @value message 消息提示
* @value dialog 对话框
* @value share 底部分享示例
* @property {Boolean} animation = [true|false] 是否开启动画
* @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃)
* @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
* @property {String} backgroundColor 主窗口背景色
* @property {String} maskBackgroundColor 蒙版颜色
* @property {String} borderRadius 设置圆角(左上、右上、右下和左下) 示例:"10px 10px 10px 10px"
* @property {Boolean} safeArea 是否适配底部安全区
* @event {Function} change 打开关闭弹窗触发e={show: false}
* @event {Function} maskClick 点击遮罩触发
*/
export default {
name: 'uniPopup',
components: {
// #ifdef H5
keypress
// #endif
},
emits: ['change', 'maskClick'],
props: {
// 开启动画
animation: {
type: Boolean,
default: true
},
// 弹出层类型可选值top: 顶部弹出层bottom底部弹出层center全屏弹出层
// message: 消息提示 ; dialog : 对话框
type: {
type: String,
default: 'center'
},
// maskClick
isMaskClick: {
type: Boolean,
default: null
},
// TODO 2 个版本后废弃属性 ,使用 isMaskClick
maskClick: {
type: Boolean,
default: null
},
backgroundColor: {
type: String,
default: 'none'
},
safeArea: {
type: Boolean,
default: true
},
maskBackgroundColor: {
type: String,
default: 'rgba(0, 0, 0, 0.4)'
},
borderRadius:{
type: String,
default: '40rpx 40rpx 0 0'
}
},
watch: {
/**
* 监听type类型
*/
type: {
handler: function(type) {
if (!this.config[type]) return
this[this.config[type]](true)
},
immediate: true
},
isDesktop: {
handler: function(newVal) {
if (!this.config[newVal]) return
this[this.config[this.type]](true)
},
immediate: true
},
/**
* 监听遮罩是否可点击
* @param {Object} val
*/
maskClick: {
handler: function(val) {
this.mkclick = val
},
immediate: true
},
isMaskClick: {
handler: function(val) {
this.mkclick = val
},
immediate: true
},
// H5 下禁止底部滚动
showPopup(show) {
// #ifdef H5
// fix by mehaotian 处理 h5 滚动穿透的问题
document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible'
// #endif
}
},
data() {
return {
duration: 300,
ani: [],
showPopup: false,
showTrans: false,
popupWidth: 0,
popupHeight: 0,
config: {
top: 'top',
bottom: 'bottom',
center: 'center',
left: 'left',
right: 'right',
message: 'top',
dialog: 'center',
share: 'bottom'
},
maskClass: {
position: 'fixed',
bottom: 0,
top: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0, 0, 0, 0.4)'
},
transClass: {
backgroundColor: 'transparent',
borderRadius: this.borderRadius || "0",
position: 'fixed',
left: 0,
right: 0
},
maskShow: true,
mkclick: true,
popupstyle: 'top'
}
},
computed: {
getStyles() {
let res = { backgroundColor: this.bg };
if (this.borderRadius || "0") {
res = Object.assign(res, { borderRadius: this.borderRadius })
}
return res;
},
isDesktop() {
return this.popupWidth >= 500 && this.popupHeight >= 500
},
bg() {
if (this.backgroundColor === '' || this.backgroundColor === 'none') {
return 'transparent'
}
return this.backgroundColor
}
},
mounted() {
const fixSize = () => {
const {
windowWidth,
windowHeight,
windowTop,
safeArea,
screenHeight,
safeAreaInsets
} = uni.getSystemInfoSync()
this.popupWidth = windowWidth
this.popupHeight = windowHeight + (windowTop || 0)
// TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复
if (safeArea && this.safeArea) {
// #ifdef MP-WEIXIN
this.safeAreaInsets = screenHeight - safeArea.bottom
// #endif
// #ifndef MP-WEIXIN
this.safeAreaInsets = safeAreaInsets.bottom
// #endif
} else {
this.safeAreaInsets = 0
}
}
fixSize()
// #ifdef H5
// window.addEventListener('resize', fixSize)
// this.$once('hook:beforeDestroy', () => {
// window.removeEventListener('resize', fixSize)
// })
// #endif
},
// #ifndef VUE3
// TODO vue2
destroyed() {
this.setH5Visible()
},
// #endif
// #ifdef VUE3
// TODO vue3
unmounted() {
this.setH5Visible()
},
// #endif
activated() {
this.setH5Visible(!this.showPopup);
},
deactivated() {
this.setH5Visible(true);
},
created() {
// this.mkclick = this.isMaskClick || this.maskClick
if (this.isMaskClick === null && this.maskClick === null) {
this.mkclick = true
} else {
this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick
}
if (this.animation) {
this.duration = 300
} else {
this.duration = 0
}
// TODO 处理 message 组件生命周期异常的问题
this.messageChild = null
// TODO 解决头条冒泡的问题
this.clearPropagation = false
this.maskClass.backgroundColor = this.maskBackgroundColor
},
methods: {
setH5Visible(visible = true) {
// #ifdef H5
// fix by mehaotian 处理 h5 滚动穿透的问题
document.getElementsByTagName('body')[0].style.overflow = visible ? "visible" : "hidden";
// #endif
},
/**
* 公用方法,不显示遮罩层
*/
closeMask() {
this.maskShow = false
},
/**
* 公用方法,遮罩层禁止点击
*/
disableMask() {
this.mkclick = false
},
// TODO nvue 取消冒泡
clear(e) {
// #ifndef APP-NVUE
e.stopPropagation()
// #endif
this.clearPropagation = true
},
open(direction) {
// fix by mehaotian 处理快速打开关闭的情况
if (this.showPopup) {
return
}
let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
if (!(direction && innerType.indexOf(direction) !== -1)) {
direction = this.type
}
if (!this.config[direction]) {
console.error('缺少类型:', direction)
return
}
this[this.config[direction]]()
this.$emit('change', {
show: true,
type: direction
})
},
close(type) {
this.showTrans = false
this.$emit('change', {
show: false,
type: this.type
})
clearTimeout(this.timer)
// // 自定义关闭事件
// this.customOpen && this.customClose()
this.timer = setTimeout(() => {
this.showPopup = false
}, 300)
},
// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
touchstart() {
this.clearPropagation = false
},
onTap() {
if (this.clearPropagation) {
// fix by mehaotian 兼容 nvue
this.clearPropagation = false
return
}
this.$emit('maskClick')
if (!this.mkclick) return
this.close()
},
/**
* 顶部弹出样式处理
*/
top(type) {
this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top'
this.ani = ['slide-top']
this.transClass = {
position: 'fixed',
left: 0,
right: 0,
backgroundColor: this.bg,
borderRadius:this.borderRadius || "0"
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
this.$nextTick(() => {
if (this.messageChild && this.type === 'message') {
this.messageChild.timerClose()
}
})
},
/**
* 底部弹出样式处理
*/
bottom(type) {
this.popupstyle = 'bottom'
this.ani = ['slide-bottom']
this.transClass = {
position: 'fixed',
left: 0,
right: 0,
bottom: 0,
paddingBottom: this.safeAreaInsets + 'px',
backgroundColor: this.bg,
borderRadius:this.borderRadius || "0",
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
},
/**
* 中间弹出样式处理
*/
center(type) {
this.popupstyle = 'center'
//微信小程序下,组合动画会出现文字向上闪动问题,再此做特殊处理
// #ifdef MP-WEIXIN
this.ani = ['fade']
// #endif
// #ifndef MP-WEIXIN
this.ani = ['zoom-out', 'fade']
// #endif
this.transClass = {
position: 'fixed',
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column',
/* #endif */
bottom: 0,
left: 0,
right: 0,
top: 0,
justifyContent: 'center',
alignItems: 'center',
borderRadius:this.borderRadius || "0"
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
},
left(type) {
this.popupstyle = 'left'
this.ani = ['slide-left']
this.transClass = {
position: 'fixed',
left: 0,
bottom: 0,
top: 0,
backgroundColor: this.bg,
borderRadius:this.borderRadius || "0",
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column'
/* #endif */
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
},
right(type) {
this.popupstyle = 'right'
this.ani = ['slide-right']
this.transClass = {
position: 'fixed',
bottom: 0,
right: 0,
top: 0,
backgroundColor: this.bg,
borderRadius:this.borderRadius || "0",
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column'
/* #endif */
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
}
}
}
</script>
<style lang="scss">
.uni-popup {
position: fixed;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
&.top,
&.left,
&.right {
/* #ifdef H5 */
top: var(--window-top);
/* #endif */
/* #ifndef H5 */
top: 0;
/* #endif */
}
.uni-popup__wrapper {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: relative;
/* iphonex 等安全区设置,底部安全区适配 */
/* #ifndef APP-NVUE */
// padding-bottom: constant(safe-area-inset-bottom);
// padding-bottom: env(safe-area-inset-bottom);
/* #endif */
&.left,
&.right {
/* #ifdef H5 */
padding-top: var(--window-top);
/* #endif */
/* #ifndef H5 */
padding-top: 0;
/* #endif */
flex: 1;
}
}
}
.fixforpc-z-index {
/* #ifndef APP-NVUE */
z-index: 999;
/* #endif */
}
.fixforpc-top {
top: 0;
}
</style>

View File

@@ -0,0 +1,88 @@
{
"id": "uni-popup",
"displayName": "uni-popup 弹出层",
"version": "1.9.1",
"description": " Popup 组件,提供常用的弹层",
"keywords": [
"uni-ui",
"弹出层",
"弹窗",
"popup",
"弹框"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-transition"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
## Popup 弹出层
> **组件名uni-popup**
> 代码块: `uPopup`
> 关联组件:`uni-transition`
弹出层组件,在应用中弹出一个消息提示窗口、提示框等
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@@ -0,0 +1,8 @@
## 1.0.32022-01-21
- 优化 组件示例
## 1.0.22021-11-22
- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题
## 1.0.12021-11-22
- 修复 vue3中scss语法兼容问题
## 1.0.02021-11-18
- init

View File

@@ -0,0 +1 @@
@import './styles/index.scss';

View File

@@ -0,0 +1,82 @@
{
"id": "uni-scss",
"displayName": "uni-scss 辅助样式",
"version": "1.0.3",
"description": "uni-sass是uni-ui提供的一套全局样式 通过一些简单的类名和sass变量实现简单的页面布局操作比如颜色、边距、圆角等。",
"keywords": [
"uni-scss",
"uni-ui",
"辅助样式"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"JS SDK",
"通用 SDK"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "n",
"联盟": "n"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@@ -0,0 +1,4 @@
`uni-sass``uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@@ -0,0 +1,7 @@
@import './setting/_variables.scss';
@import './setting/_border.scss';
@import './setting/_color.scss';
@import './setting/_space.scss';
@import './setting/_radius.scss';
@import './setting/_text.scss';
@import './setting/_styles.scss';

View File

@@ -0,0 +1,3 @@
.uni-border {
border: 1px $uni-border-1 solid;
}

View File

@@ -0,0 +1,66 @@
// TODO 暂时不需要 class ,需要用户使用变量实现 ,如果使用类名其实并不推荐
// @mixin get-styles($k,$c) {
// @if $k == size or $k == weight{
// font-#{$k}:#{$c}
// }@else{
// #{$k}:#{$c}
// }
// }
$uni-ui-color:(
// 主色
primary: $uni-primary,
primary-disable: $uni-primary-disable,
primary-light: $uni-primary-light,
// 辅助色
success: $uni-success,
success-disable: $uni-success-disable,
success-light: $uni-success-light,
warning: $uni-warning,
warning-disable: $uni-warning-disable,
warning-light: $uni-warning-light,
error: $uni-error,
error-disable: $uni-error-disable,
error-light: $uni-error-light,
info: $uni-info,
info-disable: $uni-info-disable,
info-light: $uni-info-light,
// 中性色
main-color: $uni-main-color,
base-color: $uni-base-color,
secondary-color: $uni-secondary-color,
extra-color: $uni-extra-color,
// 背景色
bg-color: $uni-bg-color,
// 边框颜色
border-1: $uni-border-1,
border-2: $uni-border-2,
border-3: $uni-border-3,
border-4: $uni-border-4,
// 黑色
black:$uni-black,
// 白色
white:$uni-white,
// 透明
transparent:$uni-transparent
) !default;
@each $key, $child in $uni-ui-color {
.uni-#{"" + $key} {
color: $child;
}
.uni-#{"" + $key}-bg {
background-color: $child;
}
}
.uni-shadow-sm {
box-shadow: $uni-shadow-sm;
}
.uni-shadow-base {
box-shadow: $uni-shadow-base;
}
.uni-shadow-lg {
box-shadow: $uni-shadow-lg;
}
.uni-mask {
background-color:$uni-mask;
}

View File

@@ -0,0 +1,55 @@
@mixin radius($r,$d:null ,$important: false){
$radius-value:map-get($uni-radius, $r) if($important, !important, null);
// Key exists within the $uni-radius variable
@if (map-has-key($uni-radius, $r) and $d){
@if $d == t {
border-top-left-radius:$radius-value;
border-top-right-radius:$radius-value;
}@else if $d == r {
border-top-right-radius:$radius-value;
border-bottom-right-radius:$radius-value;
}@else if $d == b {
border-bottom-left-radius:$radius-value;
border-bottom-right-radius:$radius-value;
}@else if $d == l {
border-top-left-radius:$radius-value;
border-bottom-left-radius:$radius-value;
}@else if $d == tl {
border-top-left-radius:$radius-value;
}@else if $d == tr {
border-top-right-radius:$radius-value;
}@else if $d == br {
border-bottom-right-radius:$radius-value;
}@else if $d == bl {
border-bottom-left-radius:$radius-value;
}
}@else{
border-radius:$radius-value;
}
}
@each $key, $child in $uni-radius {
@if($key){
.uni-radius-#{"" + $key} {
@include radius($key)
}
}@else{
.uni-radius {
@include radius($key)
}
}
}
@each $direction in t, r, b, l,tl, tr, br, bl {
@each $key, $child in $uni-radius {
@if($key){
.uni-radius-#{"" + $direction}-#{"" + $key} {
@include radius($key,$direction,false)
}
}@else{
.uni-radius-#{$direction} {
@include radius($key,$direction,false)
}
}
}
}

View File

@@ -0,0 +1,56 @@
@mixin fn($space,$direction,$size,$n) {
@if $n {
#{$space}-#{$direction}: #{$size*$uni-space-root}px
} @else {
#{$space}-#{$direction}: #{-$size*$uni-space-root}px
}
}
@mixin get-styles($direction,$i,$space,$n){
@if $direction == t {
@include fn($space, top,$i,$n);
}
@if $direction == r {
@include fn($space, right,$i,$n);
}
@if $direction == b {
@include fn($space, bottom,$i,$n);
}
@if $direction == l {
@include fn($space, left,$i,$n);
}
@if $direction == x {
@include fn($space, left,$i,$n);
@include fn($space, right,$i,$n);
}
@if $direction == y {
@include fn($space, top,$i,$n);
@include fn($space, bottom,$i,$n);
}
@if $direction == a {
@if $n {
#{$space}:#{$i*$uni-space-root}px;
} @else {
#{$space}:#{-$i*$uni-space-root}px;
}
}
}
@each $orientation in m,p {
$space: margin;
@if $orientation == m {
$space: margin;
} @else {
$space: padding;
}
@for $i from 0 through 16 {
@each $direction in t, r, b, l, x, y, a {
.uni-#{$orientation}#{$direction}-#{$i} {
@include get-styles($direction,$i,$space,true);
}
.uni-#{$orientation}#{$direction}-n#{$i} {
@include get-styles($direction,$i,$space,false);
}
}
}
}

View File

@@ -0,0 +1,167 @@
/* #ifndef APP-NVUE */
$-color-white:#fff;
$-color-black:#000;
@mixin base-style($color) {
color: #fff;
background-color: $color;
border-color: mix($-color-black, $color, 8%);
&:not([hover-class]):active {
background: mix($-color-black, $color, 10%);
border-color: mix($-color-black, $color, 20%);
color: $-color-white;
outline: none;
}
}
@mixin is-color($color) {
@include base-style($color);
&[loading] {
@include base-style($color);
&::before {
margin-right:5px;
}
}
&[disabled] {
&,
&[loading],
&:not([hover-class]):active {
color: $-color-white;
border-color: mix(darken($color,10%), $-color-white);
background-color: mix($color, $-color-white);
}
}
}
@mixin base-plain-style($color) {
color:$color;
background-color: mix($-color-white, $color, 90%);
border-color: mix($-color-white, $color, 70%);
&:not([hover-class]):active {
background: mix($-color-white, $color, 80%);
color: $color;
outline: none;
border-color: mix($-color-white, $color, 50%);
}
}
@mixin is-plain($color){
&[plain] {
@include base-plain-style($color);
&[loading] {
@include base-plain-style($color);
&::before {
margin-right:5px;
}
}
&[disabled] {
&,
&:active {
color: mix($-color-white, $color, 40%);
background-color: mix($-color-white, $color, 90%);
border-color: mix($-color-white, $color, 80%);
}
}
}
}
.uni-btn {
margin: 5px;
color: #393939;
border:1px solid #ccc;
font-size: 16px;
font-weight: 200;
background-color: #F9F9F9;
// TODO 暂时处理边框隐藏一边的问题
overflow: visible;
&::after{
border: none;
}
&:not([type]),&[type=default] {
color: #999;
&[loading] {
background: none;
&::before {
margin-right:5px;
}
}
&[disabled]{
color: mix($-color-white, #999, 60%);
&,
&[loading],
&:active {
color: mix($-color-white, #999, 60%);
background-color: mix($-color-white,$-color-black , 98%);
border-color: mix($-color-white, #999, 85%);
}
}
&[plain] {
color: #999;
background: none;
border-color: $uni-border-1;
&:not([hover-class]):active {
background: none;
color: mix($-color-white, $-color-black, 80%);
border-color: mix($-color-white, $-color-black, 90%);
outline: none;
}
&[disabled]{
&,
&[loading],
&:active {
background: none;
color: mix($-color-white, #999, 60%);
border-color: mix($-color-white, #999, 85%);
}
}
}
}
&:not([hover-class]):active {
color: mix($-color-white, $-color-black, 50%);
}
&[size=mini] {
font-size: 16px;
font-weight: 200;
border-radius: 8px;
}
&.uni-btn-small {
font-size: 14px;
}
&.uni-btn-mini {
font-size: 12px;
}
&.uni-btn-radius {
border-radius: 999px;
}
&[type=primary] {
@include is-color($uni-primary);
@include is-plain($uni-primary)
}
&[type=success] {
@include is-color($uni-success);
@include is-plain($uni-success)
}
&[type=error] {
@include is-color($uni-error);
@include is-plain($uni-error)
}
&[type=warning] {
@include is-color($uni-warning);
@include is-plain($uni-warning)
}
&[type=info] {
@include is-color($uni-info);
@include is-plain($uni-info)
}
}
/* #endif */

View File

@@ -0,0 +1,24 @@
@mixin get-styles($k,$c) {
@if $k == size or $k == weight{
font-#{$k}:#{$c}
}@else{
#{$k}:#{$c}
}
}
@each $key, $child in $uni-headings {
/* #ifndef APP-NVUE */
.uni-#{$key} {
@each $k, $c in $child {
@include get-styles($k,$c)
}
}
/* #endif */
/* #ifdef APP-NVUE */
.container .uni-#{$key} {
@each $k, $c in $child {
@include get-styles($k,$c)
}
}
/* #endif */
}

View File

@@ -0,0 +1,146 @@
// @use "sass:math";
@import '../tools/functions.scss';
// 间距基础倍数
$uni-space-root: 2 !default;
// 边框半径默认值
$uni-radius-root:5px !default;
$uni-radius: () !default;
// 边框半径断点
$uni-radius: map-deep-merge(
(
0: 0,
// TODO 当前版本暂时不支持 sm 属性
// 'sm': math.div($uni-radius-root, 2),
null: $uni-radius-root,
'lg': $uni-radius-root * 2,
'xl': $uni-radius-root * 6,
'pill': 9999px,
'circle': 50%
),
$uni-radius
);
// 字体家族
$body-font-family: 'Roboto', sans-serif !default;
// 文本
$heading-font-family: $body-font-family !default;
$uni-headings: () !default;
$letterSpacing: -0.01562em;
$uni-headings: map-deep-merge(
(
'h1': (
size: 32px,
weight: 300,
line-height: 50px,
// letter-spacing:-0.01562em
),
'h2': (
size: 28px,
weight: 300,
line-height: 40px,
// letter-spacing: -0.00833em
),
'h3': (
size: 24px,
weight: 400,
line-height: 32px,
// letter-spacing: normal
),
'h4': (
size: 20px,
weight: 400,
line-height: 30px,
// letter-spacing: 0.00735em
),
'h5': (
size: 16px,
weight: 400,
line-height: 24px,
// letter-spacing: normal
),
'h6': (
size: 14px,
weight: 500,
line-height: 18px,
// letter-spacing: 0.0125em
),
'subtitle': (
size: 12px,
weight: 400,
line-height: 20px,
// letter-spacing: 0.00937em
),
'body': (
font-size: 14px,
font-weight: 400,
line-height: 22px,
// letter-spacing: 0.03125em
),
'caption': (
'size': 12px,
'weight': 400,
'line-height': 20px,
// 'letter-spacing': 0.03333em,
// 'text-transform': false
)
),
$uni-headings
);
// 主色
$uni-primary: #2979ff !default;
$uni-primary-disable:lighten($uni-primary,20%) !default;
$uni-primary-light: lighten($uni-primary,25%) !default;
// 辅助色
// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
$uni-success: #18bc37 !default;
$uni-success-disable:lighten($uni-success,20%) !default;
$uni-success-light: lighten($uni-success,25%) !default;
$uni-warning: #f3a73f !default;
$uni-warning-disable:lighten($uni-warning,20%) !default;
$uni-warning-light: lighten($uni-warning,25%) !default;
$uni-error: #e43d33 !default;
$uni-error-disable:lighten($uni-error,20%) !default;
$uni-error-light: lighten($uni-error,25%) !default;
$uni-info: #8f939c !default;
$uni-info-disable:lighten($uni-info,20%) !default;
$uni-info-light: lighten($uni-info,25%) !default;
// 中性色
// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
$uni-main-color: #3a3a3a !default; // 主要文字
$uni-base-color: #6a6a6a !default; // 常规文字
$uni-secondary-color: #909399 !default; // 次要文字
$uni-extra-color: #c7c7c7 !default; // 辅助说明
// 边框颜色
$uni-border-1: #F0F0F0 !default;
$uni-border-2: #EDEDED !default;
$uni-border-3: #DCDCDC !default;
$uni-border-4: #B9B9B9 !default;
// 常规色
$uni-black: #000000 !default;
$uni-white: #ffffff !default;
$uni-transparent: rgba($color: #000000, $alpha: 0) !default;
// 背景色
$uni-bg-color: #f7f7f7 !default;
/* 水平间距 */
$uni-spacing-sm: 8px !default;
$uni-spacing-base: 15px !default;
$uni-spacing-lg: 30px !default;
// 阴影
$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5) !default;
$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default;
$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5) !default;
// 蒙版
$uni-mask: rgba($color: #000000, $alpha: 0.4) !default;

View File

@@ -0,0 +1,19 @@
// 合并 map
@function map-deep-merge($parent-map, $child-map){
$result: $parent-map;
@each $key, $child in $child-map {
$parent-has-key: map-has-key($result, $key);
$parent-value: map-get($result, $key);
$parent-type: type-of($parent-value);
$child-type: type-of($child);
$parent-is-map: $parent-type == map;
$child-is-map: $child-type == map;
@if (not $parent-has-key) or ($parent-type != $child-type) or (not ($parent-is-map and $child-is-map)){
$result: map-merge($result, ( $key: $child ));
}@else {
$result: map-merge($result, ( $key: map-deep-merge($parent-value, $child) ));
}
}
@return $result;
};

View File

@@ -0,0 +1,31 @@
// 间距基础倍数
$uni-space-root: 2;
// 边框半径默认值
$uni-radius-root:5px;
// 主色
$uni-primary: #2979ff;
// 辅助色
$uni-success: #4cd964;
// 警告色
$uni-warning: #f0ad4e;
// 错误色
$uni-error: #dd524d;
// 描述色
$uni-info: #909399;
// 中性色
$uni-main-color: #303133;
$uni-base-color: #606266;
$uni-secondary-color: #909399;
$uni-extra-color: #C0C4CC;
// 背景色
$uni-bg-color: #f5f5f5;
// 边框颜色
$uni-border-1: #DCDFE6;
$uni-border-2: #E4E7ED;
$uni-border-3: #EBEEF5;
$uni-border-4: #F2F6FC;
// 常规色
$uni-black: #000000;
$uni-white: #ffffff;
$uni-transparent: rgba($color: #000000, $alpha: 0);

View File

@@ -0,0 +1,62 @@
@import './styles/setting/_variables.scss';
// 间距基础倍数
$uni-space-root: 2;
// 边框半径默认值
$uni-radius-root:5px;
// 主色
$uni-primary: #2979ff;
$uni-primary-disable:mix(#fff,$uni-primary,50%);
$uni-primary-light: mix(#fff,$uni-primary,80%);
// 辅助色
// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。
$uni-success: #18bc37;
$uni-success-disable:mix(#fff,$uni-success,50%);
$uni-success-light: mix(#fff,$uni-success,80%);
$uni-warning: #f3a73f;
$uni-warning-disable:mix(#fff,$uni-warning,50%);
$uni-warning-light: mix(#fff,$uni-warning,80%);
$uni-error: #e43d33;
$uni-error-disable:mix(#fff,$uni-error,50%);
$uni-error-light: mix(#fff,$uni-error,80%);
$uni-info: #8f939c;
$uni-info-disable:mix(#fff,$uni-info,50%);
$uni-info-light: mix(#fff,$uni-info,80%);
// 中性色
// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。
$uni-main-color: #3a3a3a; // 主要文字
$uni-base-color: #6a6a6a; // 常规文字
$uni-secondary-color: #909399; // 次要文字
$uni-extra-color: #c7c7c7; // 辅助说明
// 边框颜色
$uni-border-1: #F0F0F0;
$uni-border-2: #EDEDED;
$uni-border-3: #DCDCDC;
$uni-border-4: #B9B9B9;
// 常规色
$uni-black: #000000;
$uni-white: #ffffff;
$uni-transparent: rgba($color: #000000, $alpha: 0);
// 背景色
$uni-bg-color: #f7f7f7;
/* 水平间距 */
$uni-spacing-sm: 8px;
$uni-spacing-base: 15px;
$uni-spacing-lg: 30px;
// 阴影
$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5);
$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2);
$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5);
// 蒙版
$uni-mask: rgba($color: #000000, $alpha: 0.4);

View File

@@ -0,0 +1,24 @@
## 1.3.32024-04-23
- 修复 当元素会受变量影响自动隐藏的bug
## 1.3.22023-05-04
- 修复 NVUE 平台报错的问题
## 1.3.12021-11-23
- 修复 init 方法初始化问题
## 1.3.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-transition](https://uniapp.dcloud.io/component/uniui/uni-transition)
## 1.2.12021-09-27
- 修复 init 方法不生效的 Bug
## 1.2.02021-07-30
- 组件兼容 vue3如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.12021-05-12
- 新增 示例地址
- 修复 示例项目缺少组件的 Bug
## 1.1.02021-04-22
- 新增 通过方法自定义动画
- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式
- 优化 动画触发逻辑,使动画更流畅
- 优化 支持单独的动画类型
- 优化 文档示例
## 1.0.22021-02-05
- 调整为 uni_modules 目录规范

View File

@@ -0,0 +1,131 @@
// const defaultOption = {
// duration: 300,
// timingFunction: 'linear',
// delay: 0,
// transformOrigin: '50% 50% 0'
// }
// #ifdef APP-NVUE
const nvueAnimation = uni.requireNativePlugin('animation')
// #endif
class MPAnimation {
constructor(options, _this) {
this.options = options
// 在iOS10+QQ小程序平台下传给原生的对象一定是个普通对象而不是Proxy对象否则会报parameter should be Object instead of ProxyObject的错误
this.animation = uni.createAnimation({
...options
})
this.currentStepAnimates = {}
this.next = 0
this.$ = _this
}
_nvuePushAnimates(type, args) {
let aniObj = this.currentStepAnimates[this.next]
let styles = {}
if (!aniObj) {
styles = {
styles: {},
config: {}
}
} else {
styles = aniObj
}
if (animateTypes1.includes(type)) {
if (!styles.styles.transform) {
styles.styles.transform = ''
}
let unit = ''
if(type === 'rotate'){
unit = 'deg'
}
styles.styles.transform += `${type}(${args+unit}) `
} else {
styles.styles[type] = `${args}`
}
this.currentStepAnimates[this.next] = styles
}
_animateRun(styles = {}, config = {}) {
let ref = this.$.$refs['ani'].ref
if (!ref) return
return new Promise((resolve, reject) => {
nvueAnimation.transition(ref, {
styles,
...config
}, res => {
resolve()
})
})
}
_nvueNextAnimate(animates, step = 0, fn) {
let obj = animates[step]
if (obj) {
let {
styles,
config
} = obj
this._animateRun(styles, config).then(() => {
step += 1
this._nvueNextAnimate(animates, step, fn)
})
} else {
this.currentStepAnimates = {}
typeof fn === 'function' && fn()
this.isEnd = true
}
}
step(config = {}) {
// #ifndef APP-NVUE
this.animation.step(config)
// #endif
// #ifdef APP-NVUE
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
this.next++
// #endif
return this
}
run(fn) {
// #ifndef APP-NVUE
this.$.animationData = this.animation.export()
this.$.timer = setTimeout(() => {
typeof fn === 'function' && fn()
}, this.$.durationTime)
// #endif
// #ifdef APP-NVUE
this.isEnd = false
let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
if(!ref) return
this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
this.next = 0
// #endif
}
}
const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
'translateZ'
]
const animateTypes2 = ['opacity', 'backgroundColor']
const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
MPAnimation.prototype[type] = function(...args) {
// #ifndef APP-NVUE
this.animation[type](...args)
// #endif
// #ifdef APP-NVUE
this._nvuePushAnimates(type, args)
// #endif
return this
}
})
export function createAnimation(option, _this) {
if(!_this) return
clearTimeout(_this.timer)
return new MPAnimation(option, _this)
}

View File

@@ -0,0 +1,286 @@
<template>
<!-- #ifndef APP-NVUE -->
<view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
<!-- #endif -->
</template>
<script>
import { createAnimation } from './createAnimation'
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
*/
export default {
name: 'uniTransition',
emits:['click','change'],
props: {
show: {
type: Boolean,
default: false
},
modeClass: {
type: [Array, String],
default() {
return 'fade'
}
},
duration: {
type: Number,
default: 300
},
styles: {
type: Object,
default() {
return {}
}
},
customClass:{
type: String,
default: ''
},
onceRender:{
type:Boolean,
default:false
},
},
data() {
return {
isShow: false,
transform: '',
opacity: 1,
animationData: {},
durationTime: 300,
config: {}
}
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open()
} else {
// 避免上来就执行 close,导致动画错乱
if (this.isShow) {
this.close()
}
}
},
immediate: true
}
},
computed: {
// 生成样式数据
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's'
}
let transform = ''
for (let i in styles) {
let line = this.toLine(i)
transform += line + ':' + styles[i] + ';'
}
return transform
},
// 初始化动画条件
transformStyles() {
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
}
},
created() {
// 动画默认配置
this.config = {
duration: this.duration,
timingFunction: 'ease',
transformOrigin: '50% 50%',
delay: 0
}
this.durationTime = this.duration
},
methods: {
/**
* ref 触发 初始化动画
*/
init(obj = {}) {
if (obj.duration) {
this.durationTime = obj.duration
}
this.animation = createAnimation(Object.assign(this.config, obj),this)
},
/**
* 点击组件触发回调
*/
onClick() {
this.$emit('click', {
detail: this.isShow
})
},
/**
* ref 触发 动画分组
* @param {Object} obj
*/
step(obj, config = {}) {
if (!this.animation) return
for (let i in obj) {
try {
if(typeof obj[i] === 'object'){
this.animation[i](...obj[i])
}else{
this.animation[i](obj[i])
}
} catch (e) {
console.error(`方法 ${i} 不存在`)
}
}
this.animation.step(config)
return this
},
/**
* ref 触发 执行动画
*/
run(fn) {
if (!this.animation) return
this.animation.run(fn)
},
// 开始过度动画
open() {
clearTimeout(this.timer)
this.transform = ''
this.isShow = true
let { opacity, transform } = this.styleInit(false)
if (typeof opacity !== 'undefined') {
this.opacity = opacity
}
this.transform = transform
// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
this.$nextTick(() => {
// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
this.timer = setTimeout(() => {
this.animation = createAnimation(this.config, this)
this.tranfromInit(false).step()
this.animation.run()
this.$emit('change', {
detail: this.isShow
})
}, 20)
})
},
// 关闭过度动画
close(type) {
if (!this.animation) return
this.tranfromInit(true)
.step()
.run(() => {
this.isShow = false
this.animationData = null
this.animation = null
let { opacity, transform } = this.styleInit(false)
this.opacity = opacity || 1
this.transform = transform
this.$emit('change', {
detail: this.isShow
})
})
},
// 处理动画开始前的默认样式
styleInit(type) {
let styles = {
transform: ''
}
let buildStyle = (type, mode) => {
if (mode === 'fade') {
styles.opacity = this.animationType(type)[mode]
} else {
styles.transform += this.animationType(type)[mode] + ' '
}
}
if (typeof this.modeClass === 'string') {
buildStyle(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildStyle(type, mode)
})
}
return styles
},
// 处理内置组合动画
tranfromInit(type) {
let buildTranfrom = (type, mode) => {
let aniNum = null
if (mode === 'fade') {
aniNum = type ? 0 : 1
} else {
aniNum = type ? '-100%' : '0'
if (mode === 'zoom-in') {
aniNum = type ? 0.8 : 1
}
if (mode === 'zoom-out') {
aniNum = type ? 1.2 : 1
}
if (mode === 'slide-right') {
aniNum = type ? '100%' : '0'
}
if (mode === 'slide-bottom') {
aniNum = type ? '100%' : '0'
}
}
this.animation[this.animationMode()[mode]](aniNum)
}
if (typeof this.modeClass === 'string') {
buildTranfrom(type, this.modeClass)
} else {
this.modeClass.forEach(mode => {
buildTranfrom(type, mode)
})
}
return this.animation
},
animationType(type) {
return {
fade: type ? 0 : 1,
'slide-top': `translateY(${type ? '0' : '-100%'})`,
'slide-right': `translateX(${type ? '0' : '100%'})`,
'slide-bottom': `translateY(${type ? '0' : '100%'})`,
'slide-left': `translateX(${type ? '0' : '-100%'})`,
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
}
},
// 内置动画类型与实际动画对应字典
animationMode() {
return {
fade: 'opacity',
'slide-top': 'translateY',
'slide-right': 'translateX',
'slide-bottom': 'translateY',
'slide-left': 'translateX',
'zoom-in': 'scale',
'zoom-out': 'scale'
}
},
// 驼峰转中横线
toLine(name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase()
}
}
}
</script>
<style></style>

View File

@@ -0,0 +1,85 @@
{
"id": "uni-transition",
"displayName": "uni-transition 过渡动画",
"version": "1.3.3",
"description": "元素的简单过渡动画",
"keywords": [
"uni-ui",
"uniui",
"动画",
"过渡",
"过渡动画"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
## Transition 过渡动画
> **组件名uni-transition**
> 代码块: `uTransition`
元素过渡动画
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-transition)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@@ -0,0 +1,21 @@
## 1.3.22023-10-31
修改遮罩默认透明度为0.1
## 1.3.12023-10-31
## 新增支持,自定义动画颜色(仅部分动画支持)
## 新增动画-annulus(圆环)
## 1.3.02023-08-11
支持vue3使用, 增加动画类型radar(雷达)
## 1.2.22023-06-12
增加 maskOpacity, maskMini, maskDark自定义参数, 提供更丰富的自定义遮罩层能力
## 1.2.12022-09-09
增加齿轮动画 type=gear
## 1.2.02022-05-27
1. 增加加载类型-剑气sword原子atom
2. 默认类型改为 atom
3. 遮罩透明度调整
## 1.1.12022-04-02
更新使用说明
## 1.1.02022-02-23
增加 type="love" 的心形加载动画
## 1.0.02022-01-28
首次发布

View File

@@ -0,0 +1,52 @@
<template>
<view class="container">
<view class="loader" :style="{'--color':color}"></view>
</view>
</template>
<script>
export default {
name: "loading-annulus",
props: {
color: {
type: String,
default: "#0396FF"
},
},
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loader {
width: 60px;
height: 60px;
}
.loader::before {
content: "";
box-sizing: border-box;
position: absolute;
width: 60px;
height: 60px;
border-radius: 50%;
border-top: 2px solid var(--color);
border-right: 2px solid transparent;
animation: spinner 1s linear infinite;
}
@keyframes spinner {
to {
transform: rotate(360deg);
}
}
</style>

View File

@@ -0,0 +1,115 @@
<template>
<view class="container">
<view class="box" :style="{'--color':color}">
<view class="atom"></view>
<view class="atom"></view>
<view class="atom"></view>
<view class="dot"></view>
</view>
</view>
</template>
<script>
export default {
name: 'loading-atom',
props: {
color: {
type: String,
default: "#0396FF"
},
},
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.box {
position: relative;
width: 120rpx;
height: 120rpx;
}
.dot{
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--color);
animation: dotbreath 2s linear infinite;
}
.atom {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
border-left-width: 6rpx;
border-top-width: 6rpx;
border-left-color: var(--color);
border-left-style: solid;
border-top-style: solid;
border-top-color: transparent;
}
.atom:nth-of-type(1) {
left: 0%;
top: 0%;
animation: atom1 1s linear infinite;
}
.atom:nth-of-type(2) {
right: 0%;
top: 0%;
animation: atom2 1s linear infinite;
}
.atom:nth-of-type(3) {
right: 0%;
bottom: 0%;
animation: atom3 1s linear infinite;
}
@keyframes dotbreath {
0% {
opacity:1;
}
50%{
opacity:0.5;
}
100%{
opacity:1;
}
}
@keyframes atom1 {
0% {
transform: rotateZ(120deg) rotateX(66deg) rotateZ(0deg);
}
100% {
transform: rotateZ(120deg) rotateX(66deg) rotateZ(360deg);
}
}
@keyframes atom2 {
0% {
transform: rotateZ(240deg) rotateX(66deg) rotateZ(0deg);
}
100% {
transform: rotateZ(240deg) rotateX(66deg) rotateZ(360deg);
}
}
@keyframes atom3 {
0% {
transform: rotateZ(360deg) rotateX(66deg) rotateZ(0deg);
}
100% {
transform: rotateZ(360deg) rotateX(66deg) rotateZ(360deg);
}
}
</style>

View File

@@ -0,0 +1,80 @@
<template>
<view class="container">
<view class="dot dot1"></view>
<view class="dot dot2"></view>
<view class="dot dot3"></view>
</view>
</template>
<script>
export default {
name: "loading-bounce",
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50rpx;
height: 30rpx;
}
.dot {
width: 12rpx;
height: 12rpx;
background: #007AFF;
border-radius: 50%;
position: absolute;
top: calc(50% - 5rpx);
}
.dot1 {
background: #1FA2FF;
left: 0rpx;
-webkit-animation: bounce 0.5s cubic-bezier(0.77, 0.47, 0.64, 0.28) alternate infinite;
animation: bounce 0.5s cubic-bezier(0.77, 0.47, 0.64, 0.28) alternate infinite;
}
.dot2 {
background: #12D8FA;
left: 25rpx;
-webkit-animation: bounce 0.5s 0.2s cubic-bezier(0.77, 0.47, 0.64, 0.28) alternate infinite;
animation: bounce 0.5s 0.2s cubic-bezier(0.77, 0.47, 0.64, 0.28) alternate infinite;
}
.dot3 {
background: #29ffc6;
left: 50rpx;
-webkit-animation: bounce 0.5s 0.4s cubic-bezier(0.77, 0.47, 0.64, 0.28) alternate infinite;
animation: bounce 0.5s 0.4s cubic-bezier(0.77, 0.47, 0.64, 0.28) alternate infinite;
}
@-webkit-keyframes bounce {
0% {
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
-webkit-transform: translateY(-15rpx);
transform: translateY(-15rpx);
}
}
@keyframes bounce {
0% {
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
-webkit-transform: translateY(-15rpx);
transform: translateY(-15rpx);
}
}
</style>

View File

@@ -0,0 +1,90 @@
<template>
<view class="container">
<view class="loader"></view>
</view>
</template>
<script>
export default {
name: "loading-circle",
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loader {
display: block;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
border: 3rpx solid transparent;
border-top-color: #1FA2FF;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
position: relative;
}
.loader::before {
content: "";
position: absolute;
top: 8rpx;
left: 8rpx;
right: 8rpx;
bottom: 8rpx;
border-radius: 50%;
border: 3rpx solid transparent;
border-top-color: #12D8FA;
-webkit-animation: spin 3s linear infinite;
animation: spin 3s linear infinite;
}
.loader::after {
content: "";
position: absolute;
top: 16rpx;
left: 16rpx;
right: 16rpx;
bottom: 16rpx;
border-radius: 50%;
border: 3rpx solid transparent;
border-top-color: #29ffc6;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>

View File

@@ -0,0 +1,80 @@
<template>
<view class="container">
<view class="eye"></view>
<view class="eye"></view>
</view>
</template>
<script>
export default {
name: 'loading-eyes',
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.container {
width: 110rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.eye {
width: 50rpx;
height: 50rpx;
background: linear-gradient(135deg, #1fa2ff, #12d8fa);
border-radius: 50%;
position: relative;
}
.eye:after {
background-color: #ffffff;
width: 18rpx;
height: 18rpx;
border-radius: 50%;
left: 20rpx;
top: 24rpx;
position: absolute;
content: '';
-webkit-animation: eyeball 1s linear infinite alternate;
-moz-animation: eyeball 1s linear infinite alternate;
animation: eyeball 1s linear infinite alternate;
}
@-webkit-keyframes eyeball {
0% {
left: 30rpx;
}
100% {
left: 2rpx;
}
}
@-moz-keyframes eyeball {
0% {
left: 30rpx;
}
100% {
left: 2rpx;
}
}
@keyframes eyeball {
0% {
left: 30rpx;
}
100% {
left: 2rpx;
}
}
</style>

View File

@@ -0,0 +1,126 @@
<template>
<view class="container">
<view class="box">
<view class="gear1">
<view class="inner inner1"> </view>
<view class="inner inner2"> </view>
<view class="inner inner3"> </view>
</view>
<view class="gear2">
<view class="inner inner1"> </view>
<view class="inner inner2"> </view>
<view class="inner inner3"> </view>
</view>
<view class="gear3">
<view class="inner inner1"> </view>
<view class="inner inner2"> </view>
<view class="inner inner3"> </view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'loading-gear',
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
$size:80rpx;
$bgc:red;
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.box {
width: 200rpx;
height: 200rpx;
position: relative;
}
@mixin gear($size:$size, $bgc:$bgc) {
width: $size;
height: $size;
.inner {
position: absolute;
width: $size;
height: $size;
top: 0;
left: 0;
background: $bgc;
border-radius: 6rpx;
mask: radial-gradient(transparent 40%, #fff 60%);
}
.inner2 {
transform: rotate(120deg);
}
.inner3 {
transform: rotate(240deg);
}
// &:after {
// position: absolute;
// content: '';
// background: #fff;
// width: $size / 1.8;
// height: $size / 1.8;
// border-radius: 100%;
// top: 50%;
// left: 50%;
// transform: translate(-50%, -50%);
// }
}
.gear1 {
@include gear(60rpx,#0396FF);
position: absolute;
top: 35rpx;
left: 35rpx;
animation: rotate 5s infinite linear;
}
.gear2 {
@include gear(50rpx, #dd524d);
position: absolute;
top: 50rpx;
left: 110rpx;
animation: rotateR 5s infinite linear;
}
.gear3 {
@include gear(50rpx, #f0ad4e);
position: absolute;
top: 110rpx;
left: 50rpx;
animation: rotateR 5s infinite linear;
}
@keyframes rotate {
from {
transform: rotate(0deg)
}
to {
transform: rotate(360deg)
}
}
@keyframes rotateR {
from {
transform: rotate(0deg)
}
to {
transform: rotate(-360deg)
}
}
</style>

View File

@@ -0,0 +1,210 @@
<template>
<view class="container">
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
<view class="item"></view>
</view>
</template>
<script>
export default {
name: "loading-love",
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
justify-content: center;
align-items: center;
flex-flow: row nowrap;
}
.item {
background: linear-gradient(to bottom, #F00000, #e73827);
width: 16rpx;
height: 16rpx;
border-radius: 20rpx;
margin-right: 10rpx;
}
.item:nth-child(1) {
animation: love1 4s infinite;
}
.item:nth-child(2) {
animation: love2 4s infinite;
animation-delay: 0.15s;
}
.item:nth-child(3) {
animation: love3 4s infinite;
animation-delay: 0.3s;
}
.item:nth-child(4) {
animation: love4 4s infinite;
animation-delay: 0.45s;
}
.item:nth-child(5) {
animation: love5 4s infinite;
animation-delay: 0.6s;
}
.item:nth-child(6) {
animation: love4 4s infinite;
animation-delay: 0.75s;
}
.item:nth-child(7) {
animation: love3 4s infinite;
animation-delay: 0.9s;
}
.item:nth-child(8) {
animation: love2 4s infinite;
animation-delay: 1.05s;
}
.item:nth-child(9) {
animation: love1 4s infinite;
animation-delay: 1.2s;
}
@keyframes love1 {
30%,
50% {
height: 50rpx;
transform: translateY(-20rpx);
}
75%,
100% {
height: 20rpx;
transform: translateY(0);
}
}
@keyframes love2 {
30%,
50% {
height: 90rpx;
transform: translateY(-25rpx);
}
75%,
100% {
height: 20rpx;
transform: translateY(0);
}
}
@keyframes love3 {
30%,
50% {
height: 120rpx;
transform: translateY(-20rpx);
}
75%,
100% {
height: 20rpx;
transform: translateY(0);
}
}
@keyframes love4 {
30%,
50% {
height: 130rpx;
transform: translateY(-10rpx);
}
75%,
100% {
height: 20rpx;
transform: translateY(0);
}
}
@keyframes love5 {
30%,
50% {
height: 130rpx;
transform: translateY(10rpx);
}
75%,
100% {
height: 20rpx;
transform: translateY(0);
}
}
// .item:nth-child(1) {
// height: 50rpx;
// transform: translateY(-20rpx);
// }
// .item:nth-child(2) {
// height: 90rpx;
// transform: translateY(-25rpx);
// }
// .item:nth-child(3) {
// height: 120rpx;
// transform: translateY(-20rpx);
// }
// .item:nth-child(4) {
// height: 130rpx;
// transform: translateY(-10rpx);
// }
// .item:nth-child(5) {
// height: 130rpx;
// transform: translateY(10rpx);
// }
// .item:nth-child(6) {
// height: 130rpx;
// transform: translateY(-10rpx);
// }
// .item:nth-child(7) {
// height: 120rpx;
// transform: translateY(-20rpx);
// }
// .item:nth-child(8) {
// height: 90rpx;
// transform: translateY(-25rpx);
// }
// .item:nth-child(9) {
// height: 50rpx;
// transform: translateY(-20rpx);
// }
</style>

View File

@@ -0,0 +1,64 @@
<template>
<view class="container">
<view class="pulse-bubble pulse-bubble-1"></view>
<view class="pulse-bubble pulse-bubble-2"></view>
<view class="pulse-bubble pulse-bubble-3"></view>
</view>
</template>
<script>
export default {
name: "loading-pulse",
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
/* pulse */
.container {
width: 100rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.pulse-bubble {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: #007AFF;
}
.pulse-bubble-1 {
background: #1FA2FF;
animation: pulse .4s ease 0s infinite alternate;
}
.pulse-bubble-2 {
background: #12D8FA;
animation: pulse .4s ease .2s infinite alternate;
}
.pulse-bubble-3 {
background: #29ffc6;
animation: pulse .4s ease .4s infinite alternate;
}
@keyframes pulse {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: .25;
transform: scale(.75);
}
}
</style>

View File

@@ -0,0 +1,137 @@
<template>
<view class="container">
<view class="radar">
<view class="dot dot-1"></view>
<view class="dot dot-2"></view>
<view class="dot dot-3"></view>
<view class="cover"></view>
</view>
</view>
</template>
<script>
export default {
name: "loading-radar",
data() {
return {};
},
};
</script>
<style lang="scss" scoped>
$size: 180rpx;
$dotSize: 4rpx;
$maincolor: #2da3f6;
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.radar {
position: relative;
z-index: 1;
height: $size;
width: $size;
background: -webkit-repeating-radial-gradient(rgba(45, 163, 246, 0) 0%,
rgba(45, 163, 246, 0) 23%,
rgba(45, 163, 246, 0.7) 24%,
rgba(45, 163, 246, 0) 25%);
margin: 0 auto;
border-radius: 50%;
border: 2rpx solid rgba(45, 163, 246, 0.7);
overflow: hidden;
}
.radar::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: $dotSize;
height: $dotSize;
background: $maincolor;
margin-left: -1rpx;
margin-top: -1rpx;
border-radius: 1rpx;
}
.dot {
position: absolute;
width: $dotSize;
height: $dotSize;
background: $maincolor;
opacity: 0;
border-radius: 50%;
animation: breath 3s linear infinite;
box-shadow: 0 0 2rpx 2rpx rgba(45, 163, 246, 0.5);
}
.dot-1 {
top: 50rpx;
left: 30rpx;
animation-delay: 1s;
}
.dot-2 {
top: 60rpx;
right: 20rpx;
animation-delay: 0.2s;
}
.dot-3 {
top: 140rpx;
right: 100rpx;
animation-delay: 2.3s;
}
.cover {
transform-origin: bottom right;
border-right: 1rpx solid $maincolor;
background: linear-gradient(45deg,
rgba(255, 255, 255, 0) 45%,
$maincolor 100%);
width: 50%;
height: 50%;
position: absolute;
top: 0;
left: 0;
animation: rotation 3s linear infinite;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes breath {
0% {
opacity: 0;
}
10% {
opacity: 1;
}
20% {
opacity: 1;
}
40% {
opacity: 0;
}
100% {
opacity: 0;
}
}
</style>

View File

@@ -0,0 +1,148 @@
<template>
<view class="container">
<view class="box">
<view class="sun"></view>
<view class="orbit orbit1">
<view class="planetX planet1"></view>
</view>
<view class="orbit orbit2">
<view class="planetX planet2"></view>
</view>
<view class="orbit orbit3">
<view class="planetX planet3"></view>
</view>
</view>
</view>
</template>
<script>
export default {
name: "loading-triangle",
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.box{
width:300rpx;
height:300rpx;
position: relative;
}
.sun {
background: radial-gradient(#ff0, #f90);
height: 50rpx;
width: 50rpx;
border-radius: 50%;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
.planetX {
position: absolute;
z-index: 100;
border-radius: 50%;
}
.planet1 {
left: 20rpx;
height: 13rpx;
width: 13rpx;
background-color: #fed313;
}
.planet2 {
left: 23rpx;
height: 20rpx;
width: 20rpx;
background: linear-gradient(#00ff00, #09f, #09f);
-webkit-animation: rotation 1s infinite linear;
animation: rotation 1s infinite linear;
}
.planet3 {
left: 49rpx;
height: 17rpx;
width: 17rpx;
background: radial-gradient(#ff9900, #ff4400);
}
.orbit {
background: transparent;
border-radius: 50%;
border: 1rpx solid #cccccc;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
.orbit1 {
height: 100rpx;
width: 100rpx;
-webkit-animation: rotation 2s infinite linear;
-moz-animation: rotation 2s infinite linear;
animation: rotation 2s infinite linear;
}
.orbit2 {
height: 150rpx;
width: 150rpx;
-webkit-animation: rotation 3s infinite linear;
-moz-animation: rotation 3s infinite linear;
animation: rotation 3s infinite linear;
}
.orbit3 {
height: 200rpx;
width: 200rpx;
-moz-animation: rotation 6s infinite linear;
-webkit-animation: rotation 6s infinite linear;
animation: rotation 6s infinite linear;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
}
}
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
@-moz-keyframes rotation {
from {
-moz-transform: rotate(0deg);
}
to {
-moz-transform: rotate(359deg);
}
}
</style>

View File

@@ -0,0 +1,87 @@
<template>
<view class="container">
<view class="box" :style="{'--color':color}">
<view class="sword"></view>
<view class="sword"></view>
<view class="sword"></view>
</view>
</view>
</template>
<script>
export default {
name: 'loading-sword',
props: {
color: {
type: String,
default: "#ED213A"
},
},
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.box {
position: relative;
width: 120rpx;
height: 120rpx;
}
.sword {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
}
.sword:nth-of-type(1) {
left: 0%;
top: 0%;
border-bottom: 8rpx solid var(--color);
animation: sword1 0.8s linear infinite;
}
.sword:nth-of-type(2) {
right: 0%;
top: 0%;
border-right: 8rpx solid var(--color);
animation: sword2 0.8s linear infinite;
}
.sword:nth-of-type(3) {
right: 0%;
bottom: 0%;
border-top: 8rpx solid var(--color);
animation: sword3 0.8s linear infinite;
}
@keyframes sword1 {
0% {
transform: rotateX(35deg) rotateY(-45deg) rotateZ(0deg);
}
100% {
transform: rotateX(35deg) rotateY(-45deg) rotateZ(360deg);
}
}
@keyframes sword2 {
0% {
transform: rotateX(50deg) rotateY(10deg) rotateZ(0deg);
}
100% {
transform: rotateX(50deg) rotateY(10deg) rotateZ(360deg);
}
}
@keyframes sword3 {
0% {
transform: rotateX(35deg) rotateY(55deg) rotateZ(0deg);
}
100% {
transform: rotateX(35deg) rotateY(55deg) rotateZ(360deg);
}
}
</style>

View File

@@ -0,0 +1,86 @@
<template>
<view class="container">
<view class="loader">
<view class="loader__ball"></view>
<view class="loader__ball"></view>
<view class="loader__ball"></view>
</view>
</view>
</template>
<script>
export default {
name: "loading-triangle",
data() {
return {};
}
};
</script>
<style lang="scss" scoped>
$dotColor: linear-gradient(135deg, #1FA2FF, #12D8FA, #29ffc6);
$dotSize: 30rpx;
$duration: 2s;
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.loader {
animation: rotate $duration linear infinite normal;
position: relative;
transform-origin: 50% 50%;
&__ball {
height: $dotSize;
width: $dotSize;
left: -$dotSize * 0.5;
position: absolute;
top: -$dotSize * 0.5;
transform-origin: 50% 50%;
&:nth-of-type(2) {
transform: rotate(120deg);
}
&:nth-of-type(3) {
transform: rotate(240deg);
}
&::after {
animation: move $duration * 0.5 ease-in-out infinite alternate;
background: $dotColor;
border-radius: 50%;
content: "";
display: inline-block;
height: 100%;
width: 100%;
transform-origin: 50% 50%;
}
}
}
@keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
@keyframes move {
0%,
15% {
transform: translateY(0);
}
100% {
transform: translateY(-150%);
}
}
</style>

View File

@@ -0,0 +1,134 @@
<template>
<!-- -->
<view :style="{'position': position,'z-index':zIndex,'--opacity':maskOpacity}" class="container"
:class="[mask?'mask':'',maskMini?'mask-mini':'',maskDark?'mask-dark':'']" @click.prevent="handleClick">
<view>
<view class="main">
<loading0 v-if="type=='circle'"></loading0>
<loading1 v-if="type=='pulse'"></loading1>
<loading2 v-if="type=='bounce'"></loading2>
<loading3 v-if="type=='eyes'"></loading3>
<loading4 v-if="type=='triangle'"></loading4>
<loading5 v-if="type=='sun'"></loading5>
<loading6 v-if="type=='love'"></loading6>
<loading7 v-if="type=='sword'" :color='color'></loading7>
<loading8 v-if="type=='atom'" :color='color'></loading8>
<loading9 v-if="type=='gear'"></loading9>
<loading10 v-if="type=='radar'"></loading10>
<loading11 v-if="type=='annulus'" :color='color'></loading11>
</view>
</view>
</view>
</template>
<script>
import loading0 from "./static/loading-circle.vue"
import loading1 from "./static/loading-pulse.vue"
import loading2 from "./static/loading-bounce.vue"
import loading3 from "./static/loading-eyes.vue"
import loading4 from "./static/loading-triangle.vue"
import loading5 from "./static/loading-sun.vue"
import loading6 from "./static/loading-love.vue"
import loading7 from "./static/loading-sword.vue"
import loading8 from "./static/loading-atom.vue"
import loading9 from "./static/loading-gear.vue"
import loading10 from "./static/loading-radar.vue"
import loading11 from "./static/loading-annulus.vue"
export default {
name: "zero-loading",
components: {
loading0,
loading1,
loading2,
loading3,
loading4,
loading5,
loading6,
loading7,
loading8,
loading9,
loading10,
loading11
},
props: {
type: {
type: String,
default: "atom"
},
position: {
type: String,
default: "fixed"
},
zIndex: {
type: Number,
default: 9,
},
mask: {
type: Boolean,
default: false,
},
maskOpacity: {
type: Number,
default: 0.1,
},
maskMini: {
type: Boolean,
default: false,
},
maskDark: {
type: Boolean,
default: true,
},
color: {
type: String,
default: "#0396FF"
},
},
data() {
return {
};
},
methods: {
handleClick() {
this.$emit('click')
}
},
};
</script>
<style lang="scss" scoped>
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.mask {
z-index: 999 !important;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100vh;
width: 100vw;
background: rgba(255, 255, 255, var(--opacity));
transform: translate(0, 0);
}
.mask-mini {
height: 300rpx;
width: 300rpx;
border-radius: 20rpx;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.mask-dark {
background: rgba(7, 17, 27, var(--opacity));
}
</style>

View File

@@ -0,0 +1,80 @@
{
"id": "zero-loading",
"displayName": "zero-loading(加载动画)",
"version": "1.3.2",
"description": "纯css加载动画, 一个标签元素即可实现炫酷的全屏loading效果,支持vue2,vue3",
"keywords": [
"loading",
"加载动画",
"css动画",
"加载"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": "",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "u",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More