mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-03 14:20:50 +08:00
🌈 style: 优化代码格式化
This commit is contained in:
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
out
|
||||||
|
.gitignore
|
||||||
135
.eslintrc.cjs
Normal file
135
.eslintrc.cjs
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
require('@rushstack/eslint-patch/modern-module-resolution');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'eslint-config-airbnb-base',
|
||||||
|
'@vue/typescript/recommended',
|
||||||
|
'plugin:vue/vue3-recommended',
|
||||||
|
'plugin:vue-scoped-css/base',
|
||||||
|
'@electron-toolkit',
|
||||||
|
'@electron-toolkit/eslint-config-ts/eslint-recommended',
|
||||||
|
'plugin:prettier/recommended'
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
jest: true,
|
||||||
|
es6: true
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
defineProps: 'readonly',
|
||||||
|
defineEmits: 'readonly'
|
||||||
|
},
|
||||||
|
plugins: ['vue', '@typescript-eslint', 'simple-import-sort'],
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
sourceType: 'module',
|
||||||
|
allowImportExportEverywhere: true,
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
'import/extensions': ['.js', '.jsx', '.ts', '.tsx']
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'vue/require-default-prop': 'off',
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'no-nested-ternary': 'off',
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-continue': 'off',
|
||||||
|
'no-restricted-syntax': 'off',
|
||||||
|
'no-return-assign': 'off',
|
||||||
|
'no-unused-expressions': 'off',
|
||||||
|
'no-return-await': 'off',
|
||||||
|
'no-plusplus': 'off',
|
||||||
|
'no-param-reassign': 'off',
|
||||||
|
'no-shadow': 'off',
|
||||||
|
'guard-for-in': 'off',
|
||||||
|
'import/extensions': 'off',
|
||||||
|
'import/no-unresolved': 'off',
|
||||||
|
'import/no-extraneous-dependencies': 'off',
|
||||||
|
'import/prefer-default-export': 'off',
|
||||||
|
'import/first': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'vue/first-attribute-linebreak': 0,
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-use-before-define': 'off',
|
||||||
|
'@typescript-eslint/no-use-before-define': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/ban-types': 'off',
|
||||||
|
'class-methods-use-this': 'off',
|
||||||
|
'simple-import-sort/imports': 'error',
|
||||||
|
'simple-import-sort/exports': 'error'
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.vue'],
|
||||||
|
rules: {
|
||||||
|
'vue/component-name-in-template-casing': [2, 'kebab-case'],
|
||||||
|
'vue/require-default-prop': 0,
|
||||||
|
'vue/multi-word-component-names': 0,
|
||||||
|
'vue/no-reserved-props': 0,
|
||||||
|
'vue/no-v-html': 0,
|
||||||
|
'vue-scoped-css/enforce-style-type': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
allows: ['scoped']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
// 需要行尾分号
|
||||||
|
'prettier/prettier': ['error', { endOfLine: 'auto' }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
rules: {
|
||||||
|
'no-await-in-loop': 'off',
|
||||||
|
'dot-notation': 'off',
|
||||||
|
'constructor-super': 'off',
|
||||||
|
'getter-return': 'off',
|
||||||
|
'no-const-assign': 'off',
|
||||||
|
'no-dupe-args': 'off',
|
||||||
|
'no-dupe-class-members': 'off',
|
||||||
|
'no-dupe-keys': 'off',
|
||||||
|
'no-func-assign': 'off',
|
||||||
|
'no-import-assign': 'off',
|
||||||
|
'no-new-symbol': 'off',
|
||||||
|
'no-obj-calls': 'off',
|
||||||
|
'no-redeclare': 'off',
|
||||||
|
'no-setter-return': 'off',
|
||||||
|
'no-this-before-super': 'off',
|
||||||
|
'no-undef': 'off',
|
||||||
|
'no-unreachable': 'off',
|
||||||
|
'no-unsafe-negation': 'off',
|
||||||
|
'no-var': 'error',
|
||||||
|
'prefer-const': 'error',
|
||||||
|
'prefer-rest-params': 'error',
|
||||||
|
'prefer-spread': 'error',
|
||||||
|
'valid-typeof': 'off',
|
||||||
|
'consistent-return': 'off',
|
||||||
|
'no-promise-executor-return': 'off',
|
||||||
|
'prefer-promise-reject-errors': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
6
.prettierignore
Normal file
6
.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
out
|
||||||
|
dist
|
||||||
|
pnpm-lock.yaml
|
||||||
|
LICENSE.md
|
||||||
|
tsconfig.json
|
||||||
|
tsconfig.*.json
|
||||||
5
.prettierrc.yaml
Normal file
5
.prettierrc.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
singleQuote: true
|
||||||
|
semi: true
|
||||||
|
printWidth: 100
|
||||||
|
trailingComma: none
|
||||||
|
endOfLine: auto
|
||||||
@@ -3,11 +3,11 @@ import { app, globalShortcut, ipcMain, nativeImage } from 'electron';
|
|||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { loadLyricWindow } from './lyric';
|
import { loadLyricWindow } from './lyric';
|
||||||
import { startMusicApi } from './server';
|
import { initializeConfig } from './modules/config';
|
||||||
import { initializeFileManager } from './modules/fileManager';
|
import { initializeFileManager } from './modules/fileManager';
|
||||||
import { initializeTray } from './modules/tray';
|
import { initializeTray } from './modules/tray';
|
||||||
import { createMainWindow, initializeWindowManager } from './modules/window';
|
import { createMainWindow, initializeWindowManager } from './modules/window';
|
||||||
import { initializeConfig } from './modules/config';
|
import { startMusicApi } from './server';
|
||||||
|
|
||||||
// 导入所有图标
|
// 导入所有图标
|
||||||
const iconPath = join(__dirname, '../../resources');
|
const iconPath = join(__dirname, '../../resources');
|
||||||
@@ -15,8 +15,8 @@ const icon = nativeImage.createFromPath(
|
|||||||
process.platform === 'darwin'
|
process.platform === 'darwin'
|
||||||
? join(iconPath, 'icon.icns')
|
? join(iconPath, 'icon.icns')
|
||||||
: process.platform === 'win32'
|
: process.platform === 'win32'
|
||||||
? join(iconPath, 'favicon.ico')
|
? join(iconPath, 'favicon.ico')
|
||||||
: join(iconPath, 'icon.png')
|
: join(iconPath, 'icon.png')
|
||||||
);
|
);
|
||||||
|
|
||||||
let mainWindow: Electron.BrowserWindow;
|
let mainWindow: Electron.BrowserWindow;
|
||||||
@@ -26,19 +26,19 @@ function initialize() {
|
|||||||
// 初始化各个模块
|
// 初始化各个模块
|
||||||
initializeConfig();
|
initializeConfig();
|
||||||
initializeFileManager();
|
initializeFileManager();
|
||||||
|
|
||||||
// 创建主窗口
|
// 创建主窗口
|
||||||
mainWindow = createMainWindow(icon);
|
mainWindow = createMainWindow(icon);
|
||||||
|
|
||||||
// 初始化窗口管理
|
// 初始化窗口管理
|
||||||
initializeWindowManager();
|
initializeWindowManager();
|
||||||
|
|
||||||
// 初始化托盘
|
// 初始化托盘
|
||||||
initializeTray(iconPath, mainWindow);
|
initializeTray(iconPath, mainWindow);
|
||||||
|
|
||||||
// 启动音乐API
|
// 启动音乐API
|
||||||
startMusicApi();
|
startMusicApi();
|
||||||
|
|
||||||
// 加载歌词窗口
|
// 加载歌词窗口
|
||||||
loadLyricWindow(ipcMain, mainWindow);
|
loadLyricWindow(ipcMain, mainWindow);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { app, ipcMain } from 'electron';
|
import { app, ipcMain } from 'electron';
|
||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
|
|
||||||
import set from '../set.json';
|
import set from '../set.json';
|
||||||
|
|
||||||
interface StoreType {
|
interface StoreType {
|
||||||
@@ -22,7 +23,7 @@ export function initializeConfig() {
|
|||||||
store = new Store<StoreType>({
|
store = new Store<StoreType>({
|
||||||
name: 'config',
|
name: 'config',
|
||||||
defaults: {
|
defaults: {
|
||||||
set: set
|
set
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -39,4 +40,4 @@ export function initializeConfig() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { app, dialog, shell, ipcMain } from 'electron';
|
import axios from 'axios';
|
||||||
|
import { app, dialog, ipcMain, shell } from 'electron';
|
||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化文件管理相关的IPC监听
|
* 初始化文件管理相关的IPC监听
|
||||||
@@ -29,11 +29,14 @@ export function initializeFileManager() {
|
|||||||
/**
|
/**
|
||||||
* 下载音乐功能
|
* 下载音乐功能
|
||||||
*/
|
*/
|
||||||
async function downloadMusic(event: Electron.IpcMainEvent, { url, filename }: { url: string; filename: string }) {
|
async function downloadMusic(
|
||||||
|
event: Electron.IpcMainEvent,
|
||||||
|
{ url, filename }: { url: string; filename: string }
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const store = new Store();
|
const store = new Store();
|
||||||
const downloadPath = store.get('set.downloadPath') as string || app.getPath('downloads');
|
const downloadPath = (store.get('set.downloadPath') as string) || app.getPath('downloads');
|
||||||
|
|
||||||
// 直接使用配置的下载路径
|
// 直接使用配置的下载路径
|
||||||
const filePath = path.join(downloadPath, `${filename}.mp3`);
|
const filePath = path.join(downloadPath, `${filename}.mp3`);
|
||||||
|
|
||||||
@@ -66,4 +69,4 @@ async function downloadMusic(event: Electron.IpcMainEvent, { url, filename }: {
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
event.reply('music-download-complete', { success: false, error: error.message });
|
event.reply('music-download-complete', { success: false, error: error.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { app, Menu, nativeImage, Tray, BrowserWindow } from 'electron';
|
import { app, BrowserWindow, Menu, nativeImage, Tray } from 'electron';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
let tray: Tray | null = null;
|
let tray: Tray | null = null;
|
||||||
@@ -7,7 +7,9 @@ let tray: Tray | null = null;
|
|||||||
* 初始化系统托盘
|
* 初始化系统托盘
|
||||||
*/
|
*/
|
||||||
export function initializeTray(iconPath: string, mainWindow: BrowserWindow) {
|
export function initializeTray(iconPath: string, mainWindow: BrowserWindow) {
|
||||||
const trayIcon = nativeImage.createFromPath(join(iconPath, 'icon_16x16.png')).resize({ width: 16, height: 16 });
|
const trayIcon = nativeImage
|
||||||
|
.createFromPath(join(iconPath, 'icon_16x16.png'))
|
||||||
|
.resize({ width: 16, height: 16 });
|
||||||
tray = new Tray(trayIcon);
|
tray = new Tray(trayIcon);
|
||||||
|
|
||||||
// 创建一个上下文菜单
|
// 创建一个上下文菜单
|
||||||
@@ -16,15 +18,15 @@ export function initializeTray(iconPath: string, mainWindow: BrowserWindow) {
|
|||||||
label: '显示',
|
label: '显示',
|
||||||
click: () => {
|
click: () => {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '退出',
|
label: '退出',
|
||||||
click: () => {
|
click: () => {
|
||||||
mainWindow.destroy();
|
mainWindow.destroy();
|
||||||
app.quit();
|
app.quit();
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 设置系统托盘图标的上下文菜单
|
// 设置系统托盘图标的上下文菜单
|
||||||
@@ -40,4 +42,4 @@ export function initializeTray(iconPath: string, mainWindow: BrowserWindow) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return tray;
|
return tray;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BrowserWindow, shell, ipcMain, app, session } from 'electron';
|
|
||||||
import { is } from '@electron-toolkit/utils';
|
import { is } from '@electron-toolkit/utils';
|
||||||
import { join } from 'path';
|
import { app, BrowserWindow, ipcMain, session, shell } from 'electron';
|
||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
const store = new Store();
|
const store = new Store();
|
||||||
|
|
||||||
@@ -116,4 +116,4 @@ export function createMainWindow(icon: Electron.NativeImage): BrowserWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return mainWindow;
|
return mainWindow;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import server from 'netease-cloud-music-api-alger/server';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
@@ -16,9 +17,6 @@ ipcMain.handle('unblock-music', async (_, id, data) => {
|
|||||||
return unblockMusic(id, data);
|
return unblockMusic(id, data);
|
||||||
});
|
});
|
||||||
|
|
||||||
import server from 'netease-cloud-music-api-alger/server';
|
|
||||||
|
|
||||||
|
|
||||||
async function startMusicApi(): Promise<void> {
|
async function startMusicApi(): Promise<void> {
|
||||||
console.log('MUSIC API STARTED');
|
console.log('MUSIC API STARTED');
|
||||||
|
|
||||||
|
|||||||
2
src/preload/index.d.ts
vendored
2
src/preload/index.d.ts
vendored
@@ -14,6 +14,6 @@ declare global {
|
|||||||
restart: () => void;
|
restart: () => void;
|
||||||
unblockMusic: (id: number, data: any) => Promise<any>;
|
unblockMusic: (id: number, data: any) => Promise<any>;
|
||||||
};
|
};
|
||||||
$message:any
|
$message: any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { darkTheme, lightTheme } from 'naive-ui';
|
import { darkTheme, lightTheme } from 'naive-ui';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { isElectron } from '@/utils';
|
|
||||||
|
|
||||||
import homeRouter from '@/router/home';
|
import homeRouter from '@/router/home';
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
|
import { isElectron } from '@/utils';
|
||||||
|
|
||||||
import { isMobile } from './utils';
|
import { isMobile } from './utils';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import store from '@/store';
|
||||||
import { ILyric } from '@/type/lyric';
|
import { ILyric } from '@/type/lyric';
|
||||||
import { isElectron } from '@/utils';
|
import { isElectron } from '@/utils';
|
||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
import requestMusic from '@/utils/request_music';
|
import requestMusic from '@/utils/request_music';
|
||||||
import store from '@/store';
|
|
||||||
|
|
||||||
// 获取音乐音质详情
|
// 获取音乐音质详情
|
||||||
export const getMusicQualityDetail = (id: number) => {
|
export const getMusicQualityDetail = (id: number) => {
|
||||||
@@ -11,22 +11,22 @@ export const getMusicQualityDetail = (id: number) => {
|
|||||||
|
|
||||||
// 根据音乐Id获取音乐播放URl
|
// 根据音乐Id获取音乐播放URl
|
||||||
export const getMusicUrl = async (id: number) => {
|
export const getMusicUrl = async (id: number) => {
|
||||||
const res = await request.get('/song/download/url/v1', {
|
const res = await request.get('/song/download/url/v1', {
|
||||||
params: {
|
params: {
|
||||||
id,
|
id,
|
||||||
level: store.state.setData.musicQuality || 'higher'
|
level: store.state.setData.musicQuality || 'higher'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.data.data.url) {
|
if (res.data.data.url) {
|
||||||
return {data:{ data:[{url:res.data.data.url}]}};
|
return { data: { data: [{ url: res.data.data.url }] } };
|
||||||
}
|
}
|
||||||
|
|
||||||
return await request.get('/song/url/v1', {
|
return await request.get('/song/url/v1', {
|
||||||
params: {
|
params: {
|
||||||
id,
|
id,
|
||||||
level: store.state.setData.musicQuality || 'higher'
|
level: store.state.setData.musicQuality || 'higher'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -49,8 +49,10 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { NButton, NImage, NPopover } from 'naive-ui';
|
import { NButton, NImage, NPopover } from 'naive-ui';
|
||||||
|
|
||||||
import alipay from '@/assets/alipay.png';
|
import alipay from '@/assets/alipay.png';
|
||||||
import wechat from '@/assets/wechat.png';
|
import wechat from '@/assets/wechat.png';
|
||||||
|
|
||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
const copyQQ = () => {
|
const copyQQ = () => {
|
||||||
navigator.clipboard.writeText('789288579');
|
navigator.clipboard.writeText('789288579');
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<MusicList
|
<music-list
|
||||||
v-model:show="showMusic"
|
v-model:show="showMusic"
|
||||||
:name="albumName"
|
:name="albumName"
|
||||||
:song-list="songList"
|
:song-list="songList"
|
||||||
@@ -36,9 +36,9 @@ import { onMounted, ref } from 'vue';
|
|||||||
|
|
||||||
import { getNewAlbum } from '@/api/home';
|
import { getNewAlbum } from '@/api/home';
|
||||||
import { getAlbum } from '@/api/list';
|
import { getAlbum } from '@/api/list';
|
||||||
|
import MusicList from '@/components/MusicList.vue';
|
||||||
import type { IAlbumNew } from '@/type/album';
|
import type { IAlbumNew } from '@/type/album';
|
||||||
import { getImgUrl, setAnimationClass, setAnimationDelay } from '@/utils';
|
import { getImgUrl, setAnimationClass, setAnimationDelay } from '@/utils';
|
||||||
import MusicList from '@/components/MusicList.vue';
|
|
||||||
|
|
||||||
const albumData = ref<IAlbumNew>();
|
const albumData = ref<IAlbumNew>();
|
||||||
const loadAlbumList = async () => {
|
const loadAlbumList = async () => {
|
||||||
|
|||||||
@@ -74,11 +74,11 @@ import { onMounted, ref } from 'vue';
|
|||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
import { getDayRecommend, getHotSinger } from '@/api/home';
|
import { getDayRecommend, getHotSinger } from '@/api/home';
|
||||||
|
import MusicList from '@/components/MusicList.vue';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import { IDayRecommend } from '@/type/day_recommend';
|
import { IDayRecommend } from '@/type/day_recommend';
|
||||||
import type { IHotSinger } from '@/type/singer';
|
import type { IHotSinger } from '@/type/singer';
|
||||||
import { getImgUrl, setAnimationClass, setAnimationDelay, setBackgroundImg } from '@/utils';
|
import { getImgUrl, setAnimationClass, setAnimationDelay, setBackgroundImg } from '@/utils';
|
||||||
import MusicList from '@/components/MusicList.vue';
|
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
|
|||||||
@@ -39,10 +39,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
import { isElectron, isMobile } from '@/utils';
|
import { isElectron, isMobile } from '@/utils';
|
||||||
import config from '../../../../package.json';
|
|
||||||
import { getLatestReleaseInfo } from '@/utils/update';
|
import { getLatestReleaseInfo } from '@/utils/update';
|
||||||
|
|
||||||
|
import config from '../../../../package.json';
|
||||||
|
|
||||||
const showModal = ref(false);
|
const showModal = ref(false);
|
||||||
const noPrompt = ref(false);
|
const noPrompt = ref(false);
|
||||||
const releaseInfo = ref<any>(null);
|
const releaseInfo = ref<any>(null);
|
||||||
@@ -77,36 +79,33 @@ const handleInstall = async (): Promise<void> => {
|
|||||||
const isMac = userAgent.toLowerCase().includes('mac');
|
const isMac = userAgent.toLowerCase().includes('mac');
|
||||||
const isWindows = userAgent.toLowerCase().includes('win');
|
const isWindows = userAgent.toLowerCase().includes('win');
|
||||||
const isLinux = userAgent.toLowerCase().includes('linux');
|
const isLinux = userAgent.toLowerCase().includes('linux');
|
||||||
const isX64 = userAgent.includes('x86_64') ||
|
const isX64 =
|
||||||
userAgent.includes('Win64') ||
|
userAgent.includes('x86_64') || userAgent.includes('Win64') || userAgent.includes('WOW64');
|
||||||
userAgent.includes('WOW64');
|
|
||||||
|
|
||||||
let downloadUrl = '';
|
let downloadUrl = '';
|
||||||
|
|
||||||
// 根据平台和架构选择对应的安装包
|
// 根据平台和架构选择对应的安装包
|
||||||
if (isMac) {
|
if (isMac) {
|
||||||
// macOS
|
// macOS
|
||||||
const macAsset = assets.find(asset =>
|
const macAsset = assets.find((asset) => asset.name.includes('mac'));
|
||||||
asset.name.includes('mac')
|
|
||||||
);
|
|
||||||
downloadUrl = macAsset?.browser_download_url || '';
|
downloadUrl = macAsset?.browser_download_url || '';
|
||||||
} else if (isWindows) {
|
} else if (isWindows) {
|
||||||
// Windows
|
// Windows
|
||||||
let winAsset = assets.find(asset =>
|
let winAsset = assets.find(
|
||||||
asset.name.includes('win') &&
|
(asset) =>
|
||||||
(isX64 ? asset.name.includes('x64') : asset.name.includes('ia32'))
|
asset.name.includes('win') &&
|
||||||
|
(isX64 ? asset.name.includes('x64') : asset.name.includes('ia32'))
|
||||||
);
|
);
|
||||||
if(!winAsset){
|
if (!winAsset) {
|
||||||
winAsset = assets.find(asset =>
|
winAsset = assets.find((asset) => asset.name.includes('win.exe'));
|
||||||
asset.name.includes('win.exe')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
downloadUrl = winAsset?.browser_download_url || '';
|
downloadUrl = winAsset?.browser_download_url || '';
|
||||||
} else if (isLinux) {
|
} else if (isLinux) {
|
||||||
// Linux
|
// Linux
|
||||||
const linuxAsset = assets.find(asset =>
|
const linuxAsset = assets.find(
|
||||||
(asset.name.endsWith('.AppImage') || asset.name.endsWith('.deb')) &&
|
(asset) =>
|
||||||
asset.name.includes('x64')
|
(asset.name.endsWith('.AppImage') || asset.name.endsWith('.deb')) &&
|
||||||
|
asset.name.includes('x64')
|
||||||
);
|
);
|
||||||
downloadUrl = linuxAsset?.browser_download_url || '';
|
downloadUrl = linuxAsset?.browser_download_url || '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<p class="search-item-artist">{{ item.desc }}</p>
|
<p class="search-item-artist">{{ item.desc }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MusicList
|
<music-list
|
||||||
v-if="['专辑', 'playlist'].includes(item.type)"
|
v-if="['专辑', 'playlist'].includes(item.type)"
|
||||||
v-model:show="showPop"
|
v-model:show="showPop"
|
||||||
:name="item.name"
|
:name="item.name"
|
||||||
@@ -40,6 +40,7 @@ import MvPlayer from '@/components/MvPlayer.vue';
|
|||||||
import { audioService } from '@/services/audioService';
|
import { audioService } from '@/services/audioService';
|
||||||
import { IMvItem } from '@/type/mv';
|
import { IMvItem } from '@/type/mv';
|
||||||
import { getImgUrl } from '@/utils';
|
import { getImgUrl } from '@/utils';
|
||||||
|
|
||||||
import MusicList from '../MusicList.vue';
|
import MusicList from '../MusicList.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="song-item" :class="{ 'song-mini': mini, 'song-list': list }" @contextmenu.prevent="handleContextMenu">
|
<div
|
||||||
|
class="song-item"
|
||||||
|
:class="{ 'song-mini': mini, 'song-list': list }"
|
||||||
|
@contextmenu.prevent="handleContextMenu"
|
||||||
|
>
|
||||||
<n-image
|
<n-image
|
||||||
v-if="item.picUrl"
|
v-if="item.picUrl"
|
||||||
ref="songImg"
|
ref="songImg"
|
||||||
@@ -71,17 +75,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import type { MenuOption } from 'naive-ui';
|
||||||
|
import { useMessage } from 'naive-ui';
|
||||||
import { computed, h, ref, useTemplateRef } from 'vue';
|
import { computed, h, ref, useTemplateRef } from 'vue';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import { useMessage } from 'naive-ui';
|
|
||||||
import type { MenuOption } from 'naive-ui';
|
|
||||||
|
|
||||||
|
import { getSongUrl } from '@/hooks/MusicListHook';
|
||||||
import { audioService } from '@/services/audioService';
|
import { audioService } from '@/services/audioService';
|
||||||
import type { SongResult } from '@/type/music';
|
import type { SongResult } from '@/type/music';
|
||||||
import { getImgUrl, isElectron } from '@/utils';
|
import { getImgUrl, isElectron } from '@/utils';
|
||||||
import { getImageBackground } from '@/utils/linearColor';
|
import { getImageBackground } from '@/utils/linearColor';
|
||||||
import { getSongUrl } from '@/hooks/MusicListHook';
|
|
||||||
import { cloneDeep } from 'lodash';
|
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@@ -117,7 +121,7 @@ const isDownloading = ref(false);
|
|||||||
|
|
||||||
const dropdownOptions = computed<MenuOption[]>(() => [
|
const dropdownOptions = computed<MenuOption[]>(() => [
|
||||||
{
|
{
|
||||||
label: isDownloading.value ? '下载中...' : '下载 ' + props.item.name,
|
label: isDownloading.value ? '下载中...' : `下载 ${props.item.name}`,
|
||||||
key: 'download',
|
key: 'download',
|
||||||
icon: () => h('i', { class: 'iconfont ri-download-line' }),
|
icon: () => h('i', { class: 'iconfont ri-download-line' }),
|
||||||
disabled: isDownloading.value
|
disabled: isDownloading.value
|
||||||
@@ -148,7 +152,7 @@ const downloadMusic = async () => {
|
|||||||
try {
|
try {
|
||||||
isDownloading.value = true;
|
isDownloading.value = true;
|
||||||
const loadingMessage = message.loading('正在下载中...', { duration: 0 });
|
const loadingMessage = message.loading('正在下载中...', { duration: 0 });
|
||||||
|
|
||||||
const url = await getSongUrl(props.item.id, cloneDeep(props.item));
|
const url = await getSongUrl(props.item.id, cloneDeep(props.item));
|
||||||
if (!url) {
|
if (!url) {
|
||||||
loadingMessage.destroy();
|
loadingMessage.destroy();
|
||||||
@@ -156,21 +160,21 @@ const downloadMusic = async () => {
|
|||||||
isDownloading.value = false;
|
isDownloading.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先移除可能存在的旧监听器
|
// 先移除可能存在的旧监听器
|
||||||
window.electron.ipcRenderer.removeAllListeners('music-download-complete');
|
window.electron.ipcRenderer.removeAllListeners('music-download-complete');
|
||||||
|
|
||||||
// 发送下载请求
|
// 发送下载请求
|
||||||
window.electron.ipcRenderer.send('download-music', {
|
window.electron.ipcRenderer.send('download-music', {
|
||||||
url,
|
url,
|
||||||
filename: `${props.item.name} - ${(props.item.ar || props.item.song?.artists)?.map(a => a.name).join(',')}`
|
filename: `${props.item.name} - ${(props.item.ar || props.item.song?.artists)?.map((a) => a.name).join(',')}`
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加新的一次性监听器
|
// 添加新的一次性监听器
|
||||||
window.electron.ipcRenderer.once('music-download-complete', (_, result) => {
|
window.electron.ipcRenderer.once('music-download-complete', (_, result) => {
|
||||||
isDownloading.value = false;
|
isDownloading.value = false;
|
||||||
loadingMessage.destroy();
|
loadingMessage.destroy();
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
message.success('下载成功');
|
message.success('下载成功');
|
||||||
} else if (result.canceled) {
|
} else if (result.canceled) {
|
||||||
@@ -298,7 +302,7 @@ const toggleFavorite = async (e: Event) => {
|
|||||||
|
|
||||||
&-download {
|
&-download {
|
||||||
@apply mr-2 cursor-pointer;
|
@apply mr-2 cursor-pointer;
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@apply text-xl transition text-gray-500 dark:text-gray-400 hover:text-green-500;
|
@apply text-xl transition text-gray-500 dark:text-gray-400 hover:text-green-500;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,12 +44,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, computed, watch } from 'vue';
|
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import { checkUpdate, UpdateResult } from '@/utils/update';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
import config from '../../../../package.json';
|
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
|
import { checkUpdate, UpdateResult } from '@/utils/update';
|
||||||
|
|
||||||
|
import config from '../../../../package.json';
|
||||||
|
|
||||||
// 配置 marked
|
// 配置 marked
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
breaks: true, // 支持 GitHub 风格的换行
|
breaks: true, // 支持 GitHub 风格的换行
|
||||||
@@ -65,24 +67,27 @@ const updateInfo = ref<UpdateResult>({
|
|||||||
releaseInfo: null
|
releaseInfo: null
|
||||||
});
|
});
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore();
|
||||||
|
|
||||||
// 添加计算属性
|
// 添加计算属性
|
||||||
const showUpdateModalState = computed({
|
const showUpdateModalState = computed({
|
||||||
get: () => store.state.showUpdateModal,
|
get: () => store.state.showUpdateModal,
|
||||||
set: (val) => store.commit('setShowUpdateModal', val)
|
set: (val) => store.commit('setShowUpdateModal', val)
|
||||||
})
|
});
|
||||||
|
|
||||||
// 替换原来的 watch
|
// 替换原来的 watch
|
||||||
watch(showUpdateModalState, (newVal) => {
|
watch(showUpdateModalState, (newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
showModal.value = true
|
showModal.value = true;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
watch(() => showModal.value, (newVal) => {
|
watch(
|
||||||
showUpdateModalState.value = newVal
|
() => showModal.value,
|
||||||
})
|
(newVal) => {
|
||||||
|
showUpdateModalState.value = newVal;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 解析 Markdown
|
// 解析 Markdown
|
||||||
const parsedReleaseNotes = computed(() => {
|
const parsedReleaseNotes = computed(() => {
|
||||||
@@ -116,52 +121,50 @@ const checkForUpdates = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleUpdate = async () => {
|
const handleUpdate = async () => {
|
||||||
|
|
||||||
const assets = updateInfo.value.releaseInfo?.assets || [];
|
const assets = updateInfo.value.releaseInfo?.assets || [];
|
||||||
const platform = window.electron.process.platform;
|
const { platform } = window.electron.process;
|
||||||
const arch = window.electron.ipcRenderer.sendSync('get-arch');
|
const arch = window.electron.ipcRenderer.sendSync('get-arch');
|
||||||
console.log('arch',arch)
|
console.log('arch', arch);
|
||||||
console.log('platform',platform)
|
console.log('platform', platform);
|
||||||
const version = updateInfo.value.latestVersion
|
const version = updateInfo.value.latestVersion;
|
||||||
const downUrls = {
|
const downUrls = {
|
||||||
win32: {
|
win32: {
|
||||||
all: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-win.exe`,
|
all: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-win.exe`,
|
||||||
x64: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-win-x64.exe`,
|
x64: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-win-x64.exe`,
|
||||||
ia32: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-win-ia32.exe`,
|
ia32: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-win-ia32.exe`
|
||||||
},
|
},
|
||||||
darwin: {
|
darwin: {
|
||||||
all: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}AlgerMusicPlayer-${version}-mac-universal.dmg`,
|
all: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}AlgerMusicPlayer-${version}-mac-universal.dmg`
|
||||||
},
|
},
|
||||||
linux: {
|
linux: {
|
||||||
AppImage: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-linux-x64.AppImage`,
|
AppImage: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-linux-x64.AppImage`,
|
||||||
deb: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-linux-x64.deb`,
|
deb: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-linux-x64.deb`
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
let downloadUrl = '';
|
let downloadUrl = '';
|
||||||
|
|
||||||
// 根据平台和架构选择对应的安装包
|
// 根据平台和架构选择对应的安装包
|
||||||
if (platform === 'darwin') {
|
if (platform === 'darwin') {
|
||||||
// macOS
|
// macOS
|
||||||
const macAsset = assets.find(asset =>
|
const macAsset = assets.find((asset) => asset.name.includes('mac'));
|
||||||
asset.name.includes('mac')
|
|
||||||
);
|
|
||||||
downloadUrl = macAsset?.browser_download_url || downUrls.darwin.all || '';
|
downloadUrl = macAsset?.browser_download_url || downUrls.darwin.all || '';
|
||||||
} else if (platform === 'win32') {
|
} else if (platform === 'win32') {
|
||||||
// Windows
|
// Windows
|
||||||
const winAsset = assets.find(asset =>
|
const winAsset = assets.find(
|
||||||
asset.name.includes('win') &&
|
(asset) =>
|
||||||
(arch === 'x64' ? asset.name.includes('x64') : asset.name.includes('ia32'))
|
asset.name.includes('win') &&
|
||||||
|
(arch === 'x64' ? asset.name.includes('x64') : asset.name.includes('ia32'))
|
||||||
);
|
);
|
||||||
downloadUrl = winAsset?.browser_download_url || downUrls.win32[arch] || downUrls.win32.all || '';
|
downloadUrl =
|
||||||
|
winAsset?.browser_download_url || downUrls.win32[arch] || downUrls.win32.all || '';
|
||||||
} else if (platform === 'linux') {
|
} else if (platform === 'linux') {
|
||||||
// Linux
|
// Linux
|
||||||
const linuxAsset = assets.find(asset =>
|
const linuxAsset = assets.find(
|
||||||
(asset.name.endsWith('.AppImage') || asset.name.endsWith('.deb')) &&
|
(asset) =>
|
||||||
asset.name.includes('x64')
|
(asset.name.endsWith('.AppImage') || asset.name.endsWith('.deb')) &&
|
||||||
|
asset.name.includes('x64')
|
||||||
);
|
);
|
||||||
downloadUrl = linuxAsset?.browser_download_url || downUrls.linux[arch] || '';
|
downloadUrl = linuxAsset?.browser_download_url || downUrls.linux[arch] || '';
|
||||||
}
|
}
|
||||||
@@ -211,7 +214,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
.update-body {
|
.update-body {
|
||||||
@apply p-4 pt-2 text-gray-600 dark:text-gray-300 rounded-lg overflow-hidden;
|
@apply p-4 pt-2 text-gray-600 dark:text-gray-300 rounded-lg overflow-hidden;
|
||||||
|
|
||||||
:deep(h1) {
|
:deep(h1) {
|
||||||
@apply text-xl font-bold mb-3;
|
@apply text-xl font-bold mb-3;
|
||||||
}
|
}
|
||||||
@@ -253,7 +256,8 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
:deep(table) {
|
:deep(table) {
|
||||||
@apply w-full mb-3;
|
@apply w-full mb-3;
|
||||||
th, td {
|
th,
|
||||||
|
td {
|
||||||
@apply px-3 py-2 border border-gray-200 dark:border-gray-600;
|
@apply px-3 py-2 border border-gray-200 dark:border-gray-600;
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
@@ -282,4 +286,4 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { computed, ref } from 'vue';
|
|||||||
import { audioService } from '@/services/audioService';
|
import { audioService } from '@/services/audioService';
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
import type { ILyricText, SongResult } from '@/type/music';
|
import type { ILyricText, SongResult } from '@/type/music';
|
||||||
import { getTextColors } from '@/utils/linearColor';
|
|
||||||
import { isElectron } from '@/utils';
|
import { isElectron } from '@/utils';
|
||||||
|
import { getTextColors } from '@/utils/linearColor';
|
||||||
|
|
||||||
const windowData = window as any;
|
const windowData = window as any;
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ export const currentLrcProgress = ref(0); // 来存储当前歌词的进度
|
|||||||
export const playMusic = computed(() => store.state.playMusic as SongResult); // 当前播放歌曲
|
export const playMusic = computed(() => store.state.playMusic as SongResult); // 当前播放歌曲
|
||||||
export const sound = ref<Howl | null>(audioService.getCurrentSound());
|
export const sound = ref<Howl | null>(audioService.getCurrentSound());
|
||||||
export const isLyricWindowOpen = ref(false); // 新增状态
|
export const isLyricWindowOpen = ref(false); // 新增状态
|
||||||
export const textColors = ref(getTextColors());
|
export const textColors = ref<any>(getTextColors());
|
||||||
|
|
||||||
document.onkeyup = (e) => {
|
document.onkeyup = (e) => {
|
||||||
// 检查事件目标是否是输入框元素
|
// 检查事件目标是否是输入框元素
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Howl } from 'howler';
|
import { Howl } from 'howler';
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
|
||||||
import { getMusicLrc, getMusicUrl, getParsingMusicUrl } from '@/api/music';
|
import { getMusicLrc, getMusicUrl, getParsingMusicUrl } from '@/api/music';
|
||||||
import { useMusicHistory } from '@/hooks/MusicHistoryHook';
|
import { useMusicHistory } from '@/hooks/MusicHistoryHook';
|
||||||
@@ -6,7 +7,6 @@ import { audioService } from '@/services/audioService';
|
|||||||
import type { ILyric, ILyricText, SongResult } from '@/type/music';
|
import type { ILyric, ILyricText, SongResult } from '@/type/music';
|
||||||
import { getImgUrl } from '@/utils';
|
import { getImgUrl } from '@/utils';
|
||||||
import { getImageLinearBackground } from '@/utils/linearColor';
|
import { getImageLinearBackground } from '@/utils/linearColor';
|
||||||
import { cloneDeep } from 'lodash';
|
|
||||||
|
|
||||||
const musicHistory = useMusicHistory();
|
const musicHistory = useMusicHistory();
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<play-bar v-if="isPlay" :style="isMobile && store.state.musicFull ? 'bottom: 0;' : ''" />
|
<play-bar v-if="isPlay" :style="isMobile && store.state.musicFull ? 'bottom: 0;' : ''" />
|
||||||
</div>
|
</div>
|
||||||
<install-app-modal v-if="!isElectron"></install-app-modal>
|
<install-app-modal v-if="!isElectron"></install-app-modal>
|
||||||
<update-modal v-if="isElectron"/>
|
<update-modal v-if="isElectron" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -39,9 +39,9 @@ import { useStore } from 'vuex';
|
|||||||
|
|
||||||
import InstallAppModal from '@/components/common/InstallAppModal.vue';
|
import InstallAppModal from '@/components/common/InstallAppModal.vue';
|
||||||
import PlayBottom from '@/components/common/PlayBottom.vue';
|
import PlayBottom from '@/components/common/PlayBottom.vue';
|
||||||
|
import UpdateModal from '@/components/common/UpdateModal.vue';
|
||||||
import homeRouter from '@/router/home';
|
import homeRouter from '@/router/home';
|
||||||
import { isElectron, isMobile } from '@/utils';
|
import { isElectron, isMobile } from '@/utils';
|
||||||
import UpdateModal from '@/components/common/UpdateModal.vue';
|
|
||||||
|
|
||||||
const keepAliveInclude = computed(() =>
|
const keepAliveInclude = computed(() =>
|
||||||
homeRouter
|
homeRouter
|
||||||
|
|||||||
@@ -116,11 +116,15 @@ const lrcScroll = (behavior = 'smooth') => {
|
|||||||
const debouncedLrcScroll = useDebounceFn(lrcScroll, 200);
|
const debouncedLrcScroll = useDebounceFn(lrcScroll, 200);
|
||||||
|
|
||||||
const mouseOverLayout = () => {
|
const mouseOverLayout = () => {
|
||||||
if(isMobile.value) {return}
|
if (isMobile.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
isMouse.value = true;
|
isMouse.value = true;
|
||||||
};
|
};
|
||||||
const mouseLeaveLayout = () => {
|
const mouseLeaveLayout = () => {
|
||||||
if(isMobile.value) {return}
|
if (isMobile.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isMouse.value = false;
|
isMouse.value = false;
|
||||||
lrcScroll();
|
lrcScroll();
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
</n-popover>
|
</n-popover>
|
||||||
</div>
|
</div>
|
||||||
<!-- 播放音乐 -->
|
<!-- 播放音乐 -->
|
||||||
<music-full ref="MusicFullRef" v-model:music-full="musicFullVisible" :background="background" />
|
<music-full ref="MusicFullRef" v-model:music-full="musicFullVisible" :background="background" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ import {
|
|||||||
textColors
|
textColors
|
||||||
} from '@/hooks/MusicHook';
|
} from '@/hooks/MusicHook';
|
||||||
import type { SongResult } from '@/type/music';
|
import type { SongResult } from '@/type/music';
|
||||||
import { getImgUrl, isMobile, secondToMinute, setAnimationClass, isElectron } from '@/utils';
|
import { getImgUrl, isElectron, isMobile, secondToMinute, setAnimationClass } from '@/utils';
|
||||||
|
|
||||||
import MusicFull from './MusicFull.vue';
|
import MusicFull from './MusicFull.vue';
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, watchEffect, computed } from 'vue';
|
import { computed, onMounted, ref, watchEffect } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ const checkForUpdates = async () => {
|
|||||||
|
|
||||||
const toGithubRelease = () => {
|
const toGithubRelease = () => {
|
||||||
if (updateInfo.value.hasUpdate) {
|
if (updateInfo.value.hasUpdate) {
|
||||||
store.commit('setShowUpdateModal', true)
|
store.commit('setShowUpdateModal', true);
|
||||||
} else {
|
} else {
|
||||||
window.open('https://github.com/algerkong/AlgerMusicPlayer/releases', '_blank');
|
window.open('https://github.com/algerkong/AlgerMusicPlayer/releases', '_blank');
|
||||||
}
|
}
|
||||||
@@ -317,7 +317,7 @@ const toGithubRelease = () => {
|
|||||||
|
|
||||||
.version-info {
|
.version-info {
|
||||||
@apply ml-auto flex items-center;
|
@apply ml-auto flex items-center;
|
||||||
|
|
||||||
.version-number {
|
.version-number {
|
||||||
@apply text-xs px-2 py-0.5 rounded;
|
@apply text-xs px-2 py-0.5 rounded;
|
||||||
@apply bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300;
|
@apply bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
import { isElectron } from '@/utils';
|
import { isElectron } from '@/utils';
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
@@ -56,7 +57,7 @@ const handleAction = (action: 'minimize' | 'close') => {
|
|||||||
closeAction: action
|
closeAction: action
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'minimize') {
|
if (action === 'minimize') {
|
||||||
window.api.miniTray();
|
window.api.miniTray();
|
||||||
} else {
|
} else {
|
||||||
@@ -70,13 +71,13 @@ const close = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeAction = store.state.setData.closeAction;
|
const { closeAction } = store.state.setData;
|
||||||
|
|
||||||
if (closeAction === 'minimize') {
|
if (closeAction === 'minimize') {
|
||||||
window.api.miniTray();
|
window.api.miniTray();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closeAction === 'close') {
|
if (closeAction === 'close') {
|
||||||
window.api.close();
|
window.api.close();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { createStore } from 'vuex';
|
import { createStore } from 'vuex';
|
||||||
|
|
||||||
|
import setData from '@/../main/set.json';
|
||||||
|
import { getLikedList, likeSong } from '@/api/music';
|
||||||
import { useMusicListHook } from '@/hooks/MusicListHook';
|
import { useMusicListHook } from '@/hooks/MusicListHook';
|
||||||
import homeRouter from '@/router/home';
|
import homeRouter from '@/router/home';
|
||||||
import type { SongResult } from '@/type/music';
|
import type { SongResult } from '@/type/music';
|
||||||
import { applyTheme, getCurrentTheme, ThemeType } from '@/utils/theme';
|
|
||||||
import { isElectron } from '@/utils';
|
import { isElectron } from '@/utils';
|
||||||
import { likeSong, getLikedList } from '@/api/music';
|
import { applyTheme, getCurrentTheme, ThemeType } from '@/utils/theme';
|
||||||
import setData from '@/../main/set.json'
|
|
||||||
|
|
||||||
// 默认设置
|
// 默认设置
|
||||||
const defaultSettings = setData;
|
const defaultSettings = setData;
|
||||||
@@ -93,14 +93,18 @@ const mutations = {
|
|||||||
// 'set',
|
// 'set',
|
||||||
// JSON.parse(JSON.stringify(setData))
|
// JSON.parse(JSON.stringify(setData))
|
||||||
// );
|
// );
|
||||||
window.electron.ipcRenderer.send('set-store-value', 'set', JSON.parse(JSON.stringify(setData)));
|
window.electron.ipcRenderer.send(
|
||||||
|
'set-store-value',
|
||||||
|
'set',
|
||||||
|
JSON.parse(JSON.stringify(setData))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem('appSettings', JSON.stringify(setData));
|
localStorage.setItem('appSettings', JSON.stringify(setData));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async addToFavorite(state: State, songId: number) {
|
async addToFavorite(state: State, songId: number) {
|
||||||
try {
|
try {
|
||||||
state.user && localStorage.getItem('token') && await likeSong(songId, true);
|
state.user && localStorage.getItem('token') && (await likeSong(songId, true));
|
||||||
if (!state.favoriteList.includes(songId)) {
|
if (!state.favoriteList.includes(songId)) {
|
||||||
state.favoriteList = [songId, ...state.favoriteList];
|
state.favoriteList = [songId, ...state.favoriteList];
|
||||||
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
|
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
|
||||||
@@ -111,7 +115,7 @@ const mutations = {
|
|||||||
},
|
},
|
||||||
async removeFromFavorite(state: State, songId: number) {
|
async removeFromFavorite(state: State, songId: number) {
|
||||||
try {
|
try {
|
||||||
state.user && localStorage.getItem('token') && await likeSong(songId, false);
|
state.user && localStorage.getItem('token') && (await likeSong(songId, false));
|
||||||
state.favoriteList = state.favoriteList.filter((id) => id !== songId);
|
state.favoriteList = state.favoriteList.filter((id) => id !== songId);
|
||||||
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
|
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -127,7 +131,7 @@ const mutations = {
|
|||||||
applyTheme(state.theme);
|
applyTheme(state.theme);
|
||||||
},
|
},
|
||||||
setShowUpdateModal(state, value) {
|
setShowUpdateModal(state, value) {
|
||||||
state.showUpdateModal = value
|
state.showUpdateModal = value;
|
||||||
},
|
},
|
||||||
logout(state: State) {
|
logout(state: State) {
|
||||||
state.user = null;
|
state.user = null;
|
||||||
@@ -158,13 +162,13 @@ const actions = {
|
|||||||
},
|
},
|
||||||
async initializeFavoriteList({ state }: { state: State }) {
|
async initializeFavoriteList({ state }: { state: State }) {
|
||||||
try {
|
try {
|
||||||
if(state.user && localStorage.getItem('token')){
|
if (state.user && localStorage.getItem('token')) {
|
||||||
const res = await getLikedList();
|
const res = await getLikedList();
|
||||||
if (res.data?.ids) {
|
if (res.data?.ids) {
|
||||||
state.favoriteList = res.data.ids.reverse();
|
state.favoriteList = res.data.ids.reverse();
|
||||||
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
|
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
const localFavoriteList = localStorage.getItem('favoriteList');
|
const localFavoriteList = localStorage.getItem('favoriteList');
|
||||||
if (localFavoriteList) {
|
if (localFavoriteList) {
|
||||||
state.favoriteList = JSON.parse(localFavoriteList);
|
state.favoriteList = JSON.parse(localFavoriteList);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ interface Profile {
|
|||||||
createTime: number;
|
createTime: number;
|
||||||
nickname: string;
|
nickname: string;
|
||||||
avatarUrl: string;
|
avatarUrl: string;
|
||||||
experts: Experts;
|
experts: any;
|
||||||
expertTags?: any;
|
expertTags?: any;
|
||||||
djStatus: number;
|
djStatus: number;
|
||||||
accountStatus: number;
|
accountStatus: number;
|
||||||
@@ -79,8 +79,6 @@ interface Profile {
|
|||||||
newFollows: number;
|
newFollows: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Experts {}
|
|
||||||
|
|
||||||
interface UserPoint {
|
interface UserPoint {
|
||||||
userId: number;
|
userId: number;
|
||||||
balance: number;
|
balance: number;
|
||||||
|
|||||||
2
src/renderer/types/electron.d.ts
vendored
2
src/renderer/types/electron.d.ts
vendored
@@ -19,4 +19,4 @@ declare global {
|
|||||||
interface Window {
|
interface Window {
|
||||||
api: IElectronAPI;
|
api: IElectronAPI;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,4 +29,4 @@ export const openDirectory = (path: string | undefined, message: MessageApi, sho
|
|||||||
} else if (showTip) {
|
} else if (showTip) {
|
||||||
message.info('目录不存在');
|
message.info('目录不存在');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
|
|
||||||
// 设置歌手背景图片
|
// 设置歌手背景图片
|
||||||
@@ -71,4 +72,4 @@ export const isMobile = computed(() => {
|
|||||||
return !!flag;
|
return !!flag;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const isElectron = (window as any).electron !== undefined;
|
export const isElectron = (window as any).electron !== undefined;
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
import axios, { InternalAxiosRequestConfig } from 'axios';
|
import axios, { InternalAxiosRequestConfig } from 'axios';
|
||||||
import { isElectron } from '.';
|
import { createDiscreteApi } from 'naive-ui';
|
||||||
|
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
import { createDiscreteApi } from 'naive-ui'
|
|
||||||
|
|
||||||
|
import { isElectron } from '.';
|
||||||
|
|
||||||
const { notification } = createDiscreteApi(
|
const { notification } = createDiscreteApi(['notification']);
|
||||||
['notification']
|
|
||||||
)
|
|
||||||
|
|
||||||
let setData: any = null;
|
let setData: any = null;
|
||||||
const getSetData = ()=>{
|
const getSetData = () => {
|
||||||
if (window.electron) {
|
if (window.electron) {
|
||||||
setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set');
|
setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
getSetData()
|
getSetData();
|
||||||
// 扩展请求配置接口
|
// 扩展请求配置接口
|
||||||
interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
|
interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
|
||||||
retryCount?: number;
|
retryCount?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseURL = window.electron ? `http://127.0.0.1:${setData?.musicApiPort}` : import.meta.env.VITE_API;
|
const baseURL = window.electron
|
||||||
|
? `http://127.0.0.1:${setData?.musicApiPort}`
|
||||||
|
: import.meta.env.VITE_API;
|
||||||
|
|
||||||
const request = axios.create({
|
const request = axios.create({
|
||||||
baseURL,
|
baseURL,
|
||||||
@@ -46,23 +47,23 @@ request.interceptors.request.use(
|
|||||||
if (config.method === 'get') {
|
if (config.method === 'get') {
|
||||||
config.params = {
|
config.params = {
|
||||||
...config.params,
|
...config.params,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now()
|
||||||
};
|
};
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (token) {
|
if (token) {
|
||||||
config.params.cookie = token + ' os=pc;';
|
config.params.cookie = `${token} os=pc;`;
|
||||||
}else{
|
} else {
|
||||||
config.params.cookie = 'os=pc;';
|
config.params.cookie = 'os=pc;';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isElectron){
|
if (isElectron) {
|
||||||
const proxyConfig = setData?.proxyConfig
|
const proxyConfig = setData?.proxyConfig;
|
||||||
if (proxyConfig?.enable && ['http', 'https'].includes(proxyConfig?.protocol)) {
|
if (proxyConfig?.enable && ['http', 'https'].includes(proxyConfig?.protocol)) {
|
||||||
config.params.proxy = `${proxyConfig.protocol}://${proxyConfig.host}:${proxyConfig.port}`
|
config.params.proxy = `${proxyConfig.protocol}://${proxyConfig.host}:${proxyConfig.port}`;
|
||||||
}
|
}
|
||||||
if(setData.enableRealIP && setData.realIP){
|
if (setData.enableRealIP && setData.realIP) {
|
||||||
config.params.realIP = setData.realIP
|
config.params.realIP = setData.realIP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ request.interceptors.response.use(
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
async (error) => {
|
async (error) => {
|
||||||
console.log('error',error)
|
console.log('error', error);
|
||||||
const config = error.config as CustomAxiosRequestConfig;
|
const config = error.config as CustomAxiosRequestConfig;
|
||||||
|
|
||||||
// 如果没有配置,直接返回错误
|
// 如果没有配置,直接返回错误
|
||||||
@@ -102,7 +103,7 @@ request.interceptors.response.use(
|
|||||||
meta: '请重新登录',
|
meta: '请重新登录',
|
||||||
duration: 2500,
|
duration: 2500,
|
||||||
keepAliveOnHover: true
|
keepAliveOnHover: true
|
||||||
})
|
});
|
||||||
|
|
||||||
// 延迟重试
|
// 延迟重试
|
||||||
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
|
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import config from '../../../package.json';
|
|
||||||
import { useDateFormat } from '@vueuse/core';
|
import { useDateFormat } from '@vueuse/core';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import config from '../../../package.json';
|
||||||
|
|
||||||
interface GithubReleaseInfo {
|
interface GithubReleaseInfo {
|
||||||
tag_name: string;
|
tag_name: string;
|
||||||
body: string;
|
body: string;
|
||||||
@@ -39,15 +41,15 @@ export const getLatestReleaseInfo = async (): Promise<GithubReleaseInfo | null>
|
|||||||
const apiUrls = [
|
const apiUrls = [
|
||||||
// 原始地址
|
// 原始地址
|
||||||
'https://api.github.com/repos/algerkong/AlgerMusicPlayer/releases/latest',
|
'https://api.github.com/repos/algerkong/AlgerMusicPlayer/releases/latest',
|
||||||
|
|
||||||
// 使用 ghproxy.com 代理
|
// 使用 ghproxy.com 代理
|
||||||
'https://www.ghproxy.cn/https://raw.githubusercontent.com/algerkong/AlgerMusicPlayer/dev_electron/package.json',
|
'https://www.ghproxy.cn/https://raw.githubusercontent.com/algerkong/AlgerMusicPlayer/dev_electron/package.json'
|
||||||
|
|
||||||
// 使用 gitee 镜像(如果有的话)
|
// 使用 gitee 镜像(如果有的话)
|
||||||
// 'https://gitee.com/api/v5/repos/[用户名]/AlgerMusicPlayer/releases/latest'
|
// 'https://gitee.com/api/v5/repos/[用户名]/AlgerMusicPlayer/releases/latest'
|
||||||
];
|
];
|
||||||
if (token) {
|
if (token) {
|
||||||
headers['Authorization'] = `token ${token}`;
|
headers['Authorization'] = `token ${token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const url of apiUrls) {
|
for (const url of apiUrls) {
|
||||||
@@ -58,7 +60,11 @@ export const getLatestReleaseInfo = async (): Promise<GithubReleaseInfo | null>
|
|||||||
// 如果是 package.json,直接读取版本号
|
// 如果是 package.json,直接读取版本号
|
||||||
return {
|
return {
|
||||||
tag_name: response.data.version,
|
tag_name: response.data.version,
|
||||||
body:(await axios.get('https://raw.githubusercontent.com/algerkong/AlgerMusicPlayer/dev_electron/CHANGELOG.md')).data,
|
body: (
|
||||||
|
await axios.get(
|
||||||
|
'https://raw.githubusercontent.com/algerkong/AlgerMusicPlayer/dev_electron/CHANGELOG.md'
|
||||||
|
)
|
||||||
|
).data,
|
||||||
html_url: 'https://github.com/algerkong/AlgerMusicPlayer/releases/latest',
|
html_url: 'https://github.com/algerkong/AlgerMusicPlayer/releases/latest',
|
||||||
assets: []
|
assets: []
|
||||||
} as unknown as GithubReleaseInfo;
|
} as unknown as GithubReleaseInfo;
|
||||||
@@ -86,10 +92,12 @@ export const formatDate = (dateStr: string): string => {
|
|||||||
/**
|
/**
|
||||||
* 检查更新
|
* 检查更新
|
||||||
*/
|
*/
|
||||||
export const checkUpdate = async (currentVersion: string = config.version): Promise<UpdateResult | null> => {
|
export const checkUpdate = async (
|
||||||
|
currentVersion: string = config.version
|
||||||
|
): Promise<UpdateResult | null> => {
|
||||||
try {
|
try {
|
||||||
const releaseInfo = await getLatestReleaseInfo();
|
const releaseInfo = await getLatestReleaseInfo();
|
||||||
console.log('releaseInfo',releaseInfo)
|
console.log('releaseInfo', releaseInfo);
|
||||||
if (!releaseInfo) {
|
if (!releaseInfo) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -98,8 +106,8 @@ export const checkUpdate = async (currentVersion: string = config.version): Prom
|
|||||||
if (latestVersion === currentVersion) {
|
if (latestVersion === currentVersion) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
console.log('latestVersion',latestVersion)
|
console.log('latestVersion', latestVersion);
|
||||||
console.log('currentVersion',currentVersion)
|
console.log('currentVersion', currentVersion);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasUpdate: true,
|
hasUpdate: true,
|
||||||
@@ -109,7 +117,7 @@ export const checkUpdate = async (currentVersion: string = config.version): Prom
|
|||||||
tag_name: latestVersion,
|
tag_name: latestVersion,
|
||||||
body: `## 更新内容\n\n- 版本: ${latestVersion}\n${releaseInfo.body}`,
|
body: `## 更新内容\n\n- 版本: ${latestVersion}\n${releaseInfo.body}`,
|
||||||
html_url: releaseInfo.html_url,
|
html_url: releaseInfo.html_url,
|
||||||
assets: releaseInfo.assets.map(asset => ({
|
assets: releaseInfo.assets.map((asset) => ({
|
||||||
browser_download_url: asset.browser_download_url,
|
browser_download_url: asset.browser_download_url,
|
||||||
name: asset.name
|
name: asset.name
|
||||||
}))
|
}))
|
||||||
@@ -119,4 +127,4 @@ export const checkUpdate = async (currentVersion: string = config.version): Prom
|
|||||||
console.error('检查更新失败:', error);
|
console.error('检查更新失败:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const scrollbarRef = ref();
|
|||||||
// 无限滚动相关
|
// 无限滚动相关
|
||||||
const pageSize = 16;
|
const pageSize = 16;
|
||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isComponent: {
|
isComponent: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ import { onMounted, ref } from 'vue';
|
|||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
import { getMusicDetail } from '@/api/music';
|
import { getMusicDetail } from '@/api/music';
|
||||||
|
import SongItem from '@/components/common/SongItem.vue';
|
||||||
import { useMusicHistory } from '@/hooks/MusicHistoryHook';
|
import { useMusicHistory } from '@/hooks/MusicHistoryHook';
|
||||||
import type { SongResult } from '@/type/music';
|
import type { SongResult } from '@/type/music';
|
||||||
import { setAnimationClass, setAnimationDelay } from '@/utils';
|
import { setAnimationClass, setAnimationDelay } from '@/utils';
|
||||||
import SongItem from '@/components/common/SongItem.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'History'
|
name: 'History'
|
||||||
|
|||||||
@@ -18,7 +18,12 @@
|
|||||||
</n-scrollbar>
|
</n-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
<!-- 歌单列表 -->
|
<!-- 歌单列表 -->
|
||||||
<n-scrollbar class="recommend" style="height: calc(100% - 55px)" :size="100" @scroll="handleScroll">
|
<n-scrollbar
|
||||||
|
class="recommend"
|
||||||
|
style="height: calc(100% - 55px)"
|
||||||
|
:size="100"
|
||||||
|
@scroll="handleScroll"
|
||||||
|
>
|
||||||
<div v-loading="loading" class="recommend-list">
|
<div v-loading="loading" class="recommend-list">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in recommendList"
|
v-for="(item, index) in recommendList"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
:class="setAnimationClass('animate__bounceInRight')"
|
:class="setAnimationClass('animate__bounceInRight')"
|
||||||
:style="setAnimationDelay(index, 50)"
|
:style="setAnimationDelay(index, 50)"
|
||||||
>
|
>
|
||||||
<SearchItem :item="item" />
|
<search-item :item="item" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
@@ -67,10 +67,10 @@ import { useStore } from 'vuex';
|
|||||||
|
|
||||||
import { getHotSearch } from '@/api/home';
|
import { getHotSearch } from '@/api/home';
|
||||||
import { getSearch } from '@/api/search';
|
import { getSearch } from '@/api/search';
|
||||||
|
import SearchItem from '@/components/common/SearchItem.vue';
|
||||||
import SongItem from '@/components/common/SongItem.vue';
|
import SongItem from '@/components/common/SongItem.vue';
|
||||||
import type { IHotSearch } from '@/type/search';
|
import type { IHotSearch } from '@/type/search';
|
||||||
import { isMobile, setAnimationClass, setAnimationDelay } from '@/utils';
|
import { isMobile, setAnimationClass, setAnimationDelay } from '@/utils';
|
||||||
import SearchItem from '@/components/common/SearchItem.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'Search'
|
name: 'Search'
|
||||||
|
|||||||
@@ -22,12 +22,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<n-switch v-model:value="setData.isProxy" />
|
<n-switch v-model:value="setData.isProxy" />
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="set-item" v-if="isElectron">
|
<div v-if="isElectron" class="set-item">
|
||||||
<div>
|
<div>
|
||||||
<div class="set-item-title">音乐API端口</div>
|
<div class="set-item-title">音乐API端口</div>
|
||||||
<div class="set-item-content">
|
<div class="set-item-content">修改后需要重启应用</div>
|
||||||
修改后需要重启应用
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<n-input-number v-model:value="setData.musicApiPort" />
|
<n-input-number v-model:value="setData.musicApiPort" />
|
||||||
</div>
|
</div>
|
||||||
@@ -55,7 +53,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="set-item" v-if="isElectron">
|
<div v-if="isElectron" class="set-item">
|
||||||
<div>
|
<div>
|
||||||
<div class="set-item-title">下载目录</div>
|
<div class="set-item-title">下载目录</div>
|
||||||
<div class="set-item-content">
|
<div class="set-item-content">
|
||||||
@@ -101,15 +99,17 @@
|
|||||||
@click="openAuthor"
|
@click="openAuthor"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Coffee>
|
<coffee>
|
||||||
<div>
|
<div>
|
||||||
<div class="set-item-title">作者</div>
|
<div class="set-item-title">作者</div>
|
||||||
<div class="set-item-content">algerkong 点个star🌟呗</div>
|
<div class="set-item-content">algerkong 点个star🌟呗</div>
|
||||||
</div>
|
</div>
|
||||||
</Coffee>
|
</coffee>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<n-button size="small" type="primary" @click="openAuthor"><i class="ri-github-line"></i>前往github</n-button>
|
<n-button size="small" type="primary" @click="openAuthor"
|
||||||
|
><i class="ri-github-line"></i>前往github</n-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="set-item">
|
<div class="set-item">
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
style="width: 160px"
|
style="width: 160px"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="set-item" v-if="isElectron">
|
<div v-if="isElectron" class="set-item">
|
||||||
<div>
|
<div>
|
||||||
<div class="set-item-title">关闭行为</div>
|
<div class="set-item-title">关闭行为</div>
|
||||||
<div class="set-item-content">
|
<div class="set-item-content">
|
||||||
@@ -151,14 +151,14 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="set-item" v-if="isElectron">
|
<div v-if="isElectron" class="set-item">
|
||||||
<div>
|
<div>
|
||||||
<div class="set-item-title">重启</div>
|
<div class="set-item-title">重启</div>
|
||||||
<div class="set-item-content">重启应用</div>
|
<div class="set-item-content">重启应用</div>
|
||||||
</div>
|
</div>
|
||||||
<n-button type="primary" @click="restartApp">重启</n-button>
|
<n-button type="primary" @click="restartApp">重启</n-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="set-item" v-if="isElectron">
|
<div v-if="isElectron" class="set-item">
|
||||||
<div>
|
<div>
|
||||||
<div class="set-item-title">代理设置</div>
|
<div class="set-item-title">代理设置</div>
|
||||||
<div class="set-item-content">无法访问音乐时可以开启代理</div>
|
<div class="set-item-content">无法访问音乐时可以开启代理</div>
|
||||||
@@ -171,10 +171,13 @@
|
|||||||
<n-button size="small" @click="showProxyModal = true">配置</n-button>
|
<n-button size="small" @click="showProxyModal = true">配置</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="set-item" v-if="isElectron">
|
<div v-if="isElectron" class="set-item">
|
||||||
<div>
|
<div>
|
||||||
<div class="set-item-title">realIP</div>
|
<div class="set-item-title">realIP</div>
|
||||||
<div class="set-item-content">由于限制,此项目在国外使用会受到限制可使用realIP参数,传进国内IP解决,如:116.25.146.177 即可解决</div>
|
<div class="set-item-content">
|
||||||
|
由于限制,此项目在国外使用会受到限制可使用realIP参数,传进国内IP解决,如:116.25.146.177
|
||||||
|
即可解决
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<n-switch v-model:value="setData.enableRealIP">
|
<n-switch v-model:value="setData.enableRealIP">
|
||||||
@@ -185,23 +188,22 @@
|
|||||||
v-if="setData.enableRealIP"
|
v-if="setData.enableRealIP"
|
||||||
v-model:value="setData.realIP"
|
v-model:value="setData.realIP"
|
||||||
placeholder="realIP"
|
placeholder="realIP"
|
||||||
@blur="validateAndSaveRealIP"
|
|
||||||
style="width: 200px"
|
style="width: 200px"
|
||||||
|
@blur="validateAndSaveRealIP"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<PlayBottom/>
|
<play-bottom />
|
||||||
<n-modal
|
<n-modal
|
||||||
v-model:show="showProxyModal"
|
v-model:show="showProxyModal"
|
||||||
preset="dialog"
|
preset="dialog"
|
||||||
title="代理设置"
|
title="代理设置"
|
||||||
positive-text="确认"
|
positive-text="确认"
|
||||||
negative-text="取消"
|
negative-text="取消"
|
||||||
|
:show-icon="false"
|
||||||
@positive-click="handleProxyConfirm"
|
@positive-click="handleProxyConfirm"
|
||||||
@negative-click="showProxyModal = false"
|
@negative-click="showProxyModal = false"
|
||||||
:show-icon="false"
|
|
||||||
>
|
>
|
||||||
<n-form
|
<n-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
@@ -225,7 +227,12 @@
|
|||||||
<n-input v-model:value="proxyForm.host" placeholder="请输入代理地址" />
|
<n-input v-model:value="proxyForm.host" placeholder="请输入代理地址" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="代理端口" path="port">
|
<n-form-item label="代理端口" path="port">
|
||||||
<n-input-number v-model:value="proxyForm.port" placeholder="请输入代理端口" :min="1" :max="65535" />
|
<n-input-number
|
||||||
|
v-model:value="proxyForm.port"
|
||||||
|
placeholder="请输入代理端口"
|
||||||
|
:min="1"
|
||||||
|
:max="65535"
|
||||||
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
@@ -233,16 +240,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, onMounted, watch } from 'vue';
|
|
||||||
import { useStore } from 'vuex';
|
|
||||||
import { useMessage } from 'naive-ui';
|
|
||||||
import type { FormRules } from 'naive-ui';
|
import type { FormRules } from 'naive-ui';
|
||||||
import { isElectron } from '@/utils';
|
import { useMessage } from 'naive-ui';
|
||||||
import { checkUpdate, UpdateResult } from '@/utils/update';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
import { selectDirectory, openDirectory } from '@/utils/fileOperation';
|
import { useStore } from 'vuex';
|
||||||
import config from '../../../../package.json';
|
|
||||||
import PlayBottom from '@/components/common/PlayBottom.vue';
|
|
||||||
import Coffee from '@/components/Coffee.vue';
|
import Coffee from '@/components/Coffee.vue';
|
||||||
|
import PlayBottom from '@/components/common/PlayBottom.vue';
|
||||||
|
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 store = useStore();
|
||||||
const checking = ref(false);
|
const checking = ref(false);
|
||||||
@@ -277,9 +286,13 @@ const setData = computed(() => {
|
|||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(() => setData.value, (newVal) => {
|
watch(
|
||||||
store.commit('setSetData', newVal)
|
() => setData.value,
|
||||||
}, { deep: true });
|
(newVal) => {
|
||||||
|
store.commit('setSetData', newVal);
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
const isDarkTheme = computed({
|
const isDarkTheme = computed({
|
||||||
get: () => store.state.theme === 'dark',
|
get: () => store.state.theme === 'dark',
|
||||||
@@ -317,7 +330,7 @@ const checkForUpdates = async (isClick = false) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const openReleasePage = () => {
|
const openReleasePage = () => {
|
||||||
store.commit('setShowUpdateModal', true)
|
store.commit('setShowUpdateModal', true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectDownloadPath = async () => {
|
const selectDownloadPath = async () => {
|
||||||
@@ -355,7 +368,8 @@ const proxyRules: FormRules = {
|
|||||||
validator: (_rule, value) => {
|
validator: (_rule, value) => {
|
||||||
if (!value) return false;
|
if (!value) return false;
|
||||||
// 简单的IP或域名验证
|
// 简单的IP或域名验证
|
||||||
const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$|^localhost$|^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
|
const ipRegex =
|
||||||
|
/^(\d{1,3}\.){3}\d{1,3}$|^localhost$|^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
|
||||||
return ipRegex.test(value);
|
return ipRegex.test(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -385,15 +399,19 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 监听代理配置变化
|
// 监听代理配置变化
|
||||||
watch(() => setData.value.proxyConfig, (newVal) => {
|
watch(
|
||||||
if (newVal) {
|
() => setData.value.proxyConfig,
|
||||||
proxyForm.value = {
|
(newVal) => {
|
||||||
protocol: newVal.protocol || 'http',
|
if (newVal) {
|
||||||
host: newVal.host || '127.0.0.1',
|
proxyForm.value = {
|
||||||
port: newVal.port || 7890
|
protocol: newVal.protocol || 'http',
|
||||||
};
|
host: newVal.host || '127.0.0.1',
|
||||||
}
|
port: newVal.port || 7890
|
||||||
}, { immediate: true, deep: true });
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
const handleProxyConfirm = async () => {
|
const handleProxyConfirm = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -436,15 +454,18 @@ const validateAndSaveRealIP = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 监听enableRealIP变化,当关闭时清空realIP
|
// 监听enableRealIP变化,当关闭时清空realIP
|
||||||
watch(() => setData.value.enableRealIP, (newVal) => {
|
watch(
|
||||||
if (!newVal) {
|
() => setData.value.enableRealIP,
|
||||||
store.commit('setSetData', {
|
(newVal) => {
|
||||||
...setData.value,
|
if (!newVal) {
|
||||||
realIP: '',
|
store.commit('setSetData', {
|
||||||
enableRealIP: false
|
...setData.value,
|
||||||
});
|
realIP: '',
|
||||||
|
enableRealIP: false
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, watch, onBeforeUnmount } from 'vue';
|
import { computed, onBeforeUnmount, ref, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
const loadPage = async () => {
|
const loadPage = async () => {
|
||||||
if (!mounted.value || !user.value) return;
|
if (!mounted.value || !user.value) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
infoLoading.value = true;
|
infoLoading.value = true;
|
||||||
|
|
||||||
@@ -154,15 +154,19 @@ const loadPage = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 监听用户状态变化
|
// 监听用户状态变化
|
||||||
watch(() => store.state.user, (newUser) => {
|
watch(
|
||||||
if (!mounted.value) return;
|
() => store.state.user,
|
||||||
|
(newUser) => {
|
||||||
if (!newUser) {
|
if (!mounted.value) return;
|
||||||
router.push('/login');
|
|
||||||
} else {
|
if (!newUser) {
|
||||||
loadPage();
|
router.push('/login');
|
||||||
}
|
} else {
|
||||||
}, { immediate: true });
|
loadPage();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
// 展示歌单
|
// 展示歌单
|
||||||
const showPlaylist = async (id: number, name: string) => {
|
const showPlaylist = async (id: number, name: string) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user