### bookTicket.vue

-调整购买须知弹窗属性和事件处理,优化门票信息展示逻辑。
- 简化请求拦截器和响应拦截器的代码逻辑。

### config.js
- 更新票务相关API路径,统一使用/app-api前缀。

### httpRequest.js
- 重构请求和响应拦截逻辑,优化错误处理和token刷新机制。
- 添加对正在进行的请求的存储和重新发送逻辑。
- 调整auth参数检查为严格等于判断。
lu_quan_dev
Tuzki 1 year ago
parent 32910061d0
commit c00fbf2004
  1. 23
      common/config.js
  2. 207
      common/httpRequest.js
  3. 16
      subPageC/bookTicket/bookTicket.vue
  4. 24
      uni_modules/uni-transition/changelog.md
  5. 131
      uni_modules/uni-transition/components/uni-transition/createAnimation.js
  6. 286
      uni_modules/uni-transition/components/uni-transition/uni-transition.vue
  7. 85
      uni_modules/uni-transition/package.json
  8. 11
      uni_modules/uni-transition/readme.md

@ -5,7 +5,7 @@ const REAUEST_ROOTPATH = "http://192.168.130.205:8083";
// const REAUEST_ROOTPATH = "http://192.168.0.181:8083/";
const ROOTPATH = "http://192.168.130.205:8083";
//票务根
const PWPATH = "http://192.168.130.157:48080/app-api";
const PWPATH = "http://192.168.130.157:48080";
//手绘图项目名称
const HANDDRAWNNAME = "";
module.exports = {
@ -141,17 +141,16 @@ module.exports = {
//票务这边的
pageComplaintinfo:PWPATH+"/wechatshop/complaintinfo/pageComplaintinfo",//获得投诉建议分页
createComplaintinfo: PWPATH +"/wechatshop/complaintinfo/createComplaintinfo",//创建投诉建议
getTicketSortList: PWPATH + "/wechatshop/ticket/getTicketSortList",//获取门票分类
getTicketList: PWPATH + "/wechatshop/ticket/getTicketList",//获取门票列表分页
isQuota: PWPATH + "/wechatshop/ticket/isQuota",//查询是否限购
getScencAndTicketInfo: PWPATH + "/wechatshop/ticket/getScencAndTicketInfo",//获取景区门票相关信息
getTicketDateInventory: PWPATH + "/wechatshop/ticket/getTicketDateInventory",//获得门票日期库存
getPhone: PWPATH+"/wechatshop/auth/getWeChatUserMobile",//获取手机号
weChatLogin: PWPATH+"/wechatshop/auth/weChatLogin",//登陆获取token
pageComplaintinfo:PWPATH+"/app-api/wechatshop/complaintinfo/pageComplaintinfo",//获得投诉建议分页
createComplaintinfo: PWPATH +"/app-api/wechatshop/complaintinfo/createComplaintinfo",//创建投诉建议
getTicketSortList: PWPATH + "/app-api/wechatshop/ticket/getTicketSortList",//获取门票分类
getTicketList: PWPATH + "/app-api/wechatshop/ticket/getTicketList",//获取门票列表分页
isQuota: PWPATH + "/app-api/wechatshop/ticket/isQuota",//查询是否限购
getScencAndTicketInfo: PWPATH + "/app-api/wechatshop/ticket/getScencAndTicketInfo",//获取景区门票相关信息
getTicketDateInventory: PWPATH + "/app-api/wechatshop/ticket/getTicketDateInventory",//获得门票日期库存
getPhone: PWPATH+"/app-api/wechatshop/auth/getWeChatUserMobile",//获取手机号
weChatLogin: PWPATH+"/app-api/wechatshop/auth/weChatLogin",//登陆获取token
refreshToken: PWPATH +"/admin-api/system/auth/refresh-token",//登陆获取token
}

@ -1,12 +1,113 @@
import configdata from './config'
import cache from './cache'
import * as config from './config';
import cache from './cache';
import aes from "@/common/aes.js";
import store from "@/store/index.js";
// 用于存储正在进行的请求,以便在刷新 token 后重新发送
let requests = [];
let isRefreshingToken = false;
let refreshTokenPromise = null;
const refreshToken = () => {
if (!isRefreshingToken) {
debugger
isRefreshingToken = true;
const userInfo = uni.getStorageSync('userInfo');
const refreshTokenValue = userInfo.refreshToken;
if (!refreshTokenValue){
debugger
store.commit('setUserInfo', null)
uni.removeStorageSync('userInfo');
uni.showToast({
title: '用户失效,请重新登录',
icon: 'none',
mask: true
})
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index'
});
},1500)
}else{
debugger
return module.exports.post(config.refreshToken, { refreshToken: refreshTokenValue }, 'application/json', null, false, false)
.then(response => {
debugger
const newAccessToken = response.data.accessToken;
const newRefreshToken = response.data.refreshToken;
userInfo.accessToken = newAccessToken;
userInfo.refreshToken = newRefreshToken;
store.commit('setUserInfo', userInfo)
return newAccessToken;
})
.catch(error => {
debugger
isRefreshingToken = false;
return Promise.reject(error);
});
}
} else {
return refreshTokenPromise;
}
};
const requestInterceptor = function (config) {
debugger
const tok = store.state.userInfo.accessToken || uni.getStorageSync('userInfo').accessToken;
if (tok) {
config.header['Authorization'] = 'Bearer ' + tok;
}
// 添加其他通用预处理逻辑
if (config.data && typeof config.data === 'object') {
for (const key in config.data) {
if (key === 'oldFieldName') {
config.data['newFieldName'] = config.data[key];
delete config.data[key];
}
}
}
return config;
};
const responseInterceptor = function (response,config) {
console.log(response, config,'response接口结果')
debugger
const code = response[1].data.status || response[1].data.code;
if (code&&code === 401) {
let originalRequest = config;
console.log(originalRequest, 'originalRequestsssss');
return new Promise((resolve, reject) => {
// 先将当前请求添加到队列中
requests.push(() => {
originalRequest.header['Authorization'] = 'Bearer ' + store.state.userInfo.accessToken;
return uni.request(originalRequest).then((newResponse) => {
console.log(newResponse, 'newResponse');
// 如果新的响应还是 401,递归调用直到成功或出现其他错误
if (newResponse[1].data.status === 401 || newResponse[1].data.code === 401) {
return responseInterceptor(newResponse, originalRequest);
} else {
resolve(newResponse[1]);
}
}).catch(reject);
});
// 调用刷新 token 的接口
refreshToken().then(() => {
requests.forEach(cb => cb());
requests = [];
}).catch(reject);
});
}
return response[1];
};
module.exports = {
config: function (name) {
var info = null;
if (name) {
var name2 = name.split("."); //字符分割
var name2 = name.split(".");
if (name2.length > 1) {
info = configdata[name2[0]][name2[1]] || null;
} else {
@ -26,22 +127,22 @@ module.exports = {
return info;
},
post: function (url, data, headerContentType, tentId, needAes, auth) {
console.log(url, data, headerContentType, tentId, needAes, 'post请求参数');
console.log(url, data, headerContentType, tentId, needAes, 'post 请求参数');
if (needAes != false) {
if (needAes !== false) {
for (var key in data) {
data[key] = aes.aesMinEncrypt(data[key]);
}
}
headerContentType = headerContentType == "json" ? "application/json" : "application/x-www-form-urlencoded";
headerContentType = headerContentType === "json" ? "application/json" : "application/x-www-form-urlencoded";
let headers = {
"content-type": headerContentType,
'Cache-Control': 'no-cache'
};
if (auth == true) {
if (auth === true) {
const tok = store.state.userInfo.accessToken || uni.getStorageSync('userInfo').accessToken;
headers['Authorization'] = 'Bearer ' + tok;
}
@ -51,33 +152,44 @@ module.exports = {
}
return new Promise((succ, error) => {
uni.request({
let config = {
url: url,
data: headerContentType == "application/json" ? JSON.stringify(data) : data,
data: headerContentType === "application/json" ? JSON.stringify(data) : data,
method: "POST",
header: headers,
xhrFields: {
withCredentials: true // 这里设置了withCredentials
},
success: function (result) {
succ.call(self, result.data);
},
fail: function (e) {
error.call(self, e);
withCredentials: true
}
};
config = requestInterceptor(config);
uni.request(config).then(result => {
const response = responseInterceptor(result, config);
if (response instanceof Promise) {
response.then(res => {
succ.call(self, res.data);
}).catch(err => {
console.error('POST 请求错误:', err);
error.call(self, err);
});
} else {
succ.call(self, response.data);
}
}).catch(e => {
console.error('POST 请求失败:', e);
error.call(self, e);
});
});
},
get: function (url, data, headerContentType, tentId, needAes, auth) {
console.log(url, data, headerContentType, tentId, needAes, 'get请求参数');
console.log(url, data, headerContentType, tentId, needAes, 'get 请求参数');
if (needAes != false) {
if (needAes !== false) {
for (var key in data) {
data[key] = aes.aesMinEncrypt(data[key]); // 进行加密
data[key] = aes.aesMinEncrypt(data[key]);
}
}
headerContentType = headerContentType ? headerContentType : "application/x-www-form-urlencoded";
let headers = {
@ -85,8 +197,9 @@ module.exports = {
'Cache-Control': 'no-cache'
};
if (auth == true) {
const tok = store.state.userInfo.accessToken || uni.getStorageSync('userInfo').accessToken;
if (auth === true) {
// const tok = store.state.userInfo.accessToken || uni.getStorageSync('userInfo').accessToken;
const tok = uni.getStorageSync('userInfo').accessToken;
headers['Authorization'] = 'Bearer ' + tok;
}
@ -95,41 +208,29 @@ module.exports = {
}
return new Promise((succ, error) => {
uni.request({
let config = {
url: url,
data: data,
method: "GET",
header: headers,
success: function (result) {
succ.call(self, result.data);
},
fail: function (e) {
error.call(self, e);
header: headers
};
config = requestInterceptor(config);
uni.request(config).then(result => {
const response = responseInterceptor(result, config);
if (response instanceof Promise) {
response.then(res => {
succ.call(self, res.data);
}).catch(err => {
console.error('POST 请求错误:', err);
error.call(self, err);
});
} else {
succ.call(self, response.data);
}
}).catch(e => {
console.error('GET 请求失败:', e);
error.call(self, e);
});
});
}
}
// 写法示例
// var data = {
// "pageno": "1",
// "pagesize": "10",
// "sort": "-2",
// "region": "1301",
// "type": "2907"
// }
// this.$Request.post(this.$config.messagelist, data).then(
// res => {
// console.log(">>>"+JSON.stringify(res));
// var d = res.data;
// for (var i = 0; i < d.length; i++) {
// d[i].logo = this.$config.ROOTPATH + d[i].logo;
// }
// this.mudidiList = res.data;
// },
// error => {//请求进入fail错误方法,返回接收
// console.log(">>>"+JSON.stringify(error));
// }
// )
};

@ -211,7 +211,13 @@
></f-pay>
</u-popup>
<!-- 须知 -->
<uni-popup ref="mPurchaseNotice" border-radius="10px 10px 0 0" :safe-area="true">
<uni-popup
:maskShow="true"
ref="mPurchaseNotice"
border-radius="10px 10px 0 0"
:safe-area="false"
@maskClick="maskClick"
>
<view class="m-purchase-notice m-pop-bg">
<view class="m-green-bg"></view>
<view class="m-red-bg"></view>
@ -250,7 +256,7 @@
<view class="m-table-head">费用包含</view>
<view class="m-table-th">
<view class="m-table-td">门票</view>
<view class="m-table-td">{{ ticketInfos.ticketname }}</view>
<view class="m-table-td">{{ ticketInfos.ticketname || "-" }}</view>
</view>
</view>
<view class="m-driver"></view>
@ -579,6 +585,9 @@ export default {
}
},
methods: {
maskClick() {
this.$refs.mPurchaseNotice.close();
},
//
changeMonth(e) {
console.log(e);
@ -924,7 +933,7 @@ export default {
openPop(val) {
switch (val) {
case 1:
this.$refs.mPurchaseNotice.open("top");
this.$refs.mPurchaseNotice.open("bottom");
break;
default:
break;
@ -1486,6 +1495,7 @@ export default {
.m-ul {
.m-li {
line-height: 35rpx;
}
}
}

@ -0,0 +1,24 @@
## 1.3.3(2024-04-23)
- 修复 当元素会受变量影响自动隐藏的bug
## 1.3.2(2023-05-04)
- 修复 NVUE 平台报错的问题
## 1.3.1(2021-11-23)
- 修复 init 方法初始化问题
## 1.3.0(2021-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.1(2021-09-27)
- 修复 init 方法不生效的 Bug
## 1.2.0(2021-07-30)
- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.1(2021-05-12)
- 新增 示例地址
- 修复 示例项目缺少组件的 Bug
## 1.1.0(2021-04-22)
- 新增 通过方法自定义动画
- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式
- 优化 动画触发逻辑,使动画更流畅
- 优化 支持单独的动画类型
- 优化 文档示例
## 1.0.2(2021-02-05)
- 调整为 uni_modules 目录规范

@ -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)
}

@ -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>

@ -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"
}
}
}
}
}

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