🦄 refactor: 重构代码将 Vuex替换为 Pinia

集成 Pinia 状态管理
This commit is contained in:
alger
2025-03-19 22:48:28 +08:00
parent 4fa5ed0ca6
commit e355341596
40 changed files with 1170 additions and 494 deletions
+5 -5
View File
@@ -88,18 +88,18 @@ import { useMessage } from 'naive-ui';
import { computed, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { getMusicDetail } from '@/api/music';
import SongItem from '@/components/common/SongItem.vue';
import { getSongUrl } from '@/hooks/MusicListHook';
import { usePlayerStore } from '@/store';
import type { SongResult } from '@/type/music';
import { isElectron, setAnimationClass, setAnimationDelay } from '@/utils';
const { t } = useI18n();
const store = useStore();
const playerStore = usePlayerStore();
const message = useMessage();
const favoriteList = computed(() => store.state.favoriteList);
const favoriteList = computed(() => playerStore.favoriteList);
const favoriteSongs = ref<SongResult[]>([]);
const loading = ref(false);
const noMore = ref(false);
@@ -277,7 +277,7 @@ const handleScroll = (e: any) => {
};
onMounted(async () => {
await store.dispatch('initializeFavoriteList');
await playerStore.initializeFavoriteList();
await getFavoriteSongs();
});
@@ -293,7 +293,7 @@ watch(
);
const handlePlay = () => {
store.commit('setPlayList', favoriteSongs.value);
playerStore.setPlayList(favoriteSongs.value);
};
const getItemAnimationDelay = (index: number) => {
+3 -3
View File
@@ -34,11 +34,11 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStore } from 'vuex';
import { getMusicDetail } from '@/api/music';
import SongItem from '@/components/common/SongItem.vue';
import { useMusicHistory } from '@/hooks/MusicHistoryHook';
import { usePlayerStore } from '@/store/modules/player';
import type { SongResult } from '@/type/music';
import { setAnimationClass, setAnimationDelay } from '@/utils';
@@ -47,12 +47,12 @@ defineOptions({
});
const { t } = useI18n();
const store = useStore();
const { delMusic, musicList } = useMusicHistory();
const scrollbarRef = ref();
const loading = ref(false);
const noMore = ref(false);
const displayList = ref<SongResult[]>([]);
const playerStore = usePlayerStore();
// 无限滚动相关配置
const pageSize = 20;
@@ -112,7 +112,7 @@ const handleScroll = (e: any) => {
// 播放全部
const handlePlay = () => {
store.commit('setPlayList', displayList.value);
playerStore.setPlayList(displayList.value);
};
onMounted(() => {
+5 -4
View File
@@ -3,9 +3,9 @@ import { useMessage } from 'naive-ui';
import { onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { checkQr, createQr, getQrKey, getUserDetail, loginByCellphone } from '@/api/login';
import { useUserStore } from '@/store/modules/user';
import { setAnimationClass } from '@/utils';
defineOptions({
@@ -14,11 +14,12 @@ defineOptions({
const { t } = useI18n();
const message = useMessage();
const store = useStore();
const router = useRouter();
const isQr = ref(true);
const qrUrl = ref<string>();
const userStore = useUserStore();
onMounted(() => {
loadLogin();
});
@@ -56,7 +57,7 @@ const timerIsQr = (key: string) => {
if (data.code === 803) {
localStorage.setItem('token', data.cookie);
const user = await getUserDetail();
store.state.user = user.data.profile;
userStore.user = user.data.profile;
localStorage.setItem('user', JSON.stringify(user.data.profile));
message.success(t('login.message.loginSuccess'));
@@ -95,7 +96,7 @@ const loginPhone = async () => {
const { data } = await loginByCellphone(phone.value, password.value);
if (data.code === 200) {
message.success(t('login.message.loginSuccess'));
store.state.user = data.profile;
userStore.user = data.profile;
localStorage.setItem('token', data.cookie);
setTimeout(() => {
router.push('/user');
+5 -4
View File
@@ -64,11 +64,11 @@
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';
import { useStore } from 'vuex';
import { getAllMv, getTopMv } from '@/api/mv';
import MvPlayer from '@/components/MvPlayer.vue';
import { audioService } from '@/services/audioService';
import { usePlayerStore } from '@/store/modules/player';
import { IMvItem } from '@/type/mv';
import { formatNumber, getImgUrl, setAnimationClass, setAnimationDelay } from '@/utils';
@@ -79,7 +79,6 @@ defineOptions({
const showMv = ref(false);
const mvList = ref<Array<IMvItem>>([]);
const playMvItem = ref<IMvItem>();
const store = useStore();
const initLoading = ref(false);
const loadingMore = ref(false);
const currentIndex = ref(0);
@@ -97,6 +96,8 @@ const categories = [
];
const selectedCategory = ref('全部');
const playerStore = usePlayerStore();
watch(selectedCategory, async () => {
offset.value = 0;
mvList.value = [];
@@ -114,8 +115,8 @@ onMounted(async () => {
});
const handleShowMv = async (item: IMvItem, index: number) => {
store.commit('setIsPlay', false);
store.commit('setPlayMusic', false);
playerStore.setIsPlay(false);
playerStore.setPlayMusic(false);
audioService.getCurrentSound()?.pause();
showMv.value = true;
currentIndex.value = index;
+32 -22
View File
@@ -107,12 +107,13 @@ import { useDateFormat } from '@vueuse/core';
import { onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { useStore } from 'vuex';
import { getHotSearch } from '@/api/home';
import { getSearch } from '@/api/search';
import SearchItem from '@/components/common/SearchItem.vue';
import SongItem from '@/components/common/SongItem.vue';
import { usePlayerStore } from '@/store/modules/player';
import { useSearchStore } from '@/store/modules/search';
import type { IHotSearch } from '@/type/search';
import { isMobile, setAnimationClass, setAnimationDelay } from '@/utils';
@@ -122,10 +123,11 @@ defineOptions({
const { t } = useI18n();
const route = useRoute();
const store = useStore();
const playerStore = usePlayerStore();
const searchStore = useSearchStore();
const searchDetail = ref<any>();
const searchType = computed(() => store.state.searchType as number);
const searchType = computed(() => searchStore.searchType as number);
const searchDetailLoading = ref(false);
const searchHistory = ref<string[]>([]);
@@ -188,23 +190,6 @@ onMounted(() => {
const hotKeyword = ref(route.query.keyword || t('search.title.searchList'));
watch(
() => store.state.searchValue,
(value) => {
loadSearch(value);
}
);
watch(
() => searchType.value,
() => {
if (store.state.searchValue) {
loadSearch(store.state.searchValue);
}
}
);
const dateFormat = (time: any) => useDateFormat(time, 'YYYY.MM.DD').value;
const loadSearch = async (keywords: any, type: any = null, isLoadMore = false) => {
if (!keywords) return;
@@ -295,6 +280,31 @@ const loadSearch = async (keywords: any, type: any = null, isLoadMore = false) =
}
};
watch(
() => searchStore.searchValue,
(value) => {
loadSearch(value);
}
);
watch(
() => searchType.value,
() => {
if (searchStore.searchValue) {
loadSearch(searchStore.searchValue);
}
}
);
// 修改 store.state 的访问
if (searchStore.searchValue) {
loadSearch(searchStore.searchValue);
}
// 修改 store.state 的设置
searchStore.searchValue = route.query.keyword as string;
const dateFormat = (time: any) => useDateFormat(time, 'YYYY.MM.DD').value;
// 添加滚动处理函数
const handleScroll = (e: any) => {
const { scrollTop, scrollHeight, clientHeight } = e.target;
@@ -308,14 +318,14 @@ watch(
() => route.path,
async (path) => {
if (path === '/search') {
store.state.searchValue = route.query.keyword;
searchStore.searchValue = route.query.keyword as string;
}
}
);
const handlePlay = () => {
const tracks = searchDetail.value?.songs || [];
store.commit('setPlayList', tracks);
playerStore.setPlayList(tracks);
};
// 点击搜索历史
+40 -50
View File
@@ -205,7 +205,7 @@
</div>
</div>
<div class="flex items-center gap-2">
<n-button size="small" @click="store.commit('setShowDownloadDrawer', true)">
<n-button size="small" @click="settingsStore.showDownloadDrawer = true">
{{ t('settings.application.download') }}
</n-button>
</div>
@@ -461,11 +461,11 @@
</template>
<script setup lang="ts">
import { cloneDeep } from 'lodash';
import type { FormRules } from 'naive-ui';
import { useMessage } from 'naive-ui';
import { computed, h, nextTick, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStore } from 'vuex';
import localData from '@/../main/set.json';
import Coffee from '@/components/Coffee.vue';
@@ -473,13 +473,17 @@ import DonationList from '@/components/common/DonationList.vue';
import PlayBottom from '@/components/common/PlayBottom.vue';
import LanguageSwitcher from '@/components/LanguageSwitcher.vue';
import ShortcutSettings from '@/components/settings/ShortcutSettings.vue';
import { useSettingsStore } from '@/store/modules/settings';
import { useUserStore } from '@/store/modules/user';
import { isElectron } from '@/utils';
import { openDirectory, selectDirectory } from '@/utils/fileOperation';
import { checkUpdate, UpdateResult } from '@/utils/update';
import config from '../../../../package.json';
const store = useStore();
const settingsStore = useSettingsStore();
const userStore = useUserStore();
const checking = ref(false);
const updateInfo = ref<UpdateResult>({
hasUpdate: false,
@@ -490,37 +494,23 @@ const updateInfo = ref<UpdateResult>({
const { t } = useI18n();
const setData = computed(() => {
const data = store.state.setData;
// 确保代理配置存在
if (!data.proxyConfig) {
data.proxyConfig = {
enable: false,
protocol: 'http',
host: '127.0.0.1',
port: 7890
};
}
// 确保音质设置存在
if (!data.musicQuality) {
data.musicQuality = 'higher';
}
return data;
});
watch(
() => setData.value,
(newVal) => {
store.commit('setSetData', newVal);
},
{ deep: true }
);
const setData = ref(settingsStore.setData);
const isDarkTheme = computed({
get: () => store.state.theme === 'dark',
set: () => store.commit('toggleTheme')
get: () => settingsStore.theme === 'dark',
set: () => settingsStore.toggleTheme()
});
// watch(
// () => setData.value,
// (newData) => {
// console.log('newData', newData);
// settingsStore.setSetData(newData);
// },
// {
// deep: true
// }
// );
const openAuthor = () => {
window.open(setData.value.authorUrl);
};
@@ -552,16 +542,16 @@ const checkForUpdates = async (isClick = false) => {
};
const openReleasePage = () => {
store.commit('setShowUpdateModal', true);
settingsStore.showUpdateModal = true;
};
const selectDownloadPath = async () => {
const path = await selectDirectory(message);
if (path) {
store.commit('setSetData', {
setData.value = {
...setData.value,
downloadPath: path
});
};
}
};
@@ -606,7 +596,7 @@ const proxyRules: FormRules = {
};
// 使用 store 中的字体列表
const systemFonts = computed(() => store.state.systemFonts);
const systemFonts = computed(() => settingsStore.systemFonts);
// 已选择的字体列表
const selectedFonts = ref<string[]>([]);
@@ -622,17 +612,17 @@ watch(
(newFonts) => {
// 如果没有选择任何字体,使用系统默认字体
if (newFonts.length === 0) {
store.commit('setSetData', {
setData.value = {
...setData.value,
fontFamily: 'system-ui'
});
};
return;
}
// 将选择的字体组合成字体列表
store.commit('setSetData', {
setData.value = {
...setData.value,
fontFamily: newFonts.join(',')
});
};
},
{ deep: true }
);
@@ -660,10 +650,10 @@ onMounted(async () => {
}
// 确保enableRealIP有默认值
if (setData.value.enableRealIP === undefined) {
store.commit('setSetData', {
setData.value = {
...setData.value,
enableRealIP: false
});
};
}
});
@@ -686,7 +676,7 @@ const handleProxyConfirm = async () => {
try {
await formRef.value?.validate();
// 保存代理配置时保留enable状态
store.commit('setSetData', {
setData.value = {
...setData.value,
proxyConfig: {
enable: setData.value.proxyConfig?.enable || false,
@@ -694,7 +684,7 @@ const handleProxyConfirm = async () => {
host: proxyForm.value.host,
port: proxyForm.value.port
}
});
};
showProxyModal.value = false;
message.success(t('settings.network.messages.proxySuccess'));
} catch (err) {
@@ -705,20 +695,20 @@ const handleProxyConfirm = async () => {
const validateAndSaveRealIP = () => {
const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/;
if (!setData.value.realIP || ipRegex.test(setData.value.realIP)) {
store.commit('setSetData', {
setData.value = {
...setData.value,
realIP: setData.value.realIP,
enableRealIP: true
});
};
if (setData.value.realIP) {
message.success(t('settings.network.messages.realIPSuccess'));
}
} else {
message.error(t('settings.network.messages.realIPError'));
store.commit('setSetData', {
setData.value = {
...setData.value,
realIP: ''
});
};
}
};
@@ -727,11 +717,11 @@ watch(
() => setData.value.enableRealIP,
(newVal) => {
if (!newVal) {
store.commit('setSetData', {
setData.value = {
...setData.value,
realIP: '',
enableRealIP: false
});
};
}
}
);
@@ -795,7 +785,7 @@ const clearCache = async () => {
localStorage.removeItem('favoriteList');
break;
case 'user':
store.commit('logout');
userStore.handleLogout();
break;
case 'settings':
if (window.electron) {
+13 -15
View File
@@ -99,7 +99,6 @@ import { useMessage } from 'naive-ui';
import { computed, onBeforeUnmount, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { getListDetail } from '@/api/list';
import { updatePlaylistTracks } from '@/api/music';
@@ -107,6 +106,8 @@ import { getUserDetail, getUserPlaylist, getUserRecord } from '@/api/user';
import PlayBottom from '@/components/common/PlayBottom.vue';
import SongItem from '@/components/common/SongItem.vue';
import MusicList from '@/components/MusicList.vue';
import { usePlayerStore } from '@/store/modules/player';
import { useUserStore } from '@/store/modules/user';
import type { Playlist } from '@/type/listDetail';
import type { IUserDetail } from '@/type/user';
import { getImgUrl, isMobile, setAnimationClass, setAnimationDelay } from '@/utils';
@@ -116,7 +117,8 @@ defineOptions({
});
const { t } = useI18n();
const store = useStore();
const userStore = useUserStore();
const playerStore = usePlayerStore();
const router = useRouter();
const userDetail = ref<IUserDetail>();
const playList = ref<any[]>([]);
@@ -128,7 +130,7 @@ const list = ref<Playlist>();
const listLoading = ref(false);
const message = useMessage();
const user = computed(() => store.state.user);
const user = computed(() => userStore.user);
onBeforeUnmount(() => {
mounted.value = false;
@@ -145,8 +147,8 @@ const checkLoginStatus = () => {
}
// 如果store中没有用户数据,但localStorage中有,则恢复用户数据
if (!store.state.user && userData) {
store.state.user = JSON.parse(userData);
if (!userStore.user && userData) {
userStore.setUser(JSON.parse(userData));
}
return true;
@@ -184,7 +186,7 @@ const loadData = async () => {
console.error('加载用户页面失败:', error);
// 如果获取用户数据失败,可能是token过期
if (error.response?.status === 401) {
store.commit('logout');
userStore.logout();
router.push('/login');
}
} finally {
@@ -208,17 +210,13 @@ watch(
// 监听用户状态变化
watch(
() => store.state.user,
() => userStore.user,
(newUser) => {
if (!mounted.value) return;
if (!newUser) {
router.push('/login');
} else {
loadPage();
if (newUser) {
loadUserData();
}
},
{ immediate: true }
}
);
// 页面挂载时检查登录状态
@@ -271,7 +269,7 @@ const handleRemoveFromPlaylist = async (songId: number) => {
const handlePlay = () => {
const tracks = recordList.value || [];
store.commit('setPlayList', tracks);
playerStore.setPlayList(tracks);
};
</script>