Skip to content

Commit 56045cd

Browse files
committed
完善功能
1 parent dbf6121 commit 56045cd

16 files changed

Lines changed: 552 additions & 182 deletions

File tree

components.d.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ declare module 'vue' {
1010
export interface GlobalComponents {
1111
AboutSetting: typeof import('./src/components/Setting/AboutSetting.vue')['default']
1212
ArtistList: typeof import('./src/components/List/ArtistList.vue')['default']
13-
BatchList: typeof import('./src/components/Modal/batchList.vue')['default']
13+
AutoClose: typeof import('./src/components/Modal/AutoClose.vue')['default']
14+
BatchList: typeof import('./src/components/Modal/BatchList.vue')['default']
15+
ChangeRate: typeof import('./src/components/Modal/ChangeRate.vue')['default']
1416
CloudMatch: typeof import('./src/components/Modal/CloudMatch.vue')['default']
1517
CommentList: typeof import('./src/components/List/CommentList.vue')['default']
1618
CountDown: typeof import('./src/components/Player/CountDown.vue')['default']
@@ -92,19 +94,16 @@ declare module 'vue' {
9294
NNumberAnimation: typeof import('naive-ui')['NNumberAnimation']
9395
NOl: typeof import('naive-ui')['NOl']
9496
NP: typeof import('naive-ui')['NP']
97+
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
9598
NPopover: typeof import('naive-ui')['NPopover']
96-
NPopselect: typeof import('naive-ui')['NPopselect']
9799
NProgress: typeof import('naive-ui')['NProgress']
98100
NQrCode: typeof import('naive-ui')['NQrCode']
99101
NRadio: typeof import('naive-ui')['NRadio']
100-
NRadioButton: typeof import('naive-ui')['NRadioButton']
101102
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
102103
NScrollbar: typeof import('naive-ui')['NScrollbar']
103104
NSelect: typeof import('naive-ui')['NSelect']
104-
NSilder: typeof import('naive-ui')['NSilder']
105105
NSkeleton: typeof import('naive-ui')['NSkeleton']
106106
NSlider: typeof import('naive-ui')['NSlider']
107-
NSpace: typeof import('naive-ui')['NSpace']
108107
NSpin: typeof import('naive-ui')['NSpin']
109108
NSwitch: typeof import('naive-ui')['NSwitch']
110109
NTab: typeof import('naive-ui')['NTab']

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false",
2424
"typecheck": "npm run typecheck:node && npm run typecheck:web",
2525
"start": "electron-vite preview",
26-
"dev": "electron-vite dev",
26+
"dev": "node scripts/dev.mjs",
2727
"build": "npx rimraf dist && npm run typecheck && electron-vite build",
2828
"postinstall": "electron-builder install-app-deps",
2929
"build:web": "npm run build",

scripts/dev.mjs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* 跨平台开发启动脚本
5+
* 自动检测操作系统并设置相应的字符编码
6+
*/
7+
8+
import { spawn } from "child_process";
9+
import os from "os";
10+
11+
// 检测操作系统平台
12+
const platform = os.platform();
13+
const isWindows = platform === "win32";
14+
const isMacOS = platform === "darwin";
15+
16+
console.log(`🚀 检测到操作系统: ${platform}`);
17+
18+
// 设置环境变量
19+
const env = { ...process.env };
20+
21+
if (isWindows) {
22+
console.log("Windows 环境 - 正在设置代码页为 UTF-8");
23+
// Windows 环境下先执行 chcp 65001
24+
const chcp = spawn("chcp", ["65001"], {
25+
stdio: "inherit",
26+
shell: true,
27+
env,
28+
});
29+
30+
chcp.on("close", (code) => {
31+
if (code === 0) {
32+
console.log("✅ 代码页设置成功");
33+
startElectronVite();
34+
} else {
35+
console.warn("⚠️ 代码页设置失败,继续启动...");
36+
startElectronVite();
37+
}
38+
});
39+
} else {
40+
// macOS 和 Linux 环境
41+
console.log(`🐧 ${isMacOS ? "macOS" : "Linux"} 环境 - 正在设置 UTF-8 编码`);
42+
env.LC_ALL = "en_US.UTF-8";
43+
env.LANG = "en_US.UTF-8";
44+
startElectronVite();
45+
}
46+
47+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
48+
const startElectronVite = () => {
49+
console.log("🔧 正在启动 Electron Vite 开发服务器...");
50+
51+
// 设置 Node.js 选项
52+
env.NODE_OPTIONS = "--max-old-space-size=4096";
53+
54+
const electronVite = spawn("electron-vite", ["dev"], {
55+
stdio: "inherit",
56+
shell: true,
57+
env,
58+
});
59+
60+
electronVite.on("close", (code) => {
61+
console.log(`\n🏁 开发服务器已停止 (退出码: ${code})`);
62+
process.exit(code);
63+
});
64+
65+
electronVite.on("error", (err) => {
66+
console.error("❌ 启动失败:", err.message);
67+
process.exit(1);
68+
});
69+
70+
// 优雅退出处理
71+
process.on("SIGINT", () => {
72+
console.log("\n🛑 正在停止开发服务器...");
73+
electronVite.kill("SIGINT");
74+
});
75+
76+
process.on("SIGTERM", () => {
77+
console.log("\n🛑 正在停止开发服务器...");
78+
electronVite.kill("SIGTERM");
79+
});
80+
};

src/assets/icons/PlayRate.svg

Lines changed: 1 addition & 0 deletions
Loading

src/assets/icons/TimeAuto.svg

Lines changed: 1 addition & 0 deletions
Loading

src/components/Modal/AutoClose.vue

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<template>
2+
<n-flex :size="20" class="auto-close" align="center" vertical>
3+
<n-card class="open">
4+
<n-flex align="center" justify="space-between">
5+
<n-flex size="small" align="center">
6+
<SvgIcon name="TimeAuto" size="22" />
7+
<Transition name="fade" mode="out-in">
8+
<n-text v-if="!statusStore.autoClose.enable"> 未开启 </n-text>
9+
<n-text v-else strong>
10+
{{ convertSecondsToTime(statusStore.autoClose.remainTime) }}
11+
</n-text>
12+
</Transition>
13+
</n-flex>
14+
<n-switch v-model:value="statusStore.autoClose.enable" :round="false" />
15+
</n-flex>
16+
</n-card>
17+
<!-- 时间选择 -->
18+
<n-flex size="large" align="center" justify="center">
19+
<n-tag
20+
v-for="(item, index) in [10, 20, 30, 45, 60, 90, 120]"
21+
:bordered="false"
22+
:key="index"
23+
type="primary"
24+
size="large"
25+
round
26+
>
27+
{{ item }}min
28+
</n-tag>
29+
<!-- 自定义 -->
30+
<n-popconfirm
31+
:negative-text="null"
32+
:positive-button-props="{
33+
strong: true,
34+
secondary: true,
35+
type: 'primary',
36+
}"
37+
:show-icon="false"
38+
>
39+
<template #trigger>
40+
<n-tag :bordered="false" type="primary" size="large" round> 自定义时长 </n-tag>
41+
</template>
42+
<n-flex vertical>
43+
<n-text>自定义时长(分钟)</n-text>
44+
<n-input-number v-model:value="statusStore.autoClose.remainTime" />
45+
</n-flex>
46+
</n-popconfirm>
47+
</n-flex>
48+
<!-- 是否播放完 -->
49+
<n-checkbox v-model:checked="statusStore.autoClose.waitSongEnd">
50+
等待整首歌曲播放完成再停止播放
51+
</n-checkbox>
52+
</n-flex>
53+
</template>
54+
55+
<script setup lang="ts">
56+
import { useStatusStore } from "@/stores";
57+
import { convertSecondsToTime } from "@/utils/time";
58+
59+
const statusStore = useStatusStore();
60+
</script>
61+
62+
<style scoped lang="scss">
63+
.auto-close {
64+
width: 100%;
65+
.open {
66+
width: 100%;
67+
border-radius: 8px;
68+
.n-text {
69+
font-size: 18px;
70+
}
71+
}
72+
}
73+
</style>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<template>
2+
<n-flex align="center" size="large" vertical>
3+
<n-flex align="center" justify="center">
4+
<n-tag
5+
v-for="(item, index) in [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]"
6+
:type="statusStore.playRate === item ? 'primary' : 'default'"
7+
:bordered="statusStore.playRate === item"
8+
:key="index"
9+
size="large"
10+
round
11+
@click="player.setRate(item)"
12+
>
13+
{{ item }}x
14+
</n-tag>
15+
</n-flex>
16+
<n-text :depth="3"> 当前播放速度: {{ statusStore.playRate }}x </n-text>
17+
<n-slider
18+
v-model:value="statusStore.playRate"
19+
:step="0.1"
20+
:min="0.2"
21+
:max="2"
22+
:tooltip="false"
23+
:marks="{
24+
0.2: '0.2x',
25+
1: '1x',
26+
2: '2x',
27+
}"
28+
@update:value="(value) => player.setRate(value)"
29+
/>
30+
</n-flex>
31+
</template>
32+
33+
<script setup lang="ts">
34+
import { useStatusStore } from "@/stores";
35+
import player from "@/utils/player";
36+
37+
const statusStore = useStatusStore();
38+
</script>

src/components/Player/FullPlayer.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,7 @@
7575
<div v-else-if="musicStore.isHasLrc" class="content-right">
7676
<!-- 数据 -->
7777
<PlayerData
78-
v-if="
79-
(statusStore.pureLyricMode && musicStore.isHasLrc) ||
80-
(settingStore.playerType === 'record' && musicStore.isHasLrc)
81-
"
78+
v-if="statusStore.pureLyricMode && musicStore.isHasLrc"
8279
:center="statusStore.pureLyricMode"
8380
:theme="statusStore.mainColor"
8481
/>

src/components/Player/MainPlayer.vue

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@
1515
:max="100"
1616
:tooltip="false"
1717
:keyboard="false"
18-
:marks="
19-
statusStore.chorus && statusStore.progress <= statusStore.chorus
20-
? { [statusStore.chorus]: '' }
21-
: undefined
22-
"
2318
class="player-slider"
2419
@dragstart="player.pause(false)"
2520
@dragend="sliderDragend"
@@ -61,6 +56,16 @@
6156
:speed="0.2"
6257
class="name"
6358
/>
59+
<!-- 倍速 -->
60+
<n-tag
61+
v-if="statusStore.playRate !== 1"
62+
type="primary"
63+
size="small"
64+
round
65+
@click="openChangeRate"
66+
>
67+
{{ statusStore.playRate }}x
68+
</n-tag>
6469
<!-- 喜欢 -->
6570
<SvgIcon
6671
v-if="musicStore.playSong.type !== 'radio'"
@@ -174,7 +179,7 @@ import { useMusicStore, useStatusStore, useDataStore, useSettingStore } from "@/
174179
import { secondsToTime, calculateCurrentTime } from "@/utils/time";
175180
import { renderIcon, coverLoaded } from "@/utils/helper";
176181
import { toLikeSong } from "@/utils/auth";
177-
import { openDownloadSong, openJumpArtist, openPlaylistAdd } from "@/utils/modal";
182+
import { openChangeRate, openDownloadSong, openJumpArtist, openPlaylistAdd } from "@/utils/modal";
178183
import player from "@/utils/player";
179184
180185
const router = useRouter();
@@ -293,14 +298,14 @@ const instantLyrics = computed(() => {
293298
margin: 0;
294299
--n-rail-height: 3px;
295300
--n-handle-size: 14px;
296-
:deep(.n-slider-rail) {
297-
.n-slider-rail__fill {
298-
transition: width 0.3s;
299-
}
300-
.n-slider-handle-wrapper {
301-
transition: left 0.3s;
302-
}
303-
}
301+
// :deep(.n-slider-rail) {
302+
// .n-slider-rail__fill {
303+
// transition: width 0.3s;
304+
// }
305+
// .n-slider-handle-wrapper {
306+
// transition: left 0.3s;
307+
// }
308+
// }
304309
}
305310
.play-data {
306311
display: flex;
@@ -369,6 +374,9 @@ const instantLyrics = computed(() => {
369374
max-width: calc(100% - 100px);
370375
transition: color 0.3s;
371376
}
377+
.n-tag {
378+
margin-left: 8px;
379+
}
372380
.like {
373381
color: var(--primary-hex);
374382
margin-left: 8px;

0 commit comments

Comments
 (0)