feat: 完成云存相关UI+逻辑
This commit is contained in:
parent
a1b36ccff5
commit
8c4c107127
219
pages/p2p/VideoList.vue
Normal file
219
pages/p2p/VideoList.vue
Normal file
@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<view>
|
||||
<view v-if="list.length === 0 && requestFinished">
|
||||
<image
|
||||
class="empty-list"
|
||||
src="https://oss-lock.xhjcn.ltd/mp/background_empty_list.png"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="empty-list-text">暂无数据</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view v-for="(item, index) in list" :key="index" class="mt-4">
|
||||
<view>{{ item.date }}</view>
|
||||
<view class="mt-2 flex flex-wrap gap-[19rpx]">
|
||||
<view v-for="video in item.recordList" :key="video.recordId" @click="handleVideo(video)">
|
||||
<image :src="video.imagesUrl" class="w-224 h-224 rounded-xl relative" mode="aspectFill">
|
||||
<view v-if="type === 'select'" class="absolute inset-0 bg-black bg-opacity-30">
|
||||
<image
|
||||
v-if="selectList.includes(video)"
|
||||
class="w-40 h-40 top-16 right-16 absolute"
|
||||
src="https://oss-lock.xhjcn.ltd/mp/icon_select.png"
|
||||
></image>
|
||||
<image
|
||||
v-else
|
||||
class="w-40 h-40 top-16 right-16 absolute"
|
||||
src="https://oss-lock.xhjcn.ltd/mp/icon_not_select.png"
|
||||
></image>
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
class="absolute inset-0 bg-black bg-opacity-30 flex items-center justify-center"
|
||||
>
|
||||
<up-icon name="play-right-fill" color="#ffffff" size="40"></up-icon>
|
||||
</view>
|
||||
</image>
|
||||
<view class="text-xs">{{ timeFormat(video.operateDate, 'yyyy-mm-dd hh:MM') }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { timeFormat } from 'uview-plus'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useBasicStore } from '@/stores/basic'
|
||||
import { useBluetoothStore } from '@/stores/bluetooth'
|
||||
import { getVideoList } from '@/api/sdk'
|
||||
|
||||
const $basic = useBasicStore()
|
||||
const $bluetooth = useBluetoothStore()
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'list'
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const requestFinished = ref(false)
|
||||
|
||||
const list = ref([])
|
||||
const selectList = ref([])
|
||||
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
requestFinished.value = true
|
||||
})
|
||||
|
||||
const getList = async () => {
|
||||
const res = await getVideoList({
|
||||
lockId: $bluetooth.currentLockInfo.lockId
|
||||
})
|
||||
if (res.code === 0) {
|
||||
// list.value = res.data
|
||||
|
||||
list.value = [
|
||||
{
|
||||
date: '2025-04-08',
|
||||
recordList: [
|
||||
{
|
||||
recordId: 1,
|
||||
imagesUrl:
|
||||
'https://q0.itc.cn/q_70/images03/20250331/84b2646fc92d4ea0b12f1b134652c807.jpeg',
|
||||
videoUrl:
|
||||
'https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4',
|
||||
operateDate: '1745423939000'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '2025-04-07',
|
||||
recordList: [
|
||||
{
|
||||
recordId: 2,
|
||||
imagesUrl:
|
||||
'https://q0.itc.cn/q_70/images03/20250331/84b2646fc92d4ea0b12f1b134652c807.jpeg',
|
||||
videoUrl: 'http://vjs.zencdn.net/v/oceans.mp4',
|
||||
operateDate: '1745423939000'
|
||||
},
|
||||
{
|
||||
recordId: 3,
|
||||
imagesUrl:
|
||||
'https://q0.itc.cn/q_70/images03/20250331/84b2646fc92d4ea0b12f1b134652c807.jpeg',
|
||||
videoUrl: 'http://www.w3school.com.cn/example/html5/mov_bbb.mp4',
|
||||
operateDate: '1745423939000'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '2025-04-06',
|
||||
recordList: [
|
||||
{
|
||||
recordId: 4,
|
||||
imagesUrl:
|
||||
'https://q0.itc.cn/q_70/images03/20250331/84b2646fc92d4ea0b12f1b134652c807.jpeg',
|
||||
videoUrl: 'https://www.w3schools.com/html/movie.mp4',
|
||||
operateDate: '1745423939000'
|
||||
},
|
||||
{
|
||||
recordId: 5,
|
||||
imagesUrl:
|
||||
'https://q0.itc.cn/q_70/images03/20250331/84b2646fc92d4ea0b12f1b134652c807.jpeg',
|
||||
videoUrl: 'https://media.w3.org/2010/05/sintel/trailer.mp4',
|
||||
operateDate: '1745423939000'
|
||||
},
|
||||
{
|
||||
recordId: 6,
|
||||
imagesUrl:
|
||||
'https://q0.itc.cn/q_70/images03/20250331/84b2646fc92d4ea0b12f1b134652c807.jpeg',
|
||||
videoUrl:
|
||||
'https://stream7.iqilu.com/10339/upload_transcode/202002/09/20200209105011F0zPoYzHry.mp4',
|
||||
operateDate: '1745423939000'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
date: '2025-04-05',
|
||||
recordList: [
|
||||
{
|
||||
recordId: 7,
|
||||
imagesUrl:
|
||||
'https://q0.itc.cn/q_70/images03/20250331/84b2646fc92d4ea0b12f1b134652c807.jpeg',
|
||||
videoUrl: 'http://vjs.zencdn.net/v/oceans.mp4',
|
||||
operateDate: '1745423939000'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.message,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleVideo = item => {
|
||||
if (props.type === 'select') {
|
||||
const isExist = selectList.value.find(data => data.recordId === item.recordId)
|
||||
if (isExist) {
|
||||
selectList.value = selectList.value.filter(data => data.recordId !== item.recordId)
|
||||
} else {
|
||||
selectList.value.push(item)
|
||||
}
|
||||
emit('change', selectList.value)
|
||||
} else {
|
||||
$basic.routeJump({
|
||||
type: props.type === 'list' ? 'navigateTo' : 'redirectTo',
|
||||
name: 'videoDetail',
|
||||
params: {
|
||||
video: JSON.stringify(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const selectAll = isSelectAll => {
|
||||
if (props.type === 'select') {
|
||||
if (isSelectAll) {
|
||||
list.value.forEach(item => {
|
||||
item.recordList.forEach(video => {
|
||||
selectList.value.push(video)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
selectList.value = []
|
||||
}
|
||||
emit('change', selectList.value)
|
||||
}
|
||||
}
|
||||
|
||||
const refresh = () => {
|
||||
getList()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
refresh,
|
||||
selectAll
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.empty-list {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
margin: 300rpx auto 20rpx 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.empty-list-text {
|
||||
font-size: 32rpx;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
38
pages/p2p/videoDetail.vue
Normal file
38
pages/p2p/videoDetail.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<view>
|
||||
<view>
|
||||
<view class="relative" v-if="video?.videoUrl">
|
||||
<video
|
||||
:src="video.videoUrl"
|
||||
play-btn-position="center"
|
||||
class="w-full h-650"
|
||||
object-fit="contain"
|
||||
:poster="video.imagesUrl"
|
||||
:enable-play-gesture="true"
|
||||
></video>
|
||||
<view
|
||||
class="absolute top-3 left-3 text-white bg-black bg-opacity-50 px-2 py-1 rounded z-10"
|
||||
>
|
||||
{{ timeFormat(video.operateDate, 'yyyy-mm-dd hh:MM') }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mx-2.5 pb-10">
|
||||
<VideoList type="detail" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { timeFormat } from 'uview-plus'
|
||||
import VideoList from './VideoList.vue'
|
||||
|
||||
const video = ref(null)
|
||||
|
||||
onLoad(options => {
|
||||
video.value = JSON.parse(options.video)
|
||||
console.log(video.value)
|
||||
})
|
||||
</script>
|
||||
145
pages/p2p/videoEdit.vue
Normal file
145
pages/p2p/videoEdit.vue
Normal file
@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="flex justify-between items-center px-2.5 py-2">
|
||||
<view>已选{{ selectList.length }}项</view>
|
||||
<view @click="selectAll">{{ isSelectAll ? '取消全选' : '全选' }}</view>
|
||||
</view>
|
||||
<view class="mx-2.5 pb-25">
|
||||
<VideoList ref="videoListRef" type="select" @change="handleChange" />
|
||||
</view>
|
||||
<view class="fixed bottom-0 flex justify-center w-full w-300 mx-auto bg-white">
|
||||
<view class="flex justify-between w-200 mr-4 h-160">
|
||||
<view @click="handleDownload">
|
||||
<view class="flex flex-col items-center">
|
||||
<image
|
||||
src="https://oss-lock.xhjcn.ltd/mp/icon_video_download.png"
|
||||
class="w-50 h-50"
|
||||
></image>
|
||||
<view class="mt-2">下载</view>
|
||||
</view>
|
||||
</view>
|
||||
<view @click="handleDelete">
|
||||
<view class="flex flex-col items-center">
|
||||
<image
|
||||
src="https://oss-lock.xhjcn.ltd/mp/icon_video_delete.png"
|
||||
class="w-50 h-50"
|
||||
></image>
|
||||
<view class="mt-2">删除</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getCurrentInstance, ref } from 'vue'
|
||||
import VideoList from './VideoList.vue'
|
||||
import { deleteVideo } from '@/api/sdk'
|
||||
|
||||
const instance = getCurrentInstance().proxy
|
||||
const eventChannel = instance.getOpenerEventChannel()
|
||||
|
||||
const videoListRef = ref(null)
|
||||
|
||||
const isSelectAll = ref(false)
|
||||
|
||||
const selectList = ref([])
|
||||
|
||||
const pending = ref(false)
|
||||
|
||||
const handleChange = list => {
|
||||
selectList.value = list
|
||||
}
|
||||
|
||||
const selectAll = () => {
|
||||
isSelectAll.value = !isSelectAll.value
|
||||
videoListRef.value.selectAll(isSelectAll.value)
|
||||
}
|
||||
|
||||
const handleDownload = () => {
|
||||
console.log('下载')
|
||||
if (selectList.value.length === 0) {
|
||||
uni.showToast({
|
||||
title: '请选择要下载的视频',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (selectList.value.length > 1) {
|
||||
uni.showToast({
|
||||
title: '视频暂不支持批量下载',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.showLoading({
|
||||
title: '下载中'
|
||||
})
|
||||
pending.value = true
|
||||
const url = selectList.value[0].videoUrl
|
||||
uni.downloadFile({
|
||||
url,
|
||||
success: res => {
|
||||
uni.saveVideoToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: () => {
|
||||
uni.hideLoading()
|
||||
pending.value = false
|
||||
uni.showToast({
|
||||
title: '下载成功',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
uni.hideLoading()
|
||||
pending.value = false
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
uni.hideLoading()
|
||||
pending.value = false
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (selectList.value.length === 0) {
|
||||
uni.showToast({
|
||||
title: '请选择要删除的视频',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.showLoading({
|
||||
title: '删除中'
|
||||
})
|
||||
pending.value = true
|
||||
const idList = selectList.value.map(item => item.recordId)
|
||||
const { code, message } = await deleteVideo(idList)
|
||||
uni.hideLoading()
|
||||
pending.value = false
|
||||
if (code === 0) {
|
||||
videoListRef.value.refresh()
|
||||
eventChannel.emit('refresh')
|
||||
uni.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: message,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
51
pages/p2p/videoLog.vue
Normal file
51
pages/p2p/videoLog.vue
Normal file
@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="bg-#f5f4f8 flex items-center mx-2.5 my-4 p-4 rounded-xl shadow-sm">
|
||||
<view>
|
||||
<view>3天滚动储存</view>
|
||||
<view class="text-#999999 mt-2 text-sm">
|
||||
{{ appName }}已为本设备免费提供3天滚动视频储存服务
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-none whitespace-nowrap mx-1.5">去升级</view>
|
||||
<up-icon name="arrow-right" />
|
||||
</view>
|
||||
<view class="mx-2.5 pb-10">
|
||||
<view class="flex items-center justify-between">
|
||||
<view>全部视频</view>
|
||||
<image
|
||||
src="https://oss-lock.xhjcn.ltd/mp/icon_edit.png"
|
||||
@click="handleEdit"
|
||||
class="w-48 h-48"
|
||||
/>
|
||||
</view>
|
||||
<VideoList ref="videoListRef" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import env from '@/config/env'
|
||||
import VideoList from './VideoList.vue'
|
||||
import { useBasicStore } from '@/stores/basic'
|
||||
|
||||
const $basic = useBasicStore()
|
||||
|
||||
const appName = ref('')
|
||||
const videoListRef = ref(null)
|
||||
onMounted(async () => {
|
||||
appName.value = await env[await getApp().globalData.getEnvConfig()].appName
|
||||
})
|
||||
|
||||
const handleEdit = () => {
|
||||
$basic.routeJump({
|
||||
name: 'videoEdit',
|
||||
events: {
|
||||
refresh: () => {
|
||||
videoListRef.value.refresh()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
Loading…
x
Reference in New Issue
Block a user