forked from miao-moe/QZMusic_PC
Compare commits
2 Commits
pre-v2
...
v2-touch-o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cf94fd08b | ||
|
|
38edf2fcd2 |
19
README.md
19
README.md
@@ -3,6 +3,18 @@
|
||||
一款简洁美观的跨平台音乐播放器,提供优雅的桌面音乐体验
|
||||
安卓版本请访问:[官方网站](https://music.qz.shiqianjiang.cn)
|
||||
|
||||
## ⚠️ 项目状态
|
||||
|
||||
> 🔔 **重要提示**:
|
||||
> 本项目**正在积极开发维护中**,目前仍处于早期阶段,可能存在以下问题:
|
||||
> - 部分功能尚未完善或稳定
|
||||
> - 可能存在 Bug 和兼容性问
|
||||
> - UI/UX 可能在后续版本中调整
|
||||
>
|
||||
> 如果您在使用过程中遇到任何问题,欢迎提交 [Issue](https://github.com/miao-moe/QZMusic_PC/issues) 反馈,我们会尽快处理!
|
||||
>
|
||||
> 感谢您的理解与支持 🙏
|
||||
|
||||
## 🛠 技术架构
|
||||
|
||||
| 技术 | 说明 |
|
||||
@@ -24,13 +36,14 @@
|
||||
> - AMLL 提供功能接口,不关联具体音乐数据源
|
||||
|
||||
## 🛠 开发者(排名不分先后)
|
||||
- (以下内容实时更新,更新日期:2026.1.21)
|
||||
- -蜻蜓T-T ([B站](https://space.bilibili.com/3546554124209112) [Github](https://github.com/lqtmcstudio)) | 插件系统 音频系统 UI设计/开发 后端
|
||||
- (以下内容实时更新,更新日期:2026.5.23)
|
||||
- 蜻蜓T-T ([B站](https://space.bilibili.com/3546554124209112) [Github](https://github.com/lqtmcstudio)) | 插件系统 音频系统 UI设计/开发 后端
|
||||
- 时迁酱 ([个人主页](https://shiqianjiang.cn)) 登录/鉴权系统
|
||||
- miao-moe ([GitHub](https://github.com/miao-moe) [博客](https://miao-moe.cn)) | 触控适配优化 UI/UX 改进
|
||||
|
||||
## 📄 开源协议
|
||||
|
||||
本项目采用 [**AGPL v3**](https://github.com/lqtmcstudio/QZMusic_PC/blob/master/LICENSE) 开源协议。
|
||||
本项目采用 [**AGPL v3**](https://github.com/miao-moe/QZMusic_PC/blob/master/LICENSE) 开源协议。
|
||||
|
||||
## 使用工具
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@import 'variables.css';
|
||||
@import 'touch.css';
|
||||
|
||||
* , *::before, *::after {
|
||||
margin: 0;
|
||||
|
||||
109
src/renderer/src/styles/touch.css
Normal file
109
src/renderer/src/styles/touch.css
Normal file
@@ -0,0 +1,109 @@
|
||||
/* 触控适配样式 */
|
||||
|
||||
/* 基础触控优化 */
|
||||
* {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
/* 按钮触控尺寸优化 */
|
||||
button,
|
||||
.nav-item,
|
||||
.playlist-card,
|
||||
.artist-card,
|
||||
.song-item,
|
||||
.more-btn,
|
||||
.play-btn {
|
||||
min-height: 44px;
|
||||
min-width: 44px;
|
||||
}
|
||||
|
||||
/* 触控时的视觉反馈 */
|
||||
.touch-feedback {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.touch-feedback::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.touch-feedback:active::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 滚动优化 */
|
||||
.page-content,
|
||||
.sidebar {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
/* 滚动条触控优化 - 更宽的滚动条便于触控 */
|
||||
.page-content::-webkit-scrollbar,
|
||||
.sidebar::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
/* 交互元素触控优化 */
|
||||
.nav-item,
|
||||
.playlist-card,
|
||||
.artist-card,
|
||||
.song-item {
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
/* 输入框触控优化 */
|
||||
input,
|
||||
textarea {
|
||||
font-size: 16px; /* 防止 iOS 缩放 */
|
||||
}
|
||||
|
||||
/* 播放控制按钮触控优化 */
|
||||
.media-button {
|
||||
min-width: 48px;
|
||||
min-height: 48px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/* 响应式触摸友好的间距 */
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
/* 触控设备的优化 */
|
||||
.content-wrapper {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 14px 16px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.playlist-grid,
|
||||
.artist-grid {
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.song-item {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* 显示覆盖层,不需要悬停也能看到 */
|
||||
.play-overlay {
|
||||
opacity: 0.7 !important;
|
||||
}
|
||||
|
||||
/* 触控时的点击反馈 */
|
||||
.playlist-card:active,
|
||||
.artist-card:active,
|
||||
.song-item:active,
|
||||
.nav-item:active {
|
||||
transform: scale(0.98);
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="content-wrapper">
|
||||
<!-- 每日推荐横幅 -->
|
||||
<div class="daily-recommend">
|
||||
<div class="banner-content">
|
||||
<div class="banner-content" @click="openDailyRecommend">
|
||||
<div class="date-badge">
|
||||
<div class="day">{{ currentDate.day }}</div>
|
||||
<div class="month">{{ currentDate.month }}月</div>
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="banner-info">
|
||||
<h2 class="banner-title">每日推荐</h2>
|
||||
<p class="banner-desc">根据你的音乐口味,为你精选30首歌曲</p>
|
||||
<button class="play-btn">
|
||||
<button class="play-btn" @click.stop="playDailyRecommend">
|
||||
<Icon icon="lucide:play" class="play-icon" />
|
||||
立即播放
|
||||
</button>
|
||||
@@ -26,16 +26,20 @@
|
||||
<button class="more-btn">更多</button>
|
||||
</div>
|
||||
<div class="playlist-grid">
|
||||
<div class="playlist-card" v-for="i in 6" :key="i">
|
||||
<div class="playlist-cover">
|
||||
<div class="cover-gradient"></div>
|
||||
<div
|
||||
class="playlist-card"
|
||||
v-for="playlist in playlists"
|
||||
:key="playlist.id"
|
||||
@click="openPlaylist(playlist)"
|
||||
>
|
||||
<div class="playlist-cover" :style="{ background: playlist.gradient }">
|
||||
<div class="play-overlay">
|
||||
<Icon icon="lucide:play" class="overlay-icon" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="playlist-info">
|
||||
<h4 class="playlist-name">精选歌单 {{ i }}</h4>
|
||||
<p class="playlist-desc">30首歌曲</p>
|
||||
<h4 class="playlist-name">{{ playlist.name }}</h4>
|
||||
<p class="playlist-desc">{{ playlist.songCount }}首歌曲</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -48,11 +52,15 @@
|
||||
<button class="more-btn">更多</button>
|
||||
</div>
|
||||
<div class="artist-grid">
|
||||
<div class="artist-card" v-for="i in 8" :key="i">
|
||||
<div class="artist-avatar">
|
||||
<div class="avatar-gradient"></div>
|
||||
<div
|
||||
class="artist-card"
|
||||
v-for="artist in artists"
|
||||
:key="artist.id"
|
||||
@click="openArtist(artist)"
|
||||
>
|
||||
<div class="artist-avatar" :style="{ background: artist.gradient }">
|
||||
</div>
|
||||
<p class="artist-name">歌手 {{ i }}</p>
|
||||
<p class="artist-name">{{ artist.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -61,19 +69,23 @@
|
||||
<div class="section">
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">新歌速递</h3>
|
||||
<button class="more-btn">播放全部</button>
|
||||
<button class="more-btn" @click="playAllNewSongs">播放全部</button>
|
||||
</div>
|
||||
<div class="song-list">
|
||||
<div class="song-item" v-for="i in 10" :key="i">
|
||||
<div class="song-index">{{ i }}</div>
|
||||
<div class="song-cover">
|
||||
<div class="cover-gradient"></div>
|
||||
<div
|
||||
class="song-item"
|
||||
v-for="(song, index) in newSongs"
|
||||
:key="song.id"
|
||||
@click="playSong(song)"
|
||||
>
|
||||
<div class="song-index">{{ index + 1 }}</div>
|
||||
<div class="song-cover" :style="{ background: song.gradient }">
|
||||
</div>
|
||||
<div class="song-info">
|
||||
<h4 class="song-title">歌曲名称 {{ i }}</h4>
|
||||
<p class="song-artist">歌手名称</p>
|
||||
<h4 class="song-title">{{ song.title }}</h4>
|
||||
<p class="song-artist">{{ song.artist }}</p>
|
||||
</div>
|
||||
<div class="song-duration">03:45</div>
|
||||
<div class="song-duration">{{ song.duration }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -82,8 +94,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const currentDate = computed(() => {
|
||||
const now = new Date();
|
||||
@@ -92,6 +107,66 @@ const currentDate = computed(() => {
|
||||
month: now.getMonth() + 1
|
||||
};
|
||||
});
|
||||
|
||||
// 模拟数据
|
||||
const playlists = ref([
|
||||
{ id: 1, name: '华语流行精选', songCount: 50, gradient: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' },
|
||||
{ id: 2, name: '欧美金曲榜', songCount: 45, gradient: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' },
|
||||
{ id: 3, name: '轻音乐助眠', songCount: 30, gradient: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)' },
|
||||
{ id: 4, name: '怀旧经典老歌', songCount: 60, gradient: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)' },
|
||||
{ id: 5, name: '电子舞曲', songCount: 40, gradient: 'linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%)' },
|
||||
{ id: 6, name: '民谣小调', songCount: 35, gradient: 'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)' }
|
||||
]);
|
||||
|
||||
const artists = ref([
|
||||
{ id: 1, name: '周杰伦', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' },
|
||||
{ id: 2, name: '林俊杰', gradient: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' },
|
||||
{ id: 3, name: '邓紫棋', gradient: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' },
|
||||
{ id: 4, name: '陈奕迅', gradient: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)' },
|
||||
{ id: 5, name: 'Taylor Swift', gradient: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)' },
|
||||
{ id: 6, name: 'Ed Sheeran', gradient: 'linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%)' },
|
||||
{ id: 7, name: '薛之谦', gradient: 'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)' },
|
||||
{ id: 8, name: '李荣浩', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }
|
||||
]);
|
||||
|
||||
const newSongs = ref([
|
||||
{ id: 1, title: '稻香', artist: '周杰伦', duration: '03:45', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' },
|
||||
{ id: 2, title: '晴天', artist: '周杰伦', duration: '04:29', gradient: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' },
|
||||
{ id: 3, title: '夜曲', artist: '周杰伦', duration: '03:58', gradient: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' },
|
||||
{ id: 4, title: '江南', artist: '林俊杰', duration: '04:06', gradient: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)' },
|
||||
{ id: 5, title: '光年之外', artist: '邓紫棋', duration: '03:55', gradient: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)' },
|
||||
{ id: 6, title: '十年', artist: '陈奕迅', duration: '03:25', gradient: 'linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%)' },
|
||||
{ id: 7, title: '演员', artist: '薛之谦', duration: '04:16', gradient: 'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)' },
|
||||
{ id: 8, title: '李白', artist: '李荣浩', duration: '03:43', gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' },
|
||||
{ id: 9, title: '七里香', artist: '周杰伦', duration: '04:58', gradient: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' },
|
||||
{ id: 10, title: '可惜没如果', artist: '林俊杰', duration: '04:52', gradient: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' }
|
||||
]);
|
||||
|
||||
// 交互函数
|
||||
const openDailyRecommend = () => {
|
||||
router.push('/playlist');
|
||||
};
|
||||
|
||||
const playDailyRecommend = () => {
|
||||
console.log('播放每日推荐');
|
||||
};
|
||||
|
||||
const openPlaylist = (playlist: any) => {
|
||||
console.log('打开歌单:', playlist);
|
||||
router.push('/playlist');
|
||||
};
|
||||
|
||||
const openArtist = (artist: any) => {
|
||||
console.log('打开歌手:', artist);
|
||||
};
|
||||
|
||||
const playSong = (song: any) => {
|
||||
console.log('播放歌曲:', song);
|
||||
};
|
||||
|
||||
const playAllNewSongs = () => {
|
||||
console.log('播放全部新歌');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -124,6 +199,7 @@ const currentDate = computed(() => {
|
||||
box-shadow: var(--shadow-lg);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.banner-content::before {
|
||||
@@ -263,12 +339,6 @@ const currentDate = computed(() => {
|
||||
background: var(--color-bg-tertiary);
|
||||
}
|
||||
|
||||
.cover-gradient {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||
}
|
||||
|
||||
.play-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -341,12 +411,6 @@ const currentDate = computed(() => {
|
||||
background: var(--color-bg-tertiary);
|
||||
}
|
||||
|
||||
.avatar-gradient {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
}
|
||||
|
||||
.artist-name {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-primary);
|
||||
|
||||
Reference in New Issue
Block a user