mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 19:37:35 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d46c4c4285 | ||
|
|
0b871175b2 | ||
|
|
c34c4fd880 | ||
|
|
ff00f0c283 | ||
|
|
847c2e5810 | ||
|
|
e62c81bb33 | ||
|
|
984fdb3459 |
3
.github/ISSUE_TEMPLATE/bug.yml
vendored
3
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -31,4 +31,5 @@ body:
|
||||
id: other
|
||||
attributes:
|
||||
label: "具体信息"
|
||||
description: "有需要补充的信息吗?比如控制台的报错什么的( Ctrl + Shift + i 打开控制台 )"
|
||||
description: "请填写完整的复现步骤和遇到的问题,包括但不限于报错信息、控制台输出、网络请求等"
|
||||
placeholder: "请填写具体的复现步骤和遇到的问题"
|
||||
|
||||
12
README.md
12
README.md
@@ -1,10 +1,8 @@
|
||||
<div align="center">
|
||||
<img alt="logo" height="80" src="./public/images/icons/favicon.png" />
|
||||
<h2>SPlayer</h2>
|
||||
<p>一个简约的音乐播放器</p>
|
||||
<img alt="main" src="./screenshots/main.png" />
|
||||
</div>
|
||||
<br />
|
||||
# SPlayer
|
||||
|
||||
> 一个简约的音乐播放器
|
||||
|
||||

|
||||
|
||||
## 说明
|
||||
|
||||
|
||||
46
package.json
46
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "splayer",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.5",
|
||||
"description": "A minimalist music player",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "imsyy",
|
||||
@@ -30,13 +30,13 @@
|
||||
"@electron-toolkit/preload": "^3.0.0",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"@material/material-color-utilities": "^0.2.7",
|
||||
"NeteaseCloudMusicApi": "^4.14.0",
|
||||
"axios": "^1.6.5",
|
||||
"NeteaseCloudMusicApi": "^4.15.6",
|
||||
"axios": "^1.6.7",
|
||||
"colorthief": "^2.4.0",
|
||||
"electron-dl": "^3.5.1",
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
"express": "^4.18.2",
|
||||
"electron-dl": "^3.5.2",
|
||||
"electron-store": "^8.2.0",
|
||||
"electron-updater": "^6.1.8",
|
||||
"express": "^4.18.3",
|
||||
"express-http-proxy": "^2.0.0",
|
||||
"howler": "^2.2.4",
|
||||
"js-cookie": "^3.0.5",
|
||||
@@ -47,30 +47,30 @@
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"plyr": "^3.7.8",
|
||||
"screenfull": "^6.0.2",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue-slider-component": "4.1.0-beta.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/eslint-config": "^1.0.2",
|
||||
"@rushstack/eslint-patch": "^1.6.1",
|
||||
"@vitejs/plugin-vue": "^5.0.2",
|
||||
"@rushstack/eslint-patch": "^1.7.2",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"ajv": "^8.12.0",
|
||||
"electron": "^28.1.2",
|
||||
"electron-builder": "^24.9.1",
|
||||
"electron-log": "^5.0.3",
|
||||
"electron-vite": "^2.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-vue": "^9.19.2",
|
||||
"naive-ui": "^2.37.3",
|
||||
"prettier": "^3.1.1",
|
||||
"sass": "^1.69.7",
|
||||
"terser": "^5.26.0",
|
||||
"unplugin-auto-import": "^0.17.3",
|
||||
"electron": "^28.2.7",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-log": "^5.1.2",
|
||||
"electron-vite": "^2.1.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"naive-ui": "^2.38.1",
|
||||
"prettier": "^3.2.5",
|
||||
"sass": "^1.72.0",
|
||||
"terser": "^5.29.1",
|
||||
"unplugin-auto-import": "^0.17.5",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.0.11",
|
||||
"vite": "^5.1.6",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-pwa": "^0.17.4",
|
||||
"vite-plugin-pwa": "^0.17.5",
|
||||
"vue": "3.4.8"
|
||||
}
|
||||
}
|
||||
|
||||
2405
pnpm-lock.yaml
generated
2405
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
BIN
screenshots/SPlayer.jpg
Normal file
BIN
screenshots/SPlayer.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 524 KiB |
@@ -32,7 +32,7 @@
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
}"
|
||||
:class="music.getPlaySongData?.id === item?.id ? 'songs play' : 'songs'"
|
||||
:class="Number(music.getPlaySongData?.id) === Number(item?.id) ? 'songs play' : 'songs'"
|
||||
hoverable
|
||||
@click="checkCanClick(data, item, songsIndex + index)"
|
||||
@dblclick.stop="playSong(data, item, songsIndex + index)"
|
||||
@@ -190,7 +190,7 @@
|
||||
</div>
|
||||
<!-- 更新日期 -->
|
||||
<n-text v-if="type === 'dj' && item.updateTime" class="update hidden" depth="3">
|
||||
{{ getTimestampTime(item.updateTime, false) }}
|
||||
{{ djFormatDate(item.updateTime) }}
|
||||
</n-text>
|
||||
<!-- 播放量 -->
|
||||
<n-text v-if="type === 'dj' && item.playCount" class="count hidden" depth="3">
|
||||
@@ -273,7 +273,7 @@ import { setCloudDel } from "@/api/cloud";
|
||||
import { addSongToPlayList } from "@/api/playlist";
|
||||
import { siteData, siteSettings, musicData, siteStatus } from "@/stores";
|
||||
import { initPlayer, fadePlayOrPause, addSongToNext } from "@/utils/Player";
|
||||
import { getTimestampTime } from "@/utils/timeTools";
|
||||
import { djFormatDate } from "@/utils/timeTools";
|
||||
|
||||
const router = useRouter();
|
||||
const music = musicData();
|
||||
|
||||
@@ -133,6 +133,8 @@ onBeforeUnmount(() => {
|
||||
min-width: 180px;
|
||||
height: 180px;
|
||||
width: 180px;
|
||||
min-height: 180px;
|
||||
min-width: 180px;
|
||||
box-sizing: border-box;
|
||||
transition: opacity 0.3s;
|
||||
&.hidden {
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<!-- 用户信息 -->
|
||||
<userData />
|
||||
<!-- TitleBar -->
|
||||
<TitleBar v-if="checkPlatform.electron()" />
|
||||
<TitleBar v-if="titleBarShow" />
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
@@ -122,6 +122,9 @@ const openGithub = () => {
|
||||
window.open(packageJson.github);
|
||||
};
|
||||
|
||||
// TitleBar
|
||||
const titleBarShow = ref(false);
|
||||
|
||||
// 主菜单渲染
|
||||
const mainMenuShow = ref(false);
|
||||
const mainMenuOptions = computed(() => [
|
||||
@@ -138,6 +141,13 @@ const mainMenuOptions = computed(() => [
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
onMounted(() => {
|
||||
nextTick().then(() => {
|
||||
// 是否显示 TitleBar
|
||||
titleBarShow.value = checkPlatform.electron() || typeof electron !== "undefined";
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -42,8 +42,6 @@ export const initPlayer = async (playNow = false) => {
|
||||
const { playList } = music;
|
||||
// 当前播放歌曲数据
|
||||
const playSongData = music.getPlaySongData;
|
||||
// 若为电台则更改 id
|
||||
playSongData.id = playMode === "dj" ? playSongData.mainTrackId : playSongData.id;
|
||||
// 是否为本地歌曲
|
||||
const isLocalSong = playSongData?.path ? true : false;
|
||||
console.log("当前为本地歌曲");
|
||||
|
||||
@@ -112,8 +112,7 @@ const formatData = (data, type = "playlist", noTracks = false) => {
|
||||
// dj
|
||||
case "dj":
|
||||
return {
|
||||
id: v.id || v.vid,
|
||||
mainTrackId: v.mainTrackId,
|
||||
id: v.mainTrackId || v.id || v.vid,
|
||||
name: v.name,
|
||||
creator: v.dj,
|
||||
count: v.programCount,
|
||||
|
||||
@@ -106,3 +106,31 @@ export const getCommentTime = (t) => {
|
||||
}月${userDate.getDate()}日 ${UH}:${Um}`;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 电台时间戳格式化
|
||||
* @param {number} timestamp - 要格式化的时间戳(毫秒)
|
||||
* @returns {string} - 格式化后的日期描述
|
||||
*/
|
||||
export const djFormatDate = (timestamp) => {
|
||||
const now = new Date();
|
||||
const targetDate = new Date(timestamp);
|
||||
const timeDiff = now - targetDate;
|
||||
const oneDay = 24 * 60 * 60 * 1000; // 一天的毫秒数
|
||||
const daysDiff = Math.floor(timeDiff / oneDay);
|
||||
// 数字补零
|
||||
const formatNumber = (num) => {
|
||||
return num < 10 ? `0${num}` : num;
|
||||
};
|
||||
if (daysDiff === 0) {
|
||||
return "今日";
|
||||
} else if (daysDiff === 1) {
|
||||
return "昨日";
|
||||
} else if (daysDiff <= 7) {
|
||||
return `${daysDiff}天前`;
|
||||
} else if (targetDate.getFullYear() === now.getFullYear() - 1) {
|
||||
return `${targetDate.getFullYear()}-${formatNumber(targetDate.getMonth() + 1)}`;
|
||||
} else {
|
||||
return `${formatNumber(targetDate.getMonth() + 1)}-${formatNumber(targetDate.getDate())}`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -171,13 +171,6 @@
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="!searchValue" class="song-list">
|
||||
<SongList :data="djData" type="dj" />
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
v-if="djData?.length"
|
||||
:totalCount="totalCount"
|
||||
:pageNumber="pageNumber"
|
||||
@pageNumberChange="pageNumberChange"
|
||||
/>
|
||||
</div>
|
||||
<SongList v-else-if="searchData?.length" :data="searchData" type="dj" />
|
||||
<n-empty
|
||||
@@ -209,7 +202,7 @@
|
||||
import { NIcon } from "naive-ui";
|
||||
import { useRouter } from "vue-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { siteData, siteSettings } from "@/stores";
|
||||
import { siteData } from "@/stores";
|
||||
import { getDjDetail, getDjProgram, likeDj } from "@/api/dj";
|
||||
import { fuzzySearch } from "@/utils/helper";
|
||||
import { isLogin } from "@/utils/auth";
|
||||
@@ -221,20 +214,17 @@ import SvgIcon from "@/components/Global/SvgIcon";
|
||||
|
||||
const router = useRouter();
|
||||
const data = siteData();
|
||||
const settings = siteSettings();
|
||||
const { userLikeData } = storeToRefs(data);
|
||||
const { loadSize } = storeToRefs(settings);
|
||||
|
||||
// 电台数据
|
||||
const djId = ref(router.currentRoute.value.query.id);
|
||||
const pageNumber = ref(Number(router.currentRoute.value.query?.page) || 1);
|
||||
const djDetail = ref(null);
|
||||
const djData = ref(null);
|
||||
|
||||
// 模糊搜索数据
|
||||
const loadingMsg = ref(null);
|
||||
const searchValue = ref(null);
|
||||
const searchData = ref([]);
|
||||
const totalCount = ref(0);
|
||||
|
||||
// 图标渲染
|
||||
const renderIcon = (icon) => {
|
||||
@@ -267,6 +257,8 @@ const getDjDetailData = async (id) => {
|
||||
const detail = await getDjDetail(id);
|
||||
// 基础信息
|
||||
djDetail.value = formatData(detail.data, "dj")[0];
|
||||
// 获取节目
|
||||
await getDjProgramData(djId.value, djDetail.value?.count);
|
||||
} catch (error) {
|
||||
console.error("获取电台信息出错:", error);
|
||||
$message.error("获取电台信息出现错误");
|
||||
@@ -274,16 +266,27 @@ const getDjDetailData = async (id) => {
|
||||
};
|
||||
|
||||
// 获取电台全部节目
|
||||
const getDjProgramData = async (id, limit = loadSize.value, offset = 0) => {
|
||||
const getDjProgramData = async (id, count) => {
|
||||
try {
|
||||
if (count === 0) return (djData.value = "empty");
|
||||
// 是否为超大歌单
|
||||
if (count >= 500) {
|
||||
loadingMsg.value = $message.loading("该电台节目数量过多,请稍等", {
|
||||
duration: 0,
|
||||
});
|
||||
}
|
||||
// 循环获取
|
||||
let offset = 0;
|
||||
djData.value = [];
|
||||
const result = await getDjProgram(id, limit, offset);
|
||||
console.log(result);
|
||||
// 数据总数
|
||||
totalCount.value = result.count;
|
||||
if (totalCount.value === 0) return (djData.value = "empty");
|
||||
// 处理数据
|
||||
djData.value = formatData(result.programs, "dj");
|
||||
while (count === null || offset < count) {
|
||||
const result = await getDjProgram(id, 500, offset);
|
||||
const djDetail = formatData(result.programs, "dj");
|
||||
djData.value = djData.value.concat(djDetail);
|
||||
offset += 500;
|
||||
}
|
||||
// 关闭加载提示
|
||||
loadingMsg.value?.destroy();
|
||||
loadingMsg.value = null;
|
||||
} catch (error) {
|
||||
console.error("获取电台节目错误:", error);
|
||||
$message.error("获取电台节目出现错误");
|
||||
@@ -330,36 +333,8 @@ const likeOrDislike = debounce(async (id) => {
|
||||
}
|
||||
}, 300);
|
||||
|
||||
// 页数变化
|
||||
const pageNumberChange = (page) => {
|
||||
router.push({
|
||||
path: "/dj",
|
||||
query: { id: djId.value, page },
|
||||
});
|
||||
};
|
||||
|
||||
// 监听路由变化
|
||||
watch(
|
||||
() => router.currentRoute.value,
|
||||
async (val) => {
|
||||
if (val.name === "dj") {
|
||||
// 更改参数
|
||||
pageNumber.value = Number(val.query?.page) || 1;
|
||||
djId.value = val.query?.id;
|
||||
// 调用接口
|
||||
await getDjDetailData(djId.value);
|
||||
await getDjProgramData(
|
||||
djId.value,
|
||||
loadSize.value,
|
||||
(pageNumber.value - 1) * settings.loadSize,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await getDjDetailData(djId.value);
|
||||
await getDjProgramData(djId.value, loadSize.value, (pageNumber.value - 1) * settings.loadSize);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user