完成去哪儿机票页面,视频群聊页面优化
|
|
@ -7,7 +7,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"customPlaygroundType" : "local",
|
"customPlaygroundType" : "local",
|
||||||
"playground" : "custom",
|
"playground" : "standard",
|
||||||
"type" : "uni-app:app-android"
|
"type" : "uni-app:app-android"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
137
App.vue
|
|
@ -6,9 +6,13 @@ export default {
|
||||||
recentNativeData: 0 // 初始化一个全局变量
|
recentNativeData: 0 // 初始化一个全局变量
|
||||||
},
|
},
|
||||||
onLaunch: function (options) {
|
onLaunch: function (options) {
|
||||||
|
// === wgt 包启动诊断日志 ===
|
||||||
|
console.log('=== App Launch 开始 ===')
|
||||||
|
console.log('启动参数:', JSON.stringify(options))
|
||||||
|
console.log('环境:', process.env.NODE_ENV)
|
||||||
|
|
||||||
uni.setStorageSync('onNativeEventReceive', "no")
|
uni.setStorageSync('onNativeEventReceive', "no")
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
console.log('App Launch', options)
|
|
||||||
|
|
||||||
// 1. 获取并存储系统信息(只获取一次)
|
// 1. 获取并存储系统信息(只获取一次)
|
||||||
const systemInfo = uni.getSystemInfoSync()
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
|
|
@ -24,11 +28,14 @@ export default {
|
||||||
isAndroid: systemInfo.platform === 'android'
|
isAndroid: systemInfo.platform === 'android'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 2. 同步初始化配置(必须完成)
|
// 2. 立即设置宿主消息监听(避免错过消息)
|
||||||
|
this.setupNativeEventListener()
|
||||||
|
|
||||||
|
// 3. 同步初始化配置(必须完成)
|
||||||
this.initConfig(options)
|
this.initConfig(options)
|
||||||
|
|
||||||
// 启动完成
|
// 启动完成
|
||||||
console.log(`App 启动耗时: ${Date.now() - startTime}ms`)
|
console.log(`=== App 启动完成,耗时: ${Date.now() - startTime}ms ===`)
|
||||||
|
|
||||||
// 初始化埋点,进入支付宝模拟器首页
|
// 初始化埋点,进入支付宝模拟器首页
|
||||||
this.$apiUserEvent('all', {
|
this.$apiUserEvent('all', {
|
||||||
|
|
@ -40,42 +47,7 @@ export default {
|
||||||
|
|
||||||
onShow: function () {
|
onShow: function () {
|
||||||
console.log('App Show')
|
console.log('App Show')
|
||||||
if (this.globalData.NativeEvent) {
|
// 监听已在 onLaunch 中设置,这里不再重复
|
||||||
this.globalData.NativeEvent = false
|
|
||||||
//监听宿主传递消息
|
|
||||||
uni.onNativeEventReceive((event, data) => {
|
|
||||||
if (event) {
|
|
||||||
if (event == "token") {
|
|
||||||
let header = uni.getStorageSync('header')
|
|
||||||
header["x-token"] = data
|
|
||||||
uni.setStorageSync('header', header)
|
|
||||||
//获取宿主用户信息
|
|
||||||
try {
|
|
||||||
//获取宿主用户信息
|
|
||||||
this.$getUserInfo()
|
|
||||||
} catch (error) {
|
|
||||||
//TODO handle the exception
|
|
||||||
}
|
|
||||||
} else if (event == "jump") {
|
|
||||||
if (data) {
|
|
||||||
let pages = getCurrentPages();
|
|
||||||
let currentPage = pages[pages.length - 1];
|
|
||||||
let currentUrl = currentPage.route;
|
|
||||||
if (currentUrl != data) {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/' + data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (event == 'wx_pay_result' || event == 'ios_pay_result') {
|
|
||||||
this.globalData.recentNativeEvent = event
|
|
||||||
this.globalData.recentNativeData = data
|
|
||||||
}
|
|
||||||
console.log('全局监听:接收到宿主App消息-' + event + ':' + data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onHide: function () {
|
onHide: function () {
|
||||||
|
|
@ -93,18 +65,69 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* 设置宿主消息监听(在 onLaunch 中调用)
|
||||||
|
*/
|
||||||
|
setupNativeEventListener() {
|
||||||
|
if (this.globalData.NativeEvent) {
|
||||||
|
this.globalData.NativeEvent = false
|
||||||
|
console.log('开始监听宿主消息')
|
||||||
|
|
||||||
|
uni.onNativeEventReceive((event, data) => {
|
||||||
|
if (event) {
|
||||||
|
console.log('接收到宿主消息:', event, data)
|
||||||
|
|
||||||
|
if (event == "token") {
|
||||||
|
let header = uni.getStorageSync('header') || {}
|
||||||
|
header["x-token"] = data
|
||||||
|
uni.setStorageSync('header', header)
|
||||||
|
console.log('已更新 token')
|
||||||
|
|
||||||
|
//获取宿主用户信息
|
||||||
|
try {
|
||||||
|
this.$getUserInfo()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户信息失败:', error)
|
||||||
|
}
|
||||||
|
} else if (event == "jump") {
|
||||||
|
if (data) {
|
||||||
|
let pages = getCurrentPages();
|
||||||
|
let currentPage = pages[pages.length - 1];
|
||||||
|
let currentUrl = currentPage.route;
|
||||||
|
if (currentUrl != data) {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/' + data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event == 'wx_pay_result' || event == 'ios_pay_result') {
|
||||||
|
this.globalData.recentNativeEvent = event
|
||||||
|
this.globalData.recentNativeData = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化应用配置
|
* 初始化应用配置
|
||||||
*/
|
*/
|
||||||
initConfig(options) {
|
initConfig(options) {
|
||||||
|
console.log('=== 配置初始化 ===')
|
||||||
|
|
||||||
// 检查是否有外部传入的配置数据
|
// 检查是否有外部传入的配置数据
|
||||||
const hasExtraData = options?.referrerInfo?.extraData &&
|
const hasExtraData = options?.referrerInfo?.extraData &&
|
||||||
JSON.stringify(options.referrerInfo.extraData) !== '{}'
|
JSON.stringify(options.referrerInfo.extraData) !== '{}'
|
||||||
|
|
||||||
|
console.log('宿主是否传递配置:', hasExtraData)
|
||||||
|
|
||||||
if (hasExtraData) {
|
if (hasExtraData) {
|
||||||
|
console.log('→ 使用宿主 extraData')
|
||||||
this.initFromExtraData(options.referrerInfo.extraData)
|
this.initFromExtraData(options.referrerInfo.extraData)
|
||||||
} else {
|
} else {
|
||||||
this.initFromEnvironment()
|
console.log('→ 宿主未传递配置,使用默认配置')
|
||||||
|
// ⚠️ 关键修改:不再强制退出,使用默认配置
|
||||||
|
this.initDevelopmentConfig()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -142,23 +165,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从环境配置初始化
|
* 初始化开发环境配置(也作为默认配置)
|
||||||
*/
|
|
||||||
initFromEnvironment() {
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
this.initDevelopmentConfig()
|
|
||||||
} else {
|
|
||||||
// 生产环境警告延迟显示,避免阻塞启动
|
|
||||||
setTimeout(() => {
|
|
||||||
this.showProductionWarning()
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化开发环境配置
|
|
||||||
*/
|
*/
|
||||||
initDevelopmentConfig() {
|
initDevelopmentConfig() {
|
||||||
|
console.log('初始化默认配置')
|
||||||
|
|
||||||
// 批量设置开发环境配置
|
// 批量设置开发环境配置
|
||||||
const devConfig = {
|
const devConfig = {
|
||||||
host: "https://flaunt.batiao8.com/",
|
host: "https://flaunt.batiao8.com/",
|
||||||
|
|
@ -170,22 +181,8 @@ export default {
|
||||||
Object.keys(devConfig).forEach(key => {
|
Object.keys(devConfig).forEach(key => {
|
||||||
uni.setStorageSync(key, devConfig[key])
|
uni.setStorageSync(key, devConfig[key])
|
||||||
})
|
})
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
console.log('默认配置初始化完成')
|
||||||
* 显示生产环境警告
|
|
||||||
*/
|
|
||||||
showProductionWarning() {
|
|
||||||
uni.showModal({
|
|
||||||
title: '提示',
|
|
||||||
showCancel: false,
|
|
||||||
content: "将退出,请重新进入",
|
|
||||||
success: function (res) {
|
|
||||||
// #ifdef APP-PLUS
|
|
||||||
plus.runtime.quit()
|
|
||||||
// #endif
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,533 @@
|
||||||
|
<!-- eslint-disable -->
|
||||||
|
<template>
|
||||||
|
<view
|
||||||
|
class="player-wrapper"
|
||||||
|
:id="videoWrapperId"
|
||||||
|
:parentId="id"
|
||||||
|
:randomNum="randomNum"
|
||||||
|
:change:randomNum="domVideoPlayer.randomNumChange"
|
||||||
|
:viewportProps="viewportProps"
|
||||||
|
:change:viewportProps="domVideoPlayer.viewportChange"
|
||||||
|
:videoSrc="videoSrc"
|
||||||
|
:change:videoSrc="domVideoPlayer.initVideoPlayer"
|
||||||
|
:command="eventCommand"
|
||||||
|
:change:command="domVideoPlayer.triggerCommand"
|
||||||
|
:func="renderFunc"
|
||||||
|
:change:func="domVideoPlayer.triggerFunc"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
autoplay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
loop: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
controls: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
objectFit: {
|
||||||
|
type: String,
|
||||||
|
default: 'contain'
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
playbackRate: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
isLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
poster: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
randomNum: Math.floor(Math.random() * 100000000),
|
||||||
|
videoSrc: '',
|
||||||
|
// 父组件向子组件传递的事件指令(video的原生事件)
|
||||||
|
eventCommand: null,
|
||||||
|
// 父组件传递过来的,对 renderjs 层的函数执行(对视频控制的自定义事件)
|
||||||
|
renderFunc: {
|
||||||
|
name: null,
|
||||||
|
params: null
|
||||||
|
},
|
||||||
|
// 提供给父组件进行获取的视频属性
|
||||||
|
currentTime: 0,
|
||||||
|
duration: 0,
|
||||||
|
playing: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// 监听视频资源地址更新
|
||||||
|
src: {
|
||||||
|
handler(val) {
|
||||||
|
if (!val) return
|
||||||
|
setTimeout(() => {
|
||||||
|
this.videoSrc = val
|
||||||
|
}, 0)
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
videoWrapperId() {
|
||||||
|
return `video-wrapper-${this.randomNum}`
|
||||||
|
},
|
||||||
|
// 聚合视图层的所有数据变化,传给renderjs的渲染层
|
||||||
|
viewportProps() {
|
||||||
|
return {
|
||||||
|
autoplay: this.autoplay,
|
||||||
|
muted: this.muted,
|
||||||
|
controls: this.controls,
|
||||||
|
loop: this.loop,
|
||||||
|
objectFit: this.objectFit,
|
||||||
|
poster: this.poster,
|
||||||
|
isLoading: this.isLoading,
|
||||||
|
playbackRate: this.playbackRate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 方法
|
||||||
|
methods: {
|
||||||
|
// 传递事件指令给父组件
|
||||||
|
eventEmit({ event, data }) {
|
||||||
|
this.$emit(event, data)
|
||||||
|
},
|
||||||
|
// 修改view视图层的data数据
|
||||||
|
setViewData({ key, value }) {
|
||||||
|
key && this.$set(this, key, value)
|
||||||
|
},
|
||||||
|
// 重置事件指令
|
||||||
|
resetEventCommand() {
|
||||||
|
this.eventCommand = null
|
||||||
|
},
|
||||||
|
// 播放指令
|
||||||
|
play() {
|
||||||
|
this.eventCommand = 'play'
|
||||||
|
},
|
||||||
|
// 暂停指令
|
||||||
|
pause() {
|
||||||
|
this.eventCommand = 'pause'
|
||||||
|
},
|
||||||
|
// 重置自定义函数指令
|
||||||
|
resetFunc() {
|
||||||
|
this.renderFunc = {
|
||||||
|
name: null,
|
||||||
|
params: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 自定义函数 - 移除视频
|
||||||
|
remove(params) {
|
||||||
|
this.renderFunc = {
|
||||||
|
name: 'removeHandler',
|
||||||
|
params
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 自定义函数 - 全屏播放
|
||||||
|
fullScreen(params) {
|
||||||
|
this.renderFunc = {
|
||||||
|
name: 'fullScreenHandler',
|
||||||
|
params
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 自定义函数 - 跳转到指定时间点
|
||||||
|
toSeek(sec, isDelay = false) {
|
||||||
|
this.renderFunc = {
|
||||||
|
name: 'toSeekHandler',
|
||||||
|
params: { sec, isDelay }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<script module="domVideoPlayer" lang="renderjs">
|
||||||
|
const PLAYER_ID = 'DOM_VIDEO_PLAYER'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
num: '',
|
||||||
|
videoEl: null,
|
||||||
|
loadingEl: null,
|
||||||
|
// 延迟生效的函数
|
||||||
|
delayFunc: null,
|
||||||
|
renderProps: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
playerId() {
|
||||||
|
return `${PLAYER_ID}_${this.num}`
|
||||||
|
},
|
||||||
|
wrapperId() {
|
||||||
|
return `video-wrapper-${this.num}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isApple() {
|
||||||
|
const ua = navigator.userAgent.toLowerCase()
|
||||||
|
return ua.indexOf('iphone') !== -1 || ua.indexOf('ipad') !== -1
|
||||||
|
},
|
||||||
|
async initVideoPlayer(src) {
|
||||||
|
this.delayFunc = null
|
||||||
|
await this.$nextTick()
|
||||||
|
if (!src) return
|
||||||
|
if (this.videoEl) {
|
||||||
|
// 切换视频源
|
||||||
|
if (!this.isApple() && this.loadingEl) {
|
||||||
|
this.loadingEl.style.display = 'block'
|
||||||
|
}
|
||||||
|
this.videoEl.src = src
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoEl = document.createElement('video')
|
||||||
|
this.videoEl = videoEl
|
||||||
|
// 开始监听视频相关事件
|
||||||
|
this.listenVideoEvent()
|
||||||
|
|
||||||
|
const { autoplay, muted, controls, loop, playbackRate, objectFit, poster } = this.renderProps
|
||||||
|
videoEl.src = src
|
||||||
|
videoEl.autoplay = autoplay
|
||||||
|
videoEl.controls = controls
|
||||||
|
videoEl.loop = loop
|
||||||
|
videoEl.muted = muted
|
||||||
|
videoEl.playbackRate = playbackRate
|
||||||
|
videoEl.id = this.playerId
|
||||||
|
// videoEl.setAttribute('x5-video-player-type', 'h5')
|
||||||
|
videoEl.setAttribute('preload', 'auto')
|
||||||
|
videoEl.setAttribute('playsinline', true)
|
||||||
|
videoEl.setAttribute('webkit-playsinline', true)
|
||||||
|
videoEl.setAttribute('crossorigin', 'anonymous')
|
||||||
|
videoEl.setAttribute('controlslist', 'nodownload')
|
||||||
|
videoEl.setAttribute('disablePictureInPicture', true)
|
||||||
|
videoEl.style.objectFit = objectFit
|
||||||
|
poster && (videoEl.poster = poster)
|
||||||
|
videoEl.style.width = '100%'
|
||||||
|
videoEl.style.height = '100%'
|
||||||
|
|
||||||
|
// 插入视频元素
|
||||||
|
// document.getElementById(this.wrapperId).appendChild(videoEl)
|
||||||
|
const playerWrapper = document.getElementById(this.wrapperId)
|
||||||
|
playerWrapper.insertBefore(videoEl, playerWrapper.firstChild)
|
||||||
|
|
||||||
|
// 插入loading 元素(遮挡安卓的默认加载过程中的黑色播放按钮)
|
||||||
|
this.createLoading()
|
||||||
|
},
|
||||||
|
// 创建 loading
|
||||||
|
createLoading() {
|
||||||
|
const { isLoading } = this.renderProps
|
||||||
|
if (!this.isApple() && isLoading) {
|
||||||
|
const loadingEl = document.createElement('div')
|
||||||
|
this.loadingEl = loadingEl
|
||||||
|
loadingEl.className = 'loading-wrapper'
|
||||||
|
loadingEl.style.position = 'absolute'
|
||||||
|
loadingEl.style.top = '0'
|
||||||
|
loadingEl.style.left = '0'
|
||||||
|
loadingEl.style.zIndex = '1'
|
||||||
|
loadingEl.style.width = '100%'
|
||||||
|
loadingEl.style.height = '100%'
|
||||||
|
loadingEl.style.backgroundColor = 'black'
|
||||||
|
document.getElementById(this.wrapperId).appendChild(loadingEl)
|
||||||
|
|
||||||
|
// 创建 loading 动画
|
||||||
|
const animationEl = document.createElement('div')
|
||||||
|
animationEl.className = 'loading'
|
||||||
|
animationEl.style.zIndex = '2'
|
||||||
|
animationEl.style.position = 'absolute'
|
||||||
|
animationEl.style.top = '50%'
|
||||||
|
animationEl.style.left = '50%'
|
||||||
|
animationEl.style.marginTop = '-15px'
|
||||||
|
animationEl.style.marginLeft = '-15px'
|
||||||
|
animationEl.style.width = '30px'
|
||||||
|
animationEl.style.height = '30px'
|
||||||
|
animationEl.style.border = '2px solid #FFF'
|
||||||
|
animationEl.style.borderTopColor = 'rgba(255, 255, 255, 0.2)'
|
||||||
|
animationEl.style.borderRightColor = 'rgba(255, 255, 255, 0.2)'
|
||||||
|
animationEl.style.borderBottomColor = 'rgba(255, 255, 255, 0.2)'
|
||||||
|
animationEl.style.borderRadius = '100%'
|
||||||
|
animationEl.style.animation = 'circle infinite 0.75s linear'
|
||||||
|
loadingEl.appendChild(animationEl)
|
||||||
|
|
||||||
|
// 创建 loading 动画所需的 keyframes
|
||||||
|
const style = document.createElement('style')
|
||||||
|
const keyframes = `
|
||||||
|
@keyframes circle {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
style.type = 'text/css'
|
||||||
|
if (style.styleSheet) {
|
||||||
|
style.styleSheet.cssText = keyframes
|
||||||
|
} else {
|
||||||
|
style.appendChild(document.createTextNode(keyframes))
|
||||||
|
}
|
||||||
|
document.head.appendChild(style)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 监听视频相关事件
|
||||||
|
listenVideoEvent() {
|
||||||
|
// 播放事件监听
|
||||||
|
const playHandler = () => {
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', { event: 'play' })
|
||||||
|
this.$ownerInstance.callMethod('setViewData', {
|
||||||
|
key: 'playing',
|
||||||
|
value: true
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.loadingEl) {
|
||||||
|
this.loadingEl.style.display = 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.videoEl.removeEventListener('play', playHandler)
|
||||||
|
this.videoEl.addEventListener('play', playHandler)
|
||||||
|
|
||||||
|
// 暂停事件监听
|
||||||
|
const pauseHandler = () => {
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', { event: 'pause' })
|
||||||
|
this.$ownerInstance.callMethod('setViewData', {
|
||||||
|
key: 'playing',
|
||||||
|
value: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.videoEl.removeEventListener('pause', pauseHandler)
|
||||||
|
this.videoEl.addEventListener('pause', pauseHandler)
|
||||||
|
|
||||||
|
// 结束事件监听
|
||||||
|
const endedHandler = () => {
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', { event: 'ended' })
|
||||||
|
this.$ownerInstance.callMethod('resetEventCommand')
|
||||||
|
}
|
||||||
|
this.videoEl.removeEventListener('ended', endedHandler)
|
||||||
|
this.videoEl.addEventListener('ended', endedHandler)
|
||||||
|
|
||||||
|
// 加载完成事件监听
|
||||||
|
const canPlayHandler = () => {
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', { event: 'canplay' })
|
||||||
|
this.execDelayFunc()
|
||||||
|
}
|
||||||
|
this.videoEl.removeEventListener('canplay', canPlayHandler)
|
||||||
|
this.videoEl.addEventListener('canplay', canPlayHandler)
|
||||||
|
|
||||||
|
// 加载失败事件监听
|
||||||
|
const errorHandler = (e) => {
|
||||||
|
if (this.loadingEl) {
|
||||||
|
this.loadingEl.style.display = 'block'
|
||||||
|
}
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', { event: 'error' })
|
||||||
|
}
|
||||||
|
this.videoEl.removeEventListener('error', errorHandler)
|
||||||
|
this.videoEl.addEventListener('error', errorHandler)
|
||||||
|
|
||||||
|
// loadedmetadata 事件监听
|
||||||
|
const loadedMetadataHandler = () => {
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', { event: 'loadedmetadata' })
|
||||||
|
// 获取视频的长度
|
||||||
|
const duration = this.videoEl.duration
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', {
|
||||||
|
event: 'durationchange',
|
||||||
|
data: duration
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$ownerInstance.callMethod('setViewData', {
|
||||||
|
key: 'duration',
|
||||||
|
value: duration
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载首帧视频 模拟出封面图
|
||||||
|
this.loadFirstFrame()
|
||||||
|
}
|
||||||
|
this.videoEl.removeEventListener('loadedmetadata', loadedMetadataHandler)
|
||||||
|
this.videoEl.addEventListener('loadedmetadata', loadedMetadataHandler)
|
||||||
|
|
||||||
|
// 播放进度监听
|
||||||
|
const timeupdateHandler = (e) => {
|
||||||
|
const currentTime = e.target.currentTime
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', {
|
||||||
|
event: 'timeupdate',
|
||||||
|
data: currentTime
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$ownerInstance.callMethod('setViewData', {
|
||||||
|
key: 'currentTime',
|
||||||
|
value: currentTime
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
this.videoEl.removeEventListener('timeupdate', timeupdateHandler)
|
||||||
|
this.videoEl.addEventListener('timeupdate', timeupdateHandler)
|
||||||
|
|
||||||
|
// 倍速播放监听
|
||||||
|
const ratechangeHandler = (e) => {
|
||||||
|
const playbackRate = e.target.playbackRate
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', {
|
||||||
|
event: 'ratechange',
|
||||||
|
data: playbackRate
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.videoEl.removeEventListener('ratechange', ratechangeHandler)
|
||||||
|
this.videoEl.addEventListener('ratechange', ratechangeHandler)
|
||||||
|
|
||||||
|
// 全屏事件监听
|
||||||
|
if (this.isApple()) {
|
||||||
|
const webkitbeginfullscreenHandler = () => {
|
||||||
|
const presentationMode = this.videoEl.webkitPresentationMode
|
||||||
|
let isFullScreen = null
|
||||||
|
if (presentationMode === 'fullscreen') {
|
||||||
|
isFullScreen = true
|
||||||
|
} else {
|
||||||
|
isFullScreen = false
|
||||||
|
}
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', {
|
||||||
|
event: 'fullscreenchange',
|
||||||
|
data: isFullScreen
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.videoEl.removeEventListener('webkitpresentationmodechanged', webkitbeginfullscreenHandler)
|
||||||
|
this.videoEl.addEventListener('webkitpresentationmodechanged', webkitbeginfullscreenHandler)
|
||||||
|
} else {
|
||||||
|
const fullscreenchangeHandler = () => {
|
||||||
|
let isFullScreen = null
|
||||||
|
if (document.fullscreenElement) {
|
||||||
|
isFullScreen = true
|
||||||
|
} else {
|
||||||
|
isFullScreen = false
|
||||||
|
}
|
||||||
|
this.$ownerInstance.callMethod('eventEmit', {
|
||||||
|
event: 'fullscreenchange',
|
||||||
|
data: isFullScreen
|
||||||
|
})
|
||||||
|
}
|
||||||
|
document.removeEventListener('fullscreenchange', fullscreenchangeHandler)
|
||||||
|
document.addEventListener('fullscreenchange', fullscreenchangeHandler)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 加载首帧视频,模拟出封面图
|
||||||
|
loadFirstFrame() {
|
||||||
|
let { autoplay, muted } = this.renderProps
|
||||||
|
if (this.isApple()) {
|
||||||
|
this.videoEl.play()
|
||||||
|
if (!autoplay) {
|
||||||
|
this.videoEl.pause()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// optimize: timeout 延迟调用是为了规避控制台的`https://goo.gl/LdLk22`这个报错
|
||||||
|
/**
|
||||||
|
* 原因:chromium 内核中,谷歌协议规定,视频不允许在非静音状态下进行自动播放
|
||||||
|
* 解决:在自动播放时,先将视频静音,然后延迟调用 play 方法,播放视频
|
||||||
|
* 说明:iOS 的 Safari 内核不会有这个,仅在 Android 设备出现,即使有这个报错也不影响的,所以不介意控制台报错的话是可以删掉这个 timeout 的
|
||||||
|
*/
|
||||||
|
this.videoEl.muted = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.videoEl.play()
|
||||||
|
this.videoEl.muted = muted
|
||||||
|
if (!autoplay) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.videoEl.pause()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
triggerCommand(eventType) {
|
||||||
|
if (eventType) {
|
||||||
|
this.$ownerInstance.callMethod('resetEventCommand')
|
||||||
|
this.videoEl && this.videoEl[eventType]()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
triggerFunc(func) {
|
||||||
|
const { name, params } = func || {}
|
||||||
|
if (name) {
|
||||||
|
this[name](params)
|
||||||
|
this.$ownerInstance.callMethod('resetFunc')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeHandler() {
|
||||||
|
if (this.videoEl) {
|
||||||
|
this.videoEl.pause()
|
||||||
|
this.videoEl.src = ''
|
||||||
|
this.$ownerInstance.callMethod('setViewData', {
|
||||||
|
key: 'videoSrc',
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
this.videoEl.load()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fullScreenHandler() {
|
||||||
|
if (this.isApple()) {
|
||||||
|
this.videoEl.webkitEnterFullscreen()
|
||||||
|
} else {
|
||||||
|
this.videoEl.requestFullscreen()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toSeekHandler({ sec, isDelay }) {
|
||||||
|
const func = () => {
|
||||||
|
if (this.videoEl) {
|
||||||
|
this.videoEl.currentTime = sec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 延迟执行
|
||||||
|
if (isDelay) {
|
||||||
|
this.delayFunc = func
|
||||||
|
} else {
|
||||||
|
func()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 执行延迟函数
|
||||||
|
execDelayFunc() {
|
||||||
|
this.delayFunc && this.delayFunc()
|
||||||
|
this.delayFunc = null
|
||||||
|
},
|
||||||
|
viewportChange(props) {
|
||||||
|
this.renderProps = props
|
||||||
|
const { autoplay, muted, controls, loop, playbackRate } = props
|
||||||
|
if (this.videoEl) {
|
||||||
|
this.videoEl.autoplay = autoplay
|
||||||
|
this.videoEl.controls = controls
|
||||||
|
this.videoEl.loop = loop
|
||||||
|
this.videoEl.muted = muted
|
||||||
|
this.videoEl.playbackRate = playbackRate
|
||||||
|
}
|
||||||
|
},
|
||||||
|
randomNumChange(val) {
|
||||||
|
this.num = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.player-wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
2
main.js
|
|
@ -27,7 +27,7 @@ export function createApp() {
|
||||||
const systemInfo = uni.getStorageSync('systemInfo') || {}
|
const systemInfo = uni.getStorageSync('systemInfo') || {}
|
||||||
app.config.globalProperties.$system = systemInfo.platform == 'ios' ? 'iOS' : 'Android'
|
app.config.globalProperties.$system = systemInfo.platform == 'ios' ? 'iOS' : 'Android'
|
||||||
app.config.globalProperties.$systemInfo = systemInfo
|
app.config.globalProperties.$systemInfo = systemInfo
|
||||||
uni.setStorageSync('version', '1.0.0.sp9')
|
uni.setStorageSync('version', '1.0.1')
|
||||||
app.config.globalProperties.$version = uni.getStorageSync('version')
|
app.config.globalProperties.$version = uni.getStorageSync('version')
|
||||||
|
|
||||||
app.use(globalMethods);
|
app.use(globalMethods);
|
||||||
|
|
|
||||||
110
manifest.json
|
|
@ -1,37 +1,37 @@
|
||||||
{
|
{
|
||||||
"name" : "alipay-emulator",
|
"name": "alipay-emulator",
|
||||||
"appid" : "__UNI__D535736",
|
"appid": "__UNI__D535736",
|
||||||
"description" : "",
|
"description": "",
|
||||||
"versionName" : "1.0.0",
|
"versionName": "1.0.0",
|
||||||
"versionCode" : 100,
|
"versionCode": 100,
|
||||||
"transformPx" : false,
|
"transformPx": false,
|
||||||
/* 5+App特有相关 */
|
/* 5+App特有相关 */
|
||||||
"app-plus" : {
|
"app-plus": {
|
||||||
"darkmode" : false,
|
"darkmode": false,
|
||||||
"usingComponents" : true,
|
"usingComponents": true,
|
||||||
"nvueStyleCompiler" : "uni-app",
|
"nvueStyleCompiler": "uni-app",
|
||||||
"compilerVersion" : 3,
|
"compilerVersion": 3,
|
||||||
"splashscreen" : {
|
"splashscreen": {
|
||||||
"alwaysShowBeforeRender" : true,
|
"alwaysShowBeforeRender": true,
|
||||||
"waiting" : true,
|
"waiting": true,
|
||||||
"autoclose" : true,
|
"autoclose": true,
|
||||||
"delay" : 0
|
"delay": 0
|
||||||
},
|
},
|
||||||
"optimization" : {
|
"optimization": {
|
||||||
"subPackages" : true
|
"subPackages": true
|
||||||
},
|
},
|
||||||
"runmode" : "liberate", // 开启分包优化后,必须配置资源释放模式
|
"runmode": "liberate", // 开启分包优化后,必须配置资源释放模式
|
||||||
|
|
||||||
/* 模块配置 */
|
/* 模块配置 */
|
||||||
"modules" : {
|
"modules": {
|
||||||
"Camera" : {},
|
"Camera": {},
|
||||||
"Payment" : {}
|
"Payment": {},
|
||||||
|
"LivePusher": {}
|
||||||
},
|
},
|
||||||
/* 应用发布信息 */
|
/* 应用发布信息 */
|
||||||
"distribute" : {
|
"distribute": {
|
||||||
/* android打包配置 */
|
/* android打包配置 */
|
||||||
"android" : {
|
"android": {
|
||||||
"permissions" : [
|
"permissions": [
|
||||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
|
|
@ -50,46 +50,52 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
/* ios打包配置 */
|
/* ios打包配置 */
|
||||||
"ios" : {
|
"ios": {
|
||||||
"dSYMs" : false
|
"dSYMs": false
|
||||||
},
|
},
|
||||||
/* SDK配置 */
|
/* SDK配置 */
|
||||||
"sdkConfigs" : {
|
"sdkConfigs": {
|
||||||
"payment" : {
|
"payment": {
|
||||||
"weixin" : {
|
"weixin": {
|
||||||
"__platform__" : [ "ios", "android" ],
|
"__platform__": [
|
||||||
"appid" : "123456",
|
"ios",
|
||||||
"UniversalLinks" : "https://hhhhh.com/apple-app-site-association/"
|
"android"
|
||||||
|
],
|
||||||
|
"appid": "123456",
|
||||||
|
"UniversalLinks": "https://hhhhh.com/apple-app-site-association/"
|
||||||
},
|
},
|
||||||
"alipay" : {
|
"alipay": {
|
||||||
"__platform__" : [ "ios", "android" ]
|
"__platform__": [
|
||||||
|
"ios",
|
||||||
|
"android"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nvueLaunchMode" : ""
|
"nvueLaunchMode": ""
|
||||||
},
|
},
|
||||||
/* 快应用特有相关 */
|
/* 快应用特有相关 */
|
||||||
"quickapp" : {},
|
"quickapp": {},
|
||||||
/* 小程序特有相关 */
|
/* 小程序特有相关 */
|
||||||
"mp-weixin" : {
|
"mp-weixin": {
|
||||||
"appid" : "",
|
"appid": "",
|
||||||
"setting" : {
|
"setting": {
|
||||||
"urlCheck" : false
|
"urlCheck": false
|
||||||
},
|
},
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-alipay" : {
|
"mp-alipay": {
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-baidu" : {
|
"mp-baidu": {
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"mp-toutiao" : {
|
"mp-toutiao": {
|
||||||
"usingComponents" : true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
"uniStatistics" : {
|
"uniStatistics": {
|
||||||
"enable" : false
|
"enable": false
|
||||||
},
|
},
|
||||||
"vueVersion" : "3"
|
"vueVersion": "3"
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,607 @@
|
||||||
|
{
|
||||||
|
"name": "video-player 视频播放器 html5视频播放器-解决频层级、覆盖",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "video-player 视频播放器 html5视频播放器-解决频层级、覆盖",
|
||||||
|
"version": "2.0.0"
|
||||||
|
},
|
||||||
|
"node_modules/@dcloudio/types": {
|
||||||
|
"version": "3.4.28",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dcloudio/types/-/types-3.4.28.tgz",
|
||||||
|
"integrity": "sha512-uVIRp1VLBkrL0LaGLgIS/sT3bl1zzVTKZQbfqJEQcSAvBffRdirbSh5OvOHfA1WV5lmCAGfjhKsUQouNEVUQHg==",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@dcloudio/uni-app": {
|
||||||
|
"version": "2.0.2-4080720251210002",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dcloudio/uni-app/-/uni-app-2.0.2-4080720251210002.tgz",
|
||||||
|
"integrity": "sha512-FUw/bJJwBPl/scKBog21wusICrU9T4TFwyKKVbtNbWIO98IBzjRIin0mmwzhqHNL1kZO0LbwnoRe7LtusW8LQw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@dcloudio/types": "^3.0.15",
|
||||||
|
"@vue/composition-api": "^1.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dcloudio/uni-cli-shared": {
|
||||||
|
"version": "2.0.2-4080720251210002",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dcloudio/uni-cli-shared/-/uni-cli-shared-2.0.2-4080720251210002.tgz",
|
||||||
|
"integrity": "sha512-EIvkRTpB/aRP3bZZYAtiBdXrM1IBqhJDgCf13TAuE84F9ma1b2kD088yAi2Hf0hqv3X+368eX5E2aqyficSZvg==",
|
||||||
|
"dependencies": {
|
||||||
|
"escape-string-regexp": "^4.0.0",
|
||||||
|
"fast-glob": "^3.2.11",
|
||||||
|
"fs-extra": "^10.0.0",
|
||||||
|
"glob-escape": "^0.0.2",
|
||||||
|
"hash-sum": "^1.0.2",
|
||||||
|
"postcss-urlrewrite": "^0.2.2",
|
||||||
|
"strip-json-comments": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dcloudio/uni-ui": {
|
||||||
|
"version": "1.5.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dcloudio/uni-ui/-/uni-ui-1.5.11.tgz",
|
||||||
|
"integrity": "sha512-DBtk046ofmeFd82zRI7d89SoEwrAxYzUN3WVPm1DIBkpLPG5F5QDNkHMnZGu2wNrMEmGBjBpUh3vqEY1L3jaMw=="
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
|
"version": "2.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "2.0.5",
|
||||||
|
"run-parallel": "^1.1.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.stat": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.walk": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.scandir": "2.1.5",
|
||||||
|
"fastq": "^1.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/composition-api": {
|
||||||
|
"version": "1.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/composition-api/-/composition-api-1.7.2.tgz",
|
||||||
|
"integrity": "sha512-M8jm9J/laYrYT02665HkZ5l2fWTK4dcVg3BsDHm/pfz+MjDYwX+9FUaZyGwEyXEDonQYRCo0H7aLgdklcIELjw==",
|
||||||
|
"peer": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": ">= 2.5 < 2.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/braces": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
|
"dependencies": {
|
||||||
|
"fill-range": "^7.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/copy-anything": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-what": "^3.14.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/mesqueeb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/crypto-js": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
||||||
|
},
|
||||||
|
"node_modules/errno": {
|
||||||
|
"version": "0.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
|
||||||
|
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"prr": "~1.0.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"errno": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/escape-string-regexp": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-glob": {
|
||||||
|
"version": "3.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||||
|
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
|
"@nodelib/fs.walk": "^1.2.3",
|
||||||
|
"glob-parent": "^5.1.2",
|
||||||
|
"merge2": "^1.3.0",
|
||||||
|
"micromatch": "^4.0.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fastq": {
|
||||||
|
"version": "1.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
|
||||||
|
"integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
|
||||||
|
"dependencies": {
|
||||||
|
"reusify": "^1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fill-range": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
|
"dependencies": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob-escape": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-escape/-/glob-escape-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob-parent": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-glob": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||||
|
},
|
||||||
|
"node_modules/hash-sum": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA=="
|
||||||
|
},
|
||||||
|
"node_modules/iconv-lite": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/image-size": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"image-size": "bin/image-size.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-extglob": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-glob": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-extglob": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-number": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-what": {
|
||||||
|
"version": "3.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
|
||||||
|
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/less": {
|
||||||
|
"version": "4.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/less/-/less-4.5.1.tgz",
|
||||||
|
"integrity": "sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"copy-anything": "^2.0.1",
|
||||||
|
"parse-node-version": "^1.0.1",
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"lessc": "bin/lessc"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"errno": "^0.1.1",
|
||||||
|
"graceful-fs": "^4.1.2",
|
||||||
|
"image-size": "~0.5.0",
|
||||||
|
"make-dir": "^2.1.0",
|
||||||
|
"mime": "^1.4.1",
|
||||||
|
"needle": "^3.1.0",
|
||||||
|
"source-map": "~0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/less-loader": {
|
||||||
|
"version": "12.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.3.0.tgz",
|
||||||
|
"integrity": "sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18.12.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/webpack"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@rspack/core": "0.x || 1.x",
|
||||||
|
"less": "^3.5.0 || ^4.0.0",
|
||||||
|
"webpack": "^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@rspack/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"webpack": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lottie-web": {
|
||||||
|
"version": "5.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.13.0.tgz",
|
||||||
|
"integrity": "sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ=="
|
||||||
|
},
|
||||||
|
"node_modules/make-dir": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"pify": "^4.0.1",
|
||||||
|
"semver": "^5.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/merge2": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/micromatch": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
|
"dependencies": {
|
||||||
|
"braces": "^3.0.3",
|
||||||
|
"picomatch": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"mime": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/needle": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": "^0.6.3",
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"needle": "bin/needle"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.4.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parse-node-version": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pify": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postcss-helpers": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-helpers/-/postcss-helpers-0.3.3.tgz",
|
||||||
|
"integrity": "sha512-VumiUcrpbxGlTBNQj6fUOkb/HNRUk/xYz8bNlhgVOdvk3yWEy4B+0nlDUZZM9mTVZ5bJoxUy7WT6z/4E7oMTgw==",
|
||||||
|
"dependencies": {
|
||||||
|
"urijs": "^1.18.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postcss-urlrewrite": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-urlrewrite/-/postcss-urlrewrite-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-DxPSgykgHjoV4Z+ygvq2C5HkiuiKQQD74xpoNQSQuyi8zab9nODVtNKfnCN6BEv9VZrjpOGLGAf8BDvgG6EtHg==",
|
||||||
|
"dependencies": {
|
||||||
|
"postcss-helpers": "^0.3.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prr": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/queue-microtask": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/reusify": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
||||||
|
"engines": {
|
||||||
|
"iojs": ">=1.0.0",
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/run-parallel": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"queue-microtask": "^1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-json-comments": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/to-regex-range": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-number": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/urijs": {
|
||||||
|
"version": "1.19.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
|
||||||
|
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
|
||||||
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "13.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
||||||
|
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist-node/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue": {
|
||||||
|
"version": "2.6.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz",
|
||||||
|
"integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==",
|
||||||
|
"deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
|
||||||
|
"peer": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
package.json
|
|
@ -1,24 +1,20 @@
|
||||||
{
|
{
|
||||||
"name": "alipay-emulator",
|
"id": "lius-DomVideoPlayer",
|
||||||
"version": "1.0.0",
|
"name": "video-player 视频播放器 html5视频播放器-解决频层级、覆盖",
|
||||||
"description": "",
|
"displayName": "video-player 视频播放器 html5视频播放器-解决频层级、覆盖",
|
||||||
"main": "main.js",
|
"version": "2.0.0",
|
||||||
"scripts": {
|
"description": "APP 项目中,uniapp 提供的的 video 原生视频组件层级太高,难以遮挡;该视频播放器可以被其他元素进行覆盖、遮挡,页面具有更高的定制性。",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"keywords": [
|
||||||
},
|
"video-player",
|
||||||
"keywords": [],
|
"视频播放器",
|
||||||
"author": "",
|
"视频覆盖",
|
||||||
"license": "ISC",
|
"视频层级",
|
||||||
"devDependencies": {
|
"视频遮挡"
|
||||||
"less": "^4.5.1",
|
],
|
||||||
"less-loader": "^12.3.0"
|
"dcloudext": {
|
||||||
},
|
"category": [
|
||||||
"dependencies": {
|
"前端组件",
|
||||||
"@dcloudio/uni-app": "^2.0.2-4080720251210002",
|
"通用组件"
|
||||||
"@dcloudio/uni-cli-shared": "^2.0.2-4080720251210002",
|
]
|
||||||
"@dcloudio/uni-ui": "^1.5.11",
|
|
||||||
"crypto-js": "^4.2.0",
|
|
||||||
"lottie-web": "^5.13.0",
|
|
||||||
"uuid": "^13.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
15
pages.json
|
|
@ -135,6 +135,21 @@
|
||||||
"navigationStyle": "custom",
|
"navigationStyle": "custom",
|
||||||
"navigationBarTextStyle": "white"
|
"navigationBarTextStyle": "white"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "air-tickets/edit/edit",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "修改机票信息",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path" : "tickets-app/index",
|
||||||
|
"style" :
|
||||||
|
{
|
||||||
|
"navigationBarTitleText" : "选择机票/火车票平台",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1111,7 +1111,7 @@
|
||||||
goods_id: data.goods.goods_id,
|
goods_id: data.goods.goods_id,
|
||||||
coupon: data.active_id ? data.active_id : '',
|
coupon: data.active_id ? data.active_id : '',
|
||||||
pay_type: paymentMethod.value == "wxpay" ? 'weixin' : "alipay",
|
pay_type: paymentMethod.value == "wxpay" ? 'weixin' : "alipay",
|
||||||
"pay_source": paymentMethod.value == "alipay" && proxy.$system == 'iOS' ? "h5" : "app",
|
"pay_source": "app",
|
||||||
source: "uni_alipay",
|
source: "uni_alipay",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
<view class="content-box" :style="{ height: windowHeight + 'px' }">
|
<view class="content-box" :style="{ height: windowHeight + 'px' }">
|
||||||
|
|
||||||
<scroll-view scroll-y="true" class="scroll-view"
|
<scroll-view scroll-y="true" class="scroll-view"
|
||||||
:style="{ height: (windowHeight - statusBarHeight - 44) + 'px', marginTop: (statusBarHeight + 44) + 'px' }"
|
:style="{ height: (windowHeight - statusBarHeight - 44) + 'px', marginTop: (statusBarHeight + 44) + 'px' }"
|
||||||
@scroll="handleScroll">
|
@scroll="handleScroll">
|
||||||
|
|
@ -27,7 +28,7 @@
|
||||||
left: '0px',
|
left: '0px',
|
||||||
width: '60px',
|
width: '60px',
|
||||||
height: '44px',
|
height: '44px',
|
||||||
zIndex: 10000,
|
'z-index': 10000,
|
||||||
backgroundColor: 'transparent'
|
backgroundColor: 'transparent'
|
||||||
}">
|
}">
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -122,13 +123,16 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- <view style="border: 1px solid red; text-align: center;padding: 10px;" @click="exit">退出模拟器</view> -->
|
||||||
|
|
||||||
<view class="footer-box" :class="{ 'ios-padding-bottom': platform === 'ios' }">
|
<view class="footer-box" :class="{ 'ios-padding-bottom': platform === 'ios' }">
|
||||||
<text class="vision-text">版本:{{ vision }}</text>
|
<text class="vision-text">版本:{{ vision }}</text>
|
||||||
<text class="vision-text margin-l-6" v-if="qqgroup.enable">{{ qqgroup.text }}</text>
|
<text class="vision-text margin-l-6" v-if="qqgroup.enable">{{ qqgroup.text }}</text>
|
||||||
<text class="vision-text" @click="copyNumber(qqgroup.number)">{{ qqgroup.number }}</text>
|
<text class="vision-text" @click="copyNumber(qqgroup.number)">{{ qqgroup.number }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -221,7 +225,7 @@ const menuList = [{
|
||||||
const otherList = [{
|
const otherList = [{
|
||||||
icon: "/static/image/index/qita/jipiao.png",
|
icon: "/static/image/index/qita/jipiao.png",
|
||||||
name: "机票",
|
name: "机票",
|
||||||
path: ""
|
path: "/pages/other/tickets-app/index"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "/static/image/index/qita/huochepiao.png",
|
icon: "/static/image/index/qita/huochepiao.png",
|
||||||
|
|
@ -395,7 +399,11 @@ const setUserData = () => {
|
||||||
// 启动走马灯
|
// 启动走马灯
|
||||||
startMarquee();
|
startMarquee();
|
||||||
data.videoHelpList = configData.config['client.uniapp.alipay.video_help'] || []
|
data.videoHelpList = configData.config['client.uniapp.alipay.video_help'] || []
|
||||||
data.qqgroup = configData.config['client.uniapp.qqgroup'] || { enable: false, number: "", text: "" }
|
data.qqgroup = configData.config['client.uniapp.qqgroup'] || {
|
||||||
|
enable: false,
|
||||||
|
number: "",
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
data.noticeInfo = {
|
data.noticeInfo = {
|
||||||
text: '加载中...',
|
text: '加载中...',
|
||||||
|
|
@ -472,22 +480,15 @@ const copyNumber = (number) => {
|
||||||
* 退出模拟器
|
* 退出模拟器
|
||||||
*/
|
*/
|
||||||
const exit = () => {
|
const exit = () => {
|
||||||
console.log("点击退出按钮")
|
console.log("退出模拟器")
|
||||||
// #ifdef APP-PLUS
|
// 判断是否为 iOS 环境
|
||||||
if (typeof plus !== 'undefined' && plus.runtime) {
|
if (uni.getSystemInfoSync().platform === 'ios') {
|
||||||
console.log("执行退出应用")
|
uni.sendNativeEvent('closeUniAPP', "closeUniAPP", ret => {
|
||||||
plus.runtime.quit()
|
console.log('宿主App回传的数据:' + ret);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log("plus对象未定义")
|
plus.runtime.quit();
|
||||||
}
|
}
|
||||||
// #endif
|
|
||||||
// #ifndef APP-PLUS
|
|
||||||
console.log("非APP环境,无法退出")
|
|
||||||
uni.showToast({
|
|
||||||
title: '仅APP环境支持退出',
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
// #endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const noticeContainer = ref(null);
|
const noticeContainer = ref(null);
|
||||||
|
|
@ -618,7 +619,6 @@ onHide(() => {
|
||||||
onUnload(() => {
|
onUnload(() => {
|
||||||
stopMarquee();
|
stopMarquee();
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.container {
|
.container {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"orderInfo": {
|
||||||
|
"price": "714",
|
||||||
|
"orderNo": "203012348597",
|
||||||
|
"orderTime": "2025-07-29 06:55"
|
||||||
|
},
|
||||||
|
"flightInfo": {
|
||||||
|
"airline": "南方航空",
|
||||||
|
"airlineCode": "CZ",
|
||||||
|
"flightNumber": "CZ2355",
|
||||||
|
"date": "2025-03-04",
|
||||||
|
"startTime": "08:00",
|
||||||
|
"endTime": "10:00",
|
||||||
|
"startCity": "武汉",
|
||||||
|
"endCity": "北京",
|
||||||
|
"startAirport": "天河机场T3",
|
||||||
|
"endAirport": "北京大兴机场",
|
||||||
|
"duration": "2时5分",
|
||||||
|
"aircraftType": "737-800",
|
||||||
|
"seatCategory": "头等舱",
|
||||||
|
"onTimeRate": "100%",
|
||||||
|
"meal": "正餐",
|
||||||
|
"luggageCheckInQuota": "含20kg免费托运行李"
|
||||||
|
},
|
||||||
|
"ticketNumber": "12345678901",
|
||||||
|
"passengersInfo": [
|
||||||
|
{
|
||||||
|
"name": "张三",
|
||||||
|
"idType": "身份证",
|
||||||
|
"idNumber": "123456789012345678",
|
||||||
|
"ticketNo": "12345678901"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "张三",
|
||||||
|
"idType": "身份证",
|
||||||
|
"idNumber": "123456789012345678",
|
||||||
|
"ticketNo": "12345678901"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,430 @@
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<NavBar title="修改机票信息" bgColor="#F5F5F5" isRightButton @right-click="handleRightButtonClick"></NavBar>
|
||||||
|
|
||||||
|
<scroll-view scroll-y class="form-content">
|
||||||
|
<!-- 订单信息 -->
|
||||||
|
<view class="section-container">
|
||||||
|
<view class="section-header" @click="toggleSection('orderInfo')">
|
||||||
|
<text class="section-title">订单信息</text>
|
||||||
|
<uni-icons :type="collapsed.orderInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view class="card" v-show="!collapsed.orderInfo">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">订单价格</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.orderInfo.price" />
|
||||||
|
</view>
|
||||||
|
<!-- 全局票号 -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">联系电话</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.ticketNumber" placeholder="用于掩码显示" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">订单号</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.orderInfo.orderNo" />
|
||||||
|
</view>
|
||||||
|
<uni-datetime-picker type="datetime" v-model="ticketsInfo.orderInfo.orderTime" :border="false">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">下单时间</text>
|
||||||
|
<view class="input">{{ ticketsInfo.orderInfo.orderTime }}</view>
|
||||||
|
</view>
|
||||||
|
</uni-datetime-picker>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 航班信息 -->
|
||||||
|
<view class="section-container">
|
||||||
|
<view class="section-header" @click="toggleSection('flightInfo')">
|
||||||
|
<text class="section-title">航班信息</text>
|
||||||
|
<uni-icons :type="collapsed.flightInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view class="card" v-show="!collapsed.flightInfo">
|
||||||
|
<picker mode="selector" :range="airLineList" range-key="name" @change="onAirlineChange">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">航空公司</text>
|
||||||
|
<view class="input">{{ ticketsInfo.flightInfo.airline }}</view>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">航班号</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.flightNumber" />
|
||||||
|
</view>
|
||||||
|
<picker mode="date" fields="day" :value="ticketsInfo.flightInfo.date" @change="onFlightDateChange">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">日期</text>
|
||||||
|
<view class="input">{{ ticketsInfo.flightInfo.date }}</view>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
<picker mode="multiSelector" :range="timeRange"
|
||||||
|
:value="getTimeIndex(ticketsInfo.flightInfo.startTime)" @change="onStartTimeChange">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">起飞时间</text>
|
||||||
|
<view class="input">{{ ticketsInfo.flightInfo.startTime }}</view>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
<picker mode="multiSelector" :range="timeRange"
|
||||||
|
:value="getTimeIndex(ticketsInfo.flightInfo.endTime)" @change="onEndTimeChange">
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">到达时间</text>
|
||||||
|
<view class="input">{{ ticketsInfo.flightInfo.endTime }}</view>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">出发城市</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.startCity" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">到达城市</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.endCity" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">出发机场</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.startAirport" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">到达机场</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.endAirport" />
|
||||||
|
</view>
|
||||||
|
<!-- <view class="form-item">
|
||||||
|
<text class="label">时长</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.duration" placeholder="例: 2时5分" />
|
||||||
|
</view> -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">机型</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.aircraftType" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">舱位</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.seatCategory" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">准点率</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.onTimeRate" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">餐食</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.meal" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">行李额</text>
|
||||||
|
<input class="input" v-model="ticketsInfo.flightInfo.luggageCheckInQuota" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 乘客信息 -->
|
||||||
|
<view class="section-container">
|
||||||
|
<view class="section-header" @click="toggleSection('passengersInfo')">
|
||||||
|
<text class="section-title">乘客信息 ({{ ticketsInfo.passengersInfo.length }}人)</text>
|
||||||
|
<uni-icons :type="collapsed.passengersInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-show="!collapsed.passengersInfo">
|
||||||
|
<view class="card" v-for="(passenger, index) in ticketsInfo.passengersInfo" :key="index">
|
||||||
|
<view class="card-header-row">
|
||||||
|
<text class="card-header">乘客 {{ index + 1 }}</text>
|
||||||
|
<text class="delete-btn" @click="removePassenger(index)">删除</text>
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">姓名</text>
|
||||||
|
<input class="input" v-model="passenger.name" />
|
||||||
|
</view>
|
||||||
|
<!-- <view class="form-item">
|
||||||
|
<text class="label">证件类型</text>
|
||||||
|
<input class="input" v-model="passenger.idType" />
|
||||||
|
</view> -->
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">身份证号</text>
|
||||||
|
<input class="input" v-model="passenger.idNumber" />
|
||||||
|
</view>
|
||||||
|
<view class="form-item">
|
||||||
|
<text class="label">票号</text>
|
||||||
|
<input class="input" v-model="passenger.ticketNo" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="add-btn-box" @click="addPassenger">
|
||||||
|
<uni-icons type="plusempty" size="20" color="#1677FF"></uni-icons>
|
||||||
|
<text class="add-text">添加乘客</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="placeholder"></view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import NavBar from '@/components/nav-bar/nav-bar.vue'
|
||||||
|
import { reactive, toRefs, onMounted } from 'vue';
|
||||||
|
import defualtData from '@/pages/other/air-tickets/commom/defualt.json';
|
||||||
|
import airlineJson from '@/static/json/air-line.json';
|
||||||
|
|
||||||
|
const airLineList = airlineJson.airLine;
|
||||||
|
|
||||||
|
const data = reactive({
|
||||||
|
ticketsInfo: JSON.parse(JSON.stringify(defualtData)),
|
||||||
|
collapsed: {
|
||||||
|
orderInfo: false,
|
||||||
|
flightInfo: false,
|
||||||
|
passengersInfo: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { ticketsInfo, collapsed } = toRefs(data)
|
||||||
|
|
||||||
|
const onAirlineChange = (e) => {
|
||||||
|
const index = e.detail.value;
|
||||||
|
const selected = airLineList[index];
|
||||||
|
if (selected) {
|
||||||
|
data.ticketsInfo.flightInfo.airline = selected.name;
|
||||||
|
data.ticketsInfo.flightInfo.airlineCode = selected.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const stored = uni.getStorageSync('airTicketsInfo')
|
||||||
|
if (stored) {
|
||||||
|
// Deep merge or just assign if structure matches
|
||||||
|
Object.assign(data.ticketsInfo, stored)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const toggleSection = (key) => {
|
||||||
|
data.collapsed[key] = !data.collapsed[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRightButtonClick = () => {
|
||||||
|
const orderInfo = data.ticketsInfo.orderInfo;
|
||||||
|
const flightInfo = data.ticketsInfo.flightInfo;
|
||||||
|
|
||||||
|
if (orderInfo.orderTime && flightInfo.date && flightInfo.startTime) {
|
||||||
|
const orderDate = new Date(orderInfo.orderTime.replace(/-/g, '/'));
|
||||||
|
const flightDate = new Date((flightInfo.date + ' ' + flightInfo.startTime).replace(/-/g, '/'));
|
||||||
|
|
||||||
|
if (orderDate > flightDate) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '下单时间不能晚于起飞时间',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('data.ticketsInfo', data.ticketsInfo)
|
||||||
|
uni.setStorageSync('airTicketsInfo', data.ticketsInfo)
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
const addPassenger = () => {
|
||||||
|
const lastP = data.ticketsInfo.passengersInfo[data.ticketsInfo.passengersInfo.length - 1]
|
||||||
|
data.ticketsInfo.passengersInfo.push({
|
||||||
|
name: '新乘客',
|
||||||
|
idType: lastP ? lastP.idType : '身份证',
|
||||||
|
idNumber: lastP ? lastP.idNumber : '123123********6352',
|
||||||
|
ticketNo: lastP ? lastP.ticketNo : '12345678901'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const removePassenger = (index) => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要删除该乘客吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
data.ticketsInfo.passengersInfo.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to extract YYYY-MM-DD from YYYY-MM-DD HH:mm or similar
|
||||||
|
const getDateFromStr = (str) => {
|
||||||
|
if (!str) return ''
|
||||||
|
// Try to match date pattern
|
||||||
|
const dateMatch = str.match(/\d{4}[-.]\d{2}[-.]\d{2}/)
|
||||||
|
if (dateMatch) return dateMatch[0].replace(/\./g, '-')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOrderDateChange = (e) => {
|
||||||
|
const val = e.detail.value // YYYY-MM-DD
|
||||||
|
// orderTime format in default is "YYYY-MM-DD HH:mm"
|
||||||
|
// We need to keep the time part if possible, or default to current time
|
||||||
|
let oldTime = '00:00'
|
||||||
|
if (data.ticketsInfo.orderInfo.orderTime && data.ticketsInfo.orderInfo.orderTime.includes(' ')) {
|
||||||
|
oldTime = data.ticketsInfo.orderInfo.orderTime.split(' ')[1]
|
||||||
|
}
|
||||||
|
data.ticketsInfo.orderInfo.orderTime = val + ' ' + oldTime
|
||||||
|
}
|
||||||
|
|
||||||
|
const onFlightDateChange = (e) => {
|
||||||
|
data.ticketsInfo.flightInfo.date = e.detail.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom Time Picker Data
|
||||||
|
const hours = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, '0'));
|
||||||
|
const minutes = Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0'));
|
||||||
|
const timeRange = [hours, minutes];
|
||||||
|
|
||||||
|
const getTimeIndex = (timeStr) => {
|
||||||
|
if (!timeStr) return [0, 0];
|
||||||
|
const [h, m] = timeStr.split(':');
|
||||||
|
const hIndex = hours.findIndex(item => item === h);
|
||||||
|
const mIndex = minutes.findIndex(item => item === m);
|
||||||
|
return [hIndex === -1 ? 0 : hIndex, mIndex === -1 ? 0 : mIndex];
|
||||||
|
};
|
||||||
|
|
||||||
|
const onStartTimeChange = (e) => {
|
||||||
|
const [hIndex, mIndex] = e.detail.value;
|
||||||
|
const time = `${hours[hIndex]}:${minutes[mIndex]}`;
|
||||||
|
data.ticketsInfo.flightInfo.startTime = time;
|
||||||
|
updateDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
const onEndTimeChange = (e) => {
|
||||||
|
const [hIndex, mIndex] = e.detail.value;
|
||||||
|
const time = `${hours[hIndex]}:${minutes[mIndex]}`;
|
||||||
|
data.ticketsInfo.flightInfo.endTime = time;
|
||||||
|
updateDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateDuration = () => {
|
||||||
|
// Simple calc if same day or we can just leave it to user to input manually (which we support via input)
|
||||||
|
// But let's try a best effort calc
|
||||||
|
const ft = data.ticketsInfo.flightInfo
|
||||||
|
if (ft.startTime && ft.endTime) {
|
||||||
|
const [sh, sm] = ft.startTime.split(':').map(Number)
|
||||||
|
const [eh, em] = ft.endTime.split(':').map(Number)
|
||||||
|
let diffMin = (eh * 60 + em) - (sh * 60 + sm)
|
||||||
|
if (diffMin < 0) diffMin += 24 * 60 // Assume next day if cross midnight
|
||||||
|
|
||||||
|
const h = Math.floor(diffMin / 60)
|
||||||
|
const m = diffMin % 60
|
||||||
|
ft.duration = `${h}时${m}分`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import "@/common/main.css";
|
||||||
|
|
||||||
|
page {
|
||||||
|
background-color: #F8F8F8;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-content {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
padding: 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-container {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx 12rpx 16rpx;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.card-header-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx 0 12rpx;
|
||||||
|
border-bottom: 1rpx solid #f5f5f5;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #FF4D4F;
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
border-bottom: 1rpx solid #F5F5F5;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333;
|
||||||
|
width: 240rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn-box {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border: 2rpx dashed #1677FF;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
|
.add-text {
|
||||||
|
color: #1677FF;
|
||||||
|
font-size: 30rpx;
|
||||||
|
margin-left: 8rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
height: 60rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
<template>
|
||||||
|
<view class="container">
|
||||||
|
<NavBar title="选择机票" bgColor="#F0F4F9" isBack></NavBar>
|
||||||
|
|
||||||
|
<view class="content">
|
||||||
|
<view class="app-card" v-for="(item, index) in appList" :key="index" @click="handleItemClick(item)">
|
||||||
|
<!-- Background Watermark -->
|
||||||
|
<image class="watermark" :src="item.bgImage" mode="heightFix"></image>
|
||||||
|
|
||||||
|
<!-- Front Content -->
|
||||||
|
<view class="card-left">
|
||||||
|
<view class="logo-box">
|
||||||
|
<image class="logo" :src="item.logo" mode="aspectFit"></image>
|
||||||
|
<!-- Hot Tag for Fliggy -->
|
||||||
|
<view v-if="item.isHot" class="hot-tag">
|
||||||
|
<image style="width: 72rpx;height:32rpx" src="/static/image/index/hot-icon.png"
|
||||||
|
mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="app-name">{{ item.name }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<uni-icons type="right" size="18" color="#CCCCCC"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import NavBar from '@/components/nav-bar/nav-bar.vue';
|
||||||
|
import { reactive, toRefs } from 'vue';
|
||||||
|
import { util } from '@/utils/common.js'; // Assuming util exists for navigation, similar to previous tasks
|
||||||
|
|
||||||
|
const appList = [
|
||||||
|
{
|
||||||
|
name: '去哪儿APP',
|
||||||
|
logo: '/static/image/other/tickets-app/qvnar-logo.png',
|
||||||
|
bgImage: '/static/image/other/tickets-app/qvnar-bg.png',
|
||||||
|
path: '/pages/other/air-tickets/qunar-air-tickets/qunar-air-tickets',
|
||||||
|
isHot: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '飞猪APP',
|
||||||
|
logo: '/static/image/other/tickets-app/fliggy-logo.png',
|
||||||
|
bgImage: '/static/image/other/tickets-app/fliggy-bg.png',
|
||||||
|
path: '',
|
||||||
|
isHot: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '携程APP',
|
||||||
|
logo: '/static/image/other/tickets-app/trip-com-logo.png',
|
||||||
|
bgImage: '/static/image/other/tickets-app/trip-com-bg.png',
|
||||||
|
path: '',
|
||||||
|
isHot: false
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: '铁路12306',
|
||||||
|
// logo: '/static/image/other/tickets-app/12306-logo.png',
|
||||||
|
// bgImage: '/static/image/other/tickets-app/12306-bg.png',
|
||||||
|
// path: '/pages/other/train-tickets/12306-tickets/12306-tickets',
|
||||||
|
// isHot: false
|
||||||
|
// }
|
||||||
|
]
|
||||||
|
|
||||||
|
const handleItemClick = (item) => {
|
||||||
|
if (item.path) {
|
||||||
|
util.goPage(item.path)
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '开发中',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #F0F4F9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-card {
|
||||||
|
position: relative;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
height: 188rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 24rpx 0 32rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.02);
|
||||||
|
|
||||||
|
.watermark {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 80%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
.logo-box {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 28rpx;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 108rpx;
|
||||||
|
height: 108rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
display: block; // Remove extra space
|
||||||
|
}
|
||||||
|
|
||||||
|
.hot-tag {
|
||||||
|
position: absolute;
|
||||||
|
top: -12rpx;
|
||||||
|
right: -30rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-name {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #3D3D3D;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -33,11 +33,13 @@
|
||||||
<view class="video-grid" :class="videoGridClass">
|
<view class="video-grid" :class="videoGridClass">
|
||||||
<view class="video-item" v-for="(item, index) in videoData.videoList" :key="index"
|
<view class="video-item" v-for="(item, index) in videoData.videoList" :key="index"
|
||||||
:class="{ 'dragging': data.dragState.draggingIndex === index }" :style="getItemStyle(index)"
|
:class="{ 'dragging': data.dragState.draggingIndex === index }" :style="getItemStyle(index)"
|
||||||
@touchstart="data.isEdit ? handleTouchStart($event, index) : null"
|
@touchstart="handleTouchStart($event, index)" @touchmove.prevent="handleTouchMove($event, index)"
|
||||||
@touchmove.prevent="data.isEdit ? handleTouchMove($event, index) : null"
|
@touchend="handleTouchEnd($event, index)" @click.stop="changeVideoOrImage(item, index)">
|
||||||
@touchend="data.isEdit ? handleTouchEnd($event, index) : null" @click.stop="changeIconType(item)">
|
<image v-if="item.preview" class="video-preview" :src="item.preview" mode="aspectFill"></image>
|
||||||
<image class="video-preview" :src="item.preview" mode="aspectFill"></image>
|
<DomVideoPlayer v-else-if="item.videoUrl" :ref="`videoPlayer${index}`" class="video-preview"
|
||||||
<view class="video-overlay">
|
:src="item.videoUrl" objectFit="cover" autoplay loop muted :isLoading="true" />
|
||||||
|
<view v-else class="video-preview" style="background-color: #F3F3F3;"></view>
|
||||||
|
<view class="video-overlay" @click.stop="changeIconType(item, index)">
|
||||||
<image class="mute-icon" v-if="item.iconType > 0"
|
<image class="mute-icon" v-if="item.iconType > 0"
|
||||||
:src="`/static/image/other/video-call/${item.iconType == 1 ? 'mute' : 'unmute'}.png`">
|
:src="`/static/image/other/video-call/${item.iconType == 1 ? 'mute' : 'unmute'}.png`">
|
||||||
</image>
|
</image>
|
||||||
|
|
@ -118,13 +120,29 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部选项弹窗 -->
|
||||||
|
<view v-if="data.showMediaSelector" class="action-sheet-overlay" @click="closeMediaSelector">
|
||||||
|
<view class="action-sheet" @click.stop>
|
||||||
|
<view class="action-sheet-item" @click="chooseVideo">
|
||||||
|
<text class="action-sheet-text">选择视频</text>
|
||||||
|
</view>
|
||||||
|
<view class="action-sheet-item" @click="chooseImage">
|
||||||
|
<text class="action-sheet-text">选择图片</text>
|
||||||
|
</view>
|
||||||
|
<view class="action-sheet-divider"></view>
|
||||||
|
<view class="action-sheet-item" @click="closeMediaSelector">
|
||||||
|
<text class="action-sheet-text action-sheet-cancel">取消</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import NavBar from "@/components/nav-bar/nav-bar.vue"
|
import NavBar from "@/components/nav-bar/nav-bar.vue"
|
||||||
|
|
||||||
import { ref, toRefs, onMounted, onUnmounted, reactive, computed } from 'vue'
|
import { ref, toRefs, onMounted, onUnmounted, reactive, computed } from 'vue'
|
||||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
|
||||||
import { util } from '@/utils/common.js'
|
import { util } from '@/utils/common.js'
|
||||||
|
|
||||||
const buttonGroup = [
|
const buttonGroup = [
|
||||||
|
|
@ -150,9 +168,9 @@ const data = reactive({
|
||||||
mainVideoIndex: 0,
|
mainVideoIndex: 0,
|
||||||
timeText: '125:22',
|
timeText: '125:22',
|
||||||
videoList: [
|
videoList: [
|
||||||
{ preview: '/static/image/other/video-call/defualt/video-img1.png', iconType: 0 },
|
{ preview: '/static/image/other/video-call/defualt/video-img1.png', videoUrl: '', savedVideoUrl: '', iconType: 0 },
|
||||||
{ preview: '/static/image/other/video-call/defualt/video-img2.png', iconType: 1 },
|
{ preview: '/static/image/other/video-call/defualt/video-img2.png', videoUrl: '', savedVideoUrl: '', iconType: 1 },
|
||||||
{ preview: '/static/image/other/video-call/defualt/video-img3.png', iconType: 2 }
|
{ preview: '/static/image/other/video-call/defualt/video-img3.png', videoUrl: '', savedVideoUrl: '', iconType: 2 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
videoDataBackup: null, // 编辑模式备份
|
videoDataBackup: null, // 编辑模式备份
|
||||||
|
|
@ -162,6 +180,9 @@ const data = reactive({
|
||||||
showTimeEditPopup: false,
|
showTimeEditPopup: false,
|
||||||
tempMinutes: '0',
|
tempMinutes: '0',
|
||||||
tempSeconds: '00',
|
tempSeconds: '00',
|
||||||
|
// 底部选项弹窗
|
||||||
|
showMediaSelector: false,
|
||||||
|
currentEditIndex: -1, // 当前编辑的项索引 (-1表示新增, >=0表示替换)
|
||||||
// 拖动状态管理
|
// 拖动状态管理
|
||||||
dragState: {
|
dragState: {
|
||||||
draggingIndex: -1, // 当前拖动的索引
|
draggingIndex: -1, // 当前拖动的索引
|
||||||
|
|
@ -174,6 +195,9 @@ const data = reactive({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 定时器引用,用于页面卸载时清理
|
||||||
|
let statusBarTimer = null
|
||||||
|
|
||||||
let { videoData, statusBarHeight } = toRefs(data)
|
let { videoData, statusBarHeight } = toRefs(data)
|
||||||
|
|
||||||
// 视频网格样式计算属性
|
// 视频网格样式计算属性
|
||||||
|
|
@ -225,18 +249,67 @@ onMounted(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
data.videoData = uni.getStorageSync('videoData') || data.videoData
|
const videoData = uni.getStorageSync('videoData') || data.videoData
|
||||||
|
console.log('videoData1', videoData)
|
||||||
|
const videoDataNew = {
|
||||||
|
...videoData,
|
||||||
|
videoList: videoData.videoList.map((item) => {
|
||||||
|
item.videoUrl = plus.io.convertLocalFileSystemURL(item.savedVideoUrl)
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
}
|
||||||
|
data.videoData = videoDataNew
|
||||||
|
console.log('videoData2', data.videoData)
|
||||||
})
|
})
|
||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
util.setAndroidSystemBarColor('#232323')
|
util.setAndroidSystemBarColor('#232323')
|
||||||
setTimeout(() => {
|
// 保存定时器引用,以便在页面卸载时清理
|
||||||
|
statusBarTimer = setTimeout(() => {
|
||||||
plus.navigator.setStatusBarStyle("light");
|
plus.navigator.setStatusBarStyle("light");
|
||||||
}, 500)
|
}, 500)
|
||||||
// #endif
|
// #endif
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 页面隐藏时清理定时器,防止返回时出错
|
||||||
|
onHide(() => {
|
||||||
|
// 清理状态栏定时器
|
||||||
|
if (statusBarTimer) {
|
||||||
|
clearTimeout(statusBarTimer)
|
||||||
|
statusBarTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理长按定时器
|
||||||
|
if (data.dragState.longPressTimer) {
|
||||||
|
clearTimeout(data.dragState.longPressTimer)
|
||||||
|
data.dragState.longPressTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置拖动状态
|
||||||
|
data.dragState.isDragging = false
|
||||||
|
data.dragState.draggingIndex = -1
|
||||||
|
|
||||||
|
console.log('🚪 页面隐藏,已清理定时器和状态')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 页面卸载时清理所有定时器和资源
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 清理状态栏定时器
|
||||||
|
if (statusBarTimer) {
|
||||||
|
clearTimeout(statusBarTimer)
|
||||||
|
statusBarTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理长按定时器
|
||||||
|
if (data.dragState.longPressTimer) {
|
||||||
|
clearTimeout(data.dragState.longPressTimer)
|
||||||
|
data.dragState.longPressTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🧹 页面卸载,已清理所有定时器')
|
||||||
|
})
|
||||||
|
|
||||||
// 打开时间编辑弹窗
|
// 打开时间编辑弹窗
|
||||||
const openTimeEditPopup = () => {
|
const openTimeEditPopup = () => {
|
||||||
// 解析当前时间 (格式: "125:22")
|
// 解析当前时间 (格式: "125:22")
|
||||||
|
|
@ -273,13 +346,64 @@ const closeTimeEditPopup = () => {
|
||||||
data.showTimeEditPopup = false
|
data.showTimeEditPopup = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmEdit = async () => {
|
// 判断是否为临时文件路径
|
||||||
|
const isTempFilePath = (filePath) => {
|
||||||
|
if (!filePath) return false
|
||||||
|
|
||||||
|
const lowerPath = filePath.toLowerCase()
|
||||||
|
|
||||||
|
// 永久路径白名单 - 如果路径包含这些特征,则明确判定为永久路径
|
||||||
|
const permanentKeywords = [
|
||||||
|
'/downloads/', // UniApp 永久下载目录
|
||||||
|
'saved_video_', // 已保存的视频文件
|
||||||
|
'saved_image_' // 已保存的图片文件
|
||||||
|
]
|
||||||
|
|
||||||
|
// 优先检查是否为永久路径
|
||||||
|
if (permanentKeywords.some(keyword => lowerPath.includes(keyword.toLowerCase()))) {
|
||||||
|
return false // 明确不是临时路径
|
||||||
|
}
|
||||||
|
|
||||||
|
// 常见临时路径特征
|
||||||
|
const tempKeywords = [
|
||||||
|
'tmp', // 通用临时目录
|
||||||
|
'temp', // 通用临时目录
|
||||||
|
'cache', // 缓存目录
|
||||||
|
'_doc/uniapp_temp', // UniApp Android 临时目录
|
||||||
|
'_downloads/temp', // 下载临时目录
|
||||||
|
'/var/mobile/containers/data/application', // iOS 临时路径
|
||||||
|
'wxfile://tmp', // 微信小程序临时文件
|
||||||
|
'http://tmp', // 微信小程序临时文件
|
||||||
|
'/doc/', // Android UniApp 文档临时目录
|
||||||
|
'compress_video' // UniApp 压缩视频临时文件
|
||||||
|
]
|
||||||
|
|
||||||
|
// 检查是否包含临时路径关键字
|
||||||
|
return tempKeywords.some(keyword => lowerPath.includes(keyword.toLowerCase()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const confirmEdit = () => {
|
||||||
|
// 保存图片视频
|
||||||
|
// saveImageVideo()
|
||||||
|
|
||||||
|
// 退出编辑模式
|
||||||
|
data.isEdit = false
|
||||||
|
data.videoDataBackup = null
|
||||||
|
// 保存数据
|
||||||
|
// uni.setStorageSync('videoData', data.videoData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存图片视频到本地
|
||||||
|
*/
|
||||||
|
const saveImage = async () => {
|
||||||
// 保存临时图片为永久路径
|
// 保存临时图片为永久路径
|
||||||
try {
|
try {
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
const savePromises = data.videoData.videoList.map(async (item) => {
|
const savePromises = data.videoData.videoList.map(async (item) => {
|
||||||
// 检查是否为临时路径(通常包含 tmp 或 temp)
|
// 检查图片是否为临时路径
|
||||||
if (item.preview && (item.preview.includes('tmp') || item.preview.includes('temp'))) {
|
if (isTempFilePath(item.preview)) {
|
||||||
try {
|
try {
|
||||||
const savedFile = await new Promise((resolve, reject) => {
|
const savedFile = await new Promise((resolve, reject) => {
|
||||||
uni.saveFile({
|
uni.saveFile({
|
||||||
|
|
@ -300,43 +424,172 @@ const confirmEdit = async () => {
|
||||||
// 等待所有图片保存完成
|
// 等待所有图片保存完成
|
||||||
await Promise.all(savePromises)
|
await Promise.all(savePromises)
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// 保存数据
|
|
||||||
uni.setStorageSync('videoData', data.videoData)
|
|
||||||
console.log('✅ 视频数据已保存')
|
console.log('✅ 视频数据已保存')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ 保存过程出错:', error)
|
console.error('❌ 保存过程出错:', error)
|
||||||
// 即使出错也保存数据
|
|
||||||
uni.setStorageSync('videoData', data.videoData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 退出编辑模式
|
|
||||||
data.isEdit = false
|
|
||||||
data.videoDataBackup = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeIconType = (item) => {
|
const changeIconType = (item, index) => {
|
||||||
|
|
||||||
|
if (index == data.videoData.videoList.length - 1) {
|
||||||
item.iconType = item.iconType == 2 ? 0 : item.iconType + 1
|
item.iconType = item.iconType == 2 ? 0 : item.iconType + 1
|
||||||
// 编辑模式下不立即保存,点击完成后统一保存
|
} else {
|
||||||
if (!data.isEdit) {
|
item.iconType = item.iconType == 2 ? 0 : 2
|
||||||
uni.setStorageSync('videoData', data.videoData)
|
|
||||||
}
|
}
|
||||||
|
uni.setStorageSync('videoData', data.videoData)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addVideo = () => {
|
const addVideo = () => {
|
||||||
if (data.videoData.videoList.length >= 9) {
|
if (data.videoData.videoList.length >= 9) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
uni.chooseImage({
|
// 重置为新增模式
|
||||||
count: 9 - data.videoData.videoList.length,
|
data.currentEditIndex = -1
|
||||||
sizeType: ['original', 'compressed'],
|
// 显示底部选项弹窗
|
||||||
|
data.showMediaSelector = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭底部选项弹窗
|
||||||
|
const closeMediaSelector = () => {
|
||||||
|
data.showMediaSelector = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换视频或图片
|
||||||
|
* @param item
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
const changeVideoOrImage = (item, index) => {
|
||||||
|
console.log('切换媒体:', item, index)
|
||||||
|
// 记录当前编辑的索引
|
||||||
|
data.currentEditIndex = index
|
||||||
|
// 显示媒体选择器
|
||||||
|
data.showMediaSelector = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择视频
|
||||||
|
const chooseVideo = () => {
|
||||||
|
closeMediaSelector()
|
||||||
|
uni.chooseVideo({
|
||||||
|
count: 1,
|
||||||
sourceType: ['album', 'camera'],
|
sourceType: ['album', 'camera'],
|
||||||
success: (res) => {
|
maxDuration: 60,
|
||||||
data.videoData.videoList.push(...res.tempFilePaths.map((item) => ({ preview: item, iconType: 1 })))
|
camera: 'back',
|
||||||
|
success: async (res) => {
|
||||||
|
console.log('选择视频成功:', res)
|
||||||
|
const videoUrl = plus.io.convertLocalFileSystemURL(res.tempFilePath)
|
||||||
|
const savedVideoUrl = await new Promise((resolve, reject) => {
|
||||||
|
uni.saveFile({
|
||||||
|
tempFilePath: videoUrl,
|
||||||
|
success: (res) => resolve(res.savedFilePath),
|
||||||
|
fail: (err) => reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 判断是替换还是新增
|
||||||
|
if (data.currentEditIndex >= 0) {
|
||||||
|
// 获取旧数据并删除旧文件
|
||||||
|
const oldItem = data.videoData.videoList[data.currentEditIndex]
|
||||||
|
if (oldItem) {
|
||||||
|
console.log('🔄 替换视频,删除旧文件...')
|
||||||
|
removeFile(oldItem.preview)
|
||||||
|
removeFile(oldItem.videoUrl)
|
||||||
|
removeFile(oldItem.savedVideoUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 替换模式:更新指定索引的项
|
||||||
|
data.videoData.videoList[data.currentEditIndex] = {
|
||||||
|
preview: '',
|
||||||
|
videoUrl: videoUrl,
|
||||||
|
savedVideoUrl: savedVideoUrl,
|
||||||
|
iconType: 1
|
||||||
|
}
|
||||||
|
console.log('✅ 已替换索引', data.currentEditIndex, '的视频')
|
||||||
|
} else {
|
||||||
|
// 新增模式:添加新项
|
||||||
|
data.videoData.videoList.push({
|
||||||
|
preview: '',
|
||||||
|
videoUrl: videoUrl,
|
||||||
|
savedVideoUrl: savedVideoUrl,
|
||||||
|
iconType: 1
|
||||||
|
})
|
||||||
|
console.log('✅ 已添加新视频')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存到本地存储
|
||||||
|
uni.setStorageSync('videoData', data.videoData)
|
||||||
|
console.log('💾 数据已保存到本地存储')
|
||||||
|
|
||||||
|
// 重置编辑索引
|
||||||
|
data.currentEditIndex = -1
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('选择视频失败:', err)
|
||||||
|
// 重置编辑索引
|
||||||
|
data.currentEditIndex = -1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 选择图片
|
||||||
|
const chooseImage = () => {
|
||||||
|
closeMediaSelector()
|
||||||
|
|
||||||
|
// 根据模式设置选择数量
|
||||||
|
const maxCount = data.currentEditIndex >= 0 ? 1 : (9 - data.videoData.videoList.length)
|
||||||
|
|
||||||
|
uni.chooseImage({
|
||||||
|
count: maxCount,
|
||||||
|
sizeType: ['original', 'compressed'],
|
||||||
|
sourceType: ['album', 'camera'],
|
||||||
|
success: (res) => {
|
||||||
|
console.log('选择图片成功:', res)
|
||||||
|
|
||||||
|
// 判断是替换还是新增
|
||||||
|
if (data.currentEditIndex >= 0) {
|
||||||
|
// 获取旧数据并删除旧文件
|
||||||
|
const oldItem = data.videoData.videoList[data.currentEditIndex]
|
||||||
|
if (oldItem) {
|
||||||
|
console.log('🔄 替换图片,删除旧文件...')
|
||||||
|
removeFile(oldItem.preview)
|
||||||
|
removeFile(oldItem.videoUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 替换模式:更新指定索引的项
|
||||||
|
data.videoData.videoList[data.currentEditIndex] = {
|
||||||
|
preview: res.tempFilePaths[0],
|
||||||
|
videoUrl: '',
|
||||||
|
savedVideoUrl: '',
|
||||||
|
iconType: 1
|
||||||
|
}
|
||||||
|
console.log('✅ 已替换索引', data.currentEditIndex, '的图片')
|
||||||
|
} else {
|
||||||
|
// 新增模式:添加新项
|
||||||
|
data.videoData.videoList.push(...res.tempFilePaths.map((item) => ({
|
||||||
|
preview: item,
|
||||||
|
videoUrl: '',
|
||||||
|
savedVideoUrl: '',
|
||||||
|
iconType: 1
|
||||||
|
})))
|
||||||
|
console.log('✅ 已添加', res.tempFilePaths.length, '张图片')
|
||||||
|
}
|
||||||
|
saveImage()
|
||||||
|
// 保存到本地存储
|
||||||
|
uni.setStorageSync('videoData', data.videoData)
|
||||||
|
console.log('💾 数据已保存到本地存储')
|
||||||
|
|
||||||
|
// 重置编辑索引
|
||||||
|
data.currentEditIndex = -1
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('选择图片失败:', err)
|
||||||
|
// 重置编辑索引
|
||||||
|
data.currentEditIndex = -1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 改变信息
|
// 改变信息
|
||||||
const changeInfo = (key) => {
|
const changeInfo = (key) => {
|
||||||
|
|
@ -344,14 +597,59 @@ const changeInfo = (key) => {
|
||||||
uni.setStorageSync('videoData', data.videoData)
|
uni.setStorageSync('videoData', data.videoData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除本地保存的文件
|
||||||
|
const removeFile = (filePath) => {
|
||||||
|
if (!filePath) return
|
||||||
|
|
||||||
|
// 如果是静态资源或临时文件,跳过
|
||||||
|
if (filePath.startsWith('/') && !filePath.startsWith('file://') && !filePath.includes('saved_')) {
|
||||||
|
// 简单的判断,保留 static 目录下的文件
|
||||||
|
if (filePath.includes('/static/')) return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🗑️ 准备删除文件:', filePath)
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
// 尝试使用 plus.io 删除 (支持 file:// 协议和 _downloads 等路径)
|
||||||
|
if (filePath.startsWith('file://') || filePath.includes('_downloads') || filePath.includes('saved_video_')) {
|
||||||
|
plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
|
||||||
|
entry.remove(() => {
|
||||||
|
console.log('✅ plus.io 删除文件成功:', filePath)
|
||||||
|
}, (e) => {
|
||||||
|
console.log('⚠️ plus.io 删除文件失败:', e.message)
|
||||||
|
})
|
||||||
|
}, (e) => {
|
||||||
|
// 文件可能不存在
|
||||||
|
console.log('⚠️ plus.io 解析路径失败 (可能文件已不存在):', e.message)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// 尝试使用 uni.removeSavedFile (主要针对 uni.saveFile 保存的文件)
|
||||||
|
uni.removeSavedFile({
|
||||||
|
filePath: filePath,
|
||||||
|
success: (res) => {
|
||||||
|
console.log('✅ uni.removeSavedFile 删除成功:', filePath)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.log('⚠️ uni.removeSavedFile 删除失败:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const deleteVideo = (index) => {
|
const deleteVideo = (index) => {
|
||||||
|
const item = data.videoData.videoList[index]
|
||||||
|
if (item) {
|
||||||
|
removeFile(item.preview)
|
||||||
|
removeFile(item.videoUrl)
|
||||||
|
removeFile(item.savedVideoUrl)
|
||||||
|
}
|
||||||
data.videoData.videoList.splice(index, 1)
|
data.videoData.videoList.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 触摸开始 - 启动长按检测
|
// 触摸开始 - 启动长按检测
|
||||||
const handleTouchStart = (event, index) => {
|
const handleTouchStart = (event, index) => {
|
||||||
if (!data.isEdit) return
|
|
||||||
|
|
||||||
const touch = event.touches[0]
|
const touch = event.touches[0]
|
||||||
data.dragState.startX = touch.clientX
|
data.dragState.startX = touch.clientX
|
||||||
data.dragState.startY = touch.clientY
|
data.dragState.startY = touch.clientY
|
||||||
|
|
@ -369,7 +667,7 @@ const handleTouchStart = (event, index) => {
|
||||||
|
|
||||||
// 触摸移动 - 实时更新位置偏移
|
// 触摸移动 - 实时更新位置偏移
|
||||||
const handleTouchMove = (event, index) => {
|
const handleTouchMove = (event, index) => {
|
||||||
if (!data.isEdit || !data.dragState.isDragging) return
|
if (!data.dragState.isDragging) return
|
||||||
|
|
||||||
const touch = event.touches[0]
|
const touch = event.touches[0]
|
||||||
const deltaX = touch.clientX - data.dragState.startX
|
const deltaX = touch.clientX - data.dragState.startX
|
||||||
|
|
@ -382,8 +680,6 @@ const handleTouchMove = (event, index) => {
|
||||||
|
|
||||||
// 触摸结束 - 完成拖动并保存
|
// 触摸结束 - 完成拖动并保存
|
||||||
const handleTouchEnd = (event, index) => {
|
const handleTouchEnd = (event, index) => {
|
||||||
if (!data.isEdit) return
|
|
||||||
|
|
||||||
// 清除长按定时器
|
// 清除长按定时器
|
||||||
if (data.dragState.longPressTimer) {
|
if (data.dragState.longPressTimer) {
|
||||||
clearTimeout(data.dragState.longPressTimer)
|
clearTimeout(data.dragState.longPressTimer)
|
||||||
|
|
@ -586,6 +882,7 @@ const hangup = () => {
|
||||||
width: 40rpx;
|
width: 40rpx;
|
||||||
height: 40rpx;
|
height: 40rpx;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
z-index: 999;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拖动时的样式
|
// 拖动时的样式
|
||||||
|
|
@ -773,4 +1070,62 @@ const hangup = () => {
|
||||||
color: #07C160;
|
color: #07C160;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 底部选项弹窗样式 */
|
||||||
|
.action-sheet-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-end;
|
||||||
|
z-index: 10001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-sheet {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 24rpx 24rpx 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
animation: slideUp 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
from {
|
||||||
|
transform: translateY(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-sheet-item {
|
||||||
|
padding: 32rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-sheet-text {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-sheet-cancel {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-sheet-divider {
|
||||||
|
height: 16rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 852 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 524 B |
|
After Width: | Height: | Size: 836 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 753 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 177 B |
|
After Width: | Height: | Size: 173 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 747 B |
|
After Width: | Height: | Size: 638 B |
|
After Width: | Height: | Size: 418 B |
|
After Width: | Height: | Size: 319 B |
|
After Width: | Height: | Size: 516 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 599 B |
|
After Width: | Height: | Size: 353 B |
|
After Width: | Height: | Size: 672 B |
|
After Width: | Height: | Size: 598 B |
|
After Width: | Height: | Size: 572 B |
|
After Width: | Height: | Size: 258 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 355 KiB After Width: | Height: | Size: 398 KiB |
|
After Width: | Height: | Size: 464 KiB |
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"airLine": [
|
||||||
|
{
|
||||||
|
"icon": "shenzhen",
|
||||||
|
"name": "深圳航空",
|
||||||
|
"code": "ZH",
|
||||||
|
"alias": "深航"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "zhongguonanfang",
|
||||||
|
"name": "中国南方航空",
|
||||||
|
"code": "CZ",
|
||||||
|
"alias": "南航"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "chunqiu",
|
||||||
|
"name": "春秋航空",
|
||||||
|
"code": "9C",
|
||||||
|
"alias": "春秋航空"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "xiamen",
|
||||||
|
"name": "厦门航空",
|
||||||
|
"code": "MF",
|
||||||
|
"alias": "厦航"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "zhongguoguoji",
|
||||||
|
"name": "中国国际航空",
|
||||||
|
"code": "CA",
|
||||||
|
"alias": "中国国航"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "shandong",
|
||||||
|
"name": "山东航空",
|
||||||
|
"code": "SC",
|
||||||
|
"alias": "山航"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "sichuan",
|
||||||
|
"name": "四川航空",
|
||||||
|
"code": "3U",
|
||||||
|
"alias": "川航"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "zhongguodongfang",
|
||||||
|
"name": "中国东方航空",
|
||||||
|
"code": "MU",
|
||||||
|
"alias": "中国东航"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "jixiang",
|
||||||
|
"name": "吉祥航空",
|
||||||
|
"code": "HO",
|
||||||
|
"alias": "吉祥航空"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": "hainan",
|
||||||
|
"name": "海南航空",
|
||||||
|
"code": "HU",
|
||||||
|
"alias": "海航"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||