注释很详细,直接上代码
温馨提示:本博客建立在ElementPlus组件库
、pinia
及其持久化插件
已安装的前提下,前两者很容易找到相应博客,后者可能会踩坑,此处附上另一篇博客
vue3+vite+ts+pinia实现持久化存储
项目结构:
源码:
other.ts
import {ref,computed} from "vue";
import {defineStore} from "pinia";
export const useOtherStore = defineStore(
"other",
() => {
let useLessMess=ref('ggggg');
let doubleMess = computed(() => {
return useLessMess.value+useLessMess.value;
})
let getDoubleMess=()=>{
return doubleMess.value;
}
return {getDoubleMess}
}
)
video.ts
import { ref, computed } from "vue";
import { defineStore } from "pinia";
import {useOtherStore} from "./other";
import axios from "axios";
export const useVideoStore = defineStore(
"video",
() => {
/**
* 当前播放视频索引
*/
let videoIndex = ref(0);
/**
* 当前播放视频地址
*/
let videoUrl = computed(() => {
return videoUrlList.value[videoIndex.value];
});
/**
* 视频地址列表
*/
let videoUrlList = ref<string[]>([]);
/**
* 上一曲按钮是否可用
*/
let isPreviousButtonShow = computed(() => {
return !(videoIndex.value == 0);
});
/**
* 下一曲按钮是否正在加载中
*/
let isNextButtonLoading = ref(false);
/**
* 初始化
* @returns {void}
*/
function init() {
//如果没有视频则获取视频
if (videoUrlList.value.length == 0) {
getVideoJson(false);
}
}
/**
* 获取视频Json
* @param {boolean} [op=true] -是否是初始化以外的操作
* @returns {void}
*/
function getVideoJson(op: boolean = true) {
axios
.get("http://api.treason.cn/API/nan.php")
.then(function (response) {
const data = response.data;
//判断是否重复
if (op && videoUrlList.value.find((item) => item == data)) {
console.log("重复");
getVideoJson();
} else {
//去掉最后两个字符
const url = data.substring(0, data.length - 2);
videoUrlList.value.push(url);
}
})
.catch(function (error) {
console.log(error);
});
}
/**
* 点击上一曲按钮
*/
function previousVideo() {
if (videoIndex.value > 0) {
videoIndex.value--;
}
}
/**
* 点击下一曲按钮
*/
async function nextVideo() {
if (videoIndex.value < videoUrlList.value.length - 1) {
videoIndex.value++;
} else {
isNextButtonLoading.value = true;
await getVideoJson();
isNextButtonLoading.value = false;
videoIndex.value++;
}
}
/**
* 获取其他模块的数据
*/
function getOtherData() {
return useOtherStore().getDoubleMess();
}
return {
/**
* 当前播放视频索引
* @type {number}
*/
videoIndex,
/**
* 当前播放视频地址
* @type {string}
*/
videoUrl,
/**
* 视频地址列表
* @type {Array<string>}
*/
videoUrlList,
/**
* 上一曲按钮是否可用
* @type {boolean}
*/
isPreviousButtonShow,
/**
* 下一曲按钮是否正在加载中
* @type {boolean}
*/
isNextButtonLoading,
/**
* 初始化
* @returns {void}
*/
init,
/**
* 点击上一曲按钮
* @returns {void}
*/
previousVideo,
/**
* 点击下一曲按钮
* @returns {void}
*/
nextVideo,
/**
* 获取其他模块的数据
* @returns {string}
*/
getOtherData
};
},
{
persist:{
key:"videoData",//可以指定存储的key(即存入的名字)
storage:sessionStorage,//可以指定存储的存储方式:1. sessionStorage(会话储存,临时) 2. localStorage(本地储存,永久)
// paths:["videoUrlList"]//可以指定需要存储的内容(计算属性别存啊,存了也无效的)
}
}
);
App.vue
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useVideoStore } from "@/stores/video";
import { storeToRefs } from "pinia";
const videoStore = useVideoStore();
const { videoUrl, isPreviousButtonShow, isNextButtonLoading } =
storeToRefs(videoStore);
const { init, previousVideo, nextVideo ,getOtherData} = videoStore;
/**
* @type {boolean} -是否开启自动播放
*/
let isAuto = ref(false);
/**
* 视频是否播放到结尾
*/
let isVideoEnd = ref(false);
/**
* 视频播放到结尾
*/
function videoEndSlove() {
isVideoEnd.value = true;
if (isAuto.value) {
setTimeout(() => {
nextVideo();
}, 1000)
}
}
/**
* 切换自动连播状态
*/
function switchSlove() {
if (isVideoEnd.value && isAuto.value) {
nextVideo();
}
}
/**
* 批量更新
*/
function usePatchJust(){
//如果只是单个变量直接修改就行,当然得注意别修改const类型的地址
videoStore.$patch(//没有改变内容,示范而已
(state) => {
state.isNextButtonLoading = state.isNextButtonLoading;
state.videoUrlList = state.videoUrlList;
state.videoIndex = state.videoIndex;
}
)
}
init(); //初始化一下
usePatchJust();//批量更新
console.log(getOtherData());//获取其他数据并输出
</script>
<template>
<el-card class="el-card-class">
<template #header>
<div class="header-class">
<div>懒大人演唱会</div>
<div>
<el-switch @change="switchSlove" v-model="isAuto" active-text="自动播放"></el-switch>
</div>
</div>
</template>
<template #default>
<div>
<video @play="isVideoEnd = false" @ended="videoEndSlove" controls="true" autoplay :src="videoUrl"
class="video-class"></video>
</div>
</template>
<template #footer>
<el-button type="primary" size="large" @click="previousVideo" :disabled="!isPreviousButtonShow">上一首</el-button>
<el-button type="primary" size="large" @click="nextVideo"
:loading="isNextButtonLoading">下一首</el-button></template>
</el-card>
</template>
<style scoped>
.el-card-class {
width: 1320px;
height: 910px;
}
.header-class {
display: flex;
justify-content: space-between;
}
.video-class {
width: 100%;
max-width: 100%;
/* 最大宽度 */
height: 720px;
object-fit: contain;
/* 控制视频如何适应其框架 */
border-radius: 5px;
}
</style>
main.ts
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
// 导入插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
const app = createApp(App)
// 创建pinia,别创建多个了,看清楚昂
const pinia = createPinia()
// 使用pinia插件,插件是加在pinia实例上的而不是app,注意注意
pinia.use(piniaPluginPersistedstate)
app.use(pinia)//这步不能忘哇
app.use(router)
app.use(ElementPlus)
app.mount('#app')
效果演示