From 346fa2c0045e276158c75c90524a376b6a013e62 Mon Sep 17 00:00:00 2001
From: lyswhut <lyswhut@qq.com>
Date: Tue, 28 Apr 2020 13:33:55 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=92=AD=E6=94=BE=E8=AF=A6?=
 =?UTF-8?q?=E6=83=85=E9=A1=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 postcss.config.js                             |   5 +
 publish/changeLog.md                          |   2 +
 src/main/utils/flacMeta.js                    |   1 -
 src/renderer/App.vue                          |  16 +-
 src/renderer/assets/styles/variables.less     |  39 +
 src/renderer/components/core/Aside.vue        |   5 +
 src/renderer/components/core/Icons.vue        |   3 +
 src/renderer/components/core/Player.vue       | 200 +++--
 src/renderer/components/core/PlayerDetail.vue | 706 ++++++++++++++++++
 src/renderer/components/core/Toolbar.vue      |   9 +-
 src/renderer/lang/cns/core/player.json        |   7 +-
 src/renderer/lang/cnt/core/player.json        |   7 +-
 src/renderer/lang/en/core/player.json         |   7 +-
 src/renderer/store/modules/player.js          |   5 +
 src/renderer/utils/index.js                   |   5 +
 15 files changed, 935 insertions(+), 82 deletions(-)
 create mode 100644 src/renderer/components/core/PlayerDetail.vue

diff --git a/postcss.config.js b/postcss.config.js
index 456a2009..260098a9 100644
--- a/postcss.config.js
+++ b/postcss.config.js
@@ -11,9 +11,14 @@ module.exports = {
         'line-height',
         'letter-spacing',
         'padding', 'margin',
+        'padding-left', 'padding-right',
+        'padding-top', 'padding-bottom',
+        'margin-left', 'margin-right',
+        'margin-top', 'margin-bottom',
         'height', 'width',
         'max-width', 'max-height',
         'min-width', 'min-height',
+        'flex', '::-webkit-scrollbar',
         'top', 'left', 'bottom', 'right',
         'border-radius',
       ],
diff --git a/publish/changeLog.md b/publish/changeLog.md
index 08531797..b61ee280 100644
--- a/publish/changeLog.md
+++ b/publish/changeLog.md
@@ -8,6 +8,7 @@
 - 新增虾米音源
 - 新增新皮肤“粉妆玉琢”、“青出于黑”,可去体验下~
 - 新增“超大”、“巨大”窗口尺寸
+- 新增播放详情页(退出详情页可点击右上角退出按钮或者在播放详情页任意地方**鼠标快速右击两次**)
 
 ### 优化
 
@@ -20,6 +21,7 @@
 
 - 下载列表的歌曲下载、播放将随设置中的保存路径改变而改变,不再固定指向其初始位置
 - 移除列表多选框,现在多选需要键盘配合,想要多选前需按下`Shift`或`Ctrl`键然后再鼠标点击想要选中的内容即可触发多选机制,其中`Shift`键用于连续选择,`Ctrl`键用于不连续选择,`Ctrl+a`用于快速全选。例子一:想要选中1-5项,则先按下`Shift`键后,鼠标点击第一项,再点击第五项即可完成选择;例子二:想要选中1项与第3项,则先按下`Ctrl`键后,鼠标点击第一项,再点击第三项即可完成选择;例子三:想要选中当前列表的全部内容,键盘先按下`Ctrl`键不放,然后按`a`键,即可完成选择。用`Shift`或`Ctrl`选择时,鼠标点击未选中的内容会将其选中,点击已选择的内容会将其取消选择,若想全部取消选择,在不按`Shift`或`Alt`键的情况下,随意点击列表里的一项内容即可全部取消选择。(P.S:`Ctrl`键对应Mac OS上的`Command`键)
+- 现在进度条的封面图左击改为打开播放详情页,在列表定位歌曲改为右击
 
 ### 修复
 
diff --git a/src/main/utils/flacMeta.js b/src/main/utils/flacMeta.js
index c9beaf26..9be44505 100644
--- a/src/main/utils/flacMeta.js
+++ b/src/main/utils/flacMeta.js
@@ -19,7 +19,6 @@ const writeMeta = async(filePath, meta, picPath) => {
   }
   if (picPath) {
     const apicData = await fsPromises.readFile(picPath)
-    console.log(apicData)
     let imgSize = getImgSize(apicData)
     let mime_type
     let bitsPerPixel
diff --git a/src/renderer/App.vue b/src/renderer/App.vue
index f4e1107d..19f74dcd 100644
--- a/src/renderer/App.vue
+++ b/src/renderer/App.vue
@@ -1,24 +1,24 @@
 <template lang="pug">
-#container(v-if="isProd && !isNt" :class="theme" @mouseenter="enableIgnoreMouseEvents" @mouseleave="dieableIgnoreMouseEvents")
+#container(v-if="isProd && !isNt" :class="[theme, nd ? 'nd' : '']" @mouseenter="enableIgnoreMouseEvents" @mouseleave="dieableIgnoreMouseEvents")
   core-aside#left
   #right
     core-toolbar#toolbar
     core-view#view
     core-player#player
   core-icons
-  material-pact-modal(v-show="!setting.isAgreePact || globalObj.isShowPact")
-  material-version-modal(v-show="version.showModal")
   material-xm-verify-modal(v-show="globalObj.xm.isShowVerify" :show="globalObj.xm.isShowVerify" :bg-close="false" @close="handleXMVerifyModalClose")
-#container(v-else :class="theme")
+  material-version-modal(v-show="version.showModal")
+  material-pact-modal(v-show="!setting.isAgreePact || globalObj.isShowPact")
+#container(v-else :class="[theme, nd ? 'nd' : '']")
   core-aside#left
   #right
     core-toolbar#toolbar
     core-view#view
     core-player#player
   core-icons
-  material-pact-modal(v-show="!setting.isAgreePact || globalObj.isShowPact")
-  material-version-modal(v-show="version.showModal")
   material-xm-verify-modal(v-show="globalObj.xm.isShowVerify" :show="globalObj.xm.isShowVerify" :bg-close="false" @close="handleXMVerifyModalClose")
+  material-version-modal(v-show="version.showModal")
+  material-pact-modal(v-show="!setting.isAgreePact || globalObj.isShowPact")
 </template>
 
 <script>
@@ -55,6 +55,7 @@ export default {
     }
   },
   computed: {
+    ...mapGetters('player', ['isShowPlayerDetail']),
     ...mapGetters(['setting', 'theme', 'version', 'windowSizeActive']),
     ...mapGetters('list', ['defaultList', 'loveList']),
     ...mapGetters('download', {
@@ -64,6 +65,9 @@ export default {
     ...mapGetters('search', {
       searchHistoryList: 'historyList',
     }),
+    nd() {
+      return this.isShowPlayerDetail
+    },
   },
   created() {
     this.saveSetting = throttle(n => {
diff --git a/src/renderer/assets/styles/variables.less b/src/renderer/assets/styles/variables.less
index 4f0a869f..1bc71284 100644
--- a/src/renderer/assets/styles/variables.less
+++ b/src/renderer/assets/styles/variables.less
@@ -46,14 +46,17 @@
 @color-player-progress-bar1: darken(@color-theme_2, 12%);
 @color-player-progress-bar2: lighten(@color-theme, 12%);
 @color-player-status-text: lighten(@color-theme_2-font, 10%);
+@color-player-detail-play-btn: lighten(@color-theme, 7%);
 
 @color-tab-btn-background: fadeout(lighten(@color-theme, 10%), 80%);
 @color-tab-btn-background-hover: @color-theme_2-hover;
 @color-tab-border-top: fadeout(lighten(@color-theme, 5%), 50%);
 @color-tab-border-bottom: lighten(@color-theme, 5%);
+@color-hideBtn: #3bc2b2;
 @color-minBtn: #85c43b;
 @color-maxBtn: #e7aa36;
 @color-closeBtn: #ea6e4d;
+@color-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-minBtn-hover: fadeout(@color-minBtn, 10%);
 @color-maxBtn-hover: fadeout(@color-maxBtn, 10%);
 @color-closeBtn-hover: fadeout(@color-closeBtn, 10%);
@@ -101,13 +104,16 @@
 @color-green-player-progress-bar1: darken(@color-green-theme_2, 12%);
 @color-green-player-progress-bar2: lighten(@color-green-theme, 12%);
 @color-green-player-status-text: lighten(@color-green-theme_2-font, 10%);
+@color-green-player-detail-play-btn: lighten(@color-green-theme, 7%);
 @color-green-tab-btn-background: fadeout(lighten(@color-green-theme, 10%), 80%);
 @color-green-tab-btn-background-hover: @color-green-theme_2-hover;
 @color-green-tab-border-top: fadeout(lighten(@color-green-theme, 5%), 50%);
 @color-green-tab-border-bottom: lighten(@color-green-theme, 5%);
+@color-green-hideBtn: #3bc2b2;
 @color-green-minBtn: #85c43b;
 @color-green-maxBtn: #e7aa36;
 @color-green-closeBtn: #ea6e4d;
+@color-green-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-green-minBtn-hover: fadeout(@color-green-minBtn, 10%);
 @color-green-maxBtn-hover: fadeout(@color-green-maxBtn, 10%);
 @color-green-closeBtn-hover: fadeout(@color-green-closeBtn, 10%);
@@ -151,13 +157,16 @@
 @color-yellow-player-progress-bar1: darken(@color-yellow-theme_2, 12%);
 @color-yellow-player-progress-bar2: lighten(@color-yellow-theme, 2%);
 @color-yellow-player-status-text: lighten(@color-yellow-theme_2-font, 10%);
+@color-yellow-player-detail-play-btn: lighten(@color-yellow-theme, 7%);
 @color-yellow-tab-btn-background: fadeout(lighten(@color-yellow-theme, 10%), 70%);
 @color-yellow-tab-btn-background-hover: @color-yellow-theme_2-hover;
 @color-yellow-tab-border-top: fadeout(lighten(@color-yellow-theme, 5%), 40%);
 @color-yellow-tab-border-bottom: @color-yellow-theme;
+@color-yellow-hideBtn: #3bc2b2;
 @color-yellow-minBtn: #85c43b;
 @color-yellow-maxBtn: #e7aa36;
 @color-yellow-closeBtn: #ea6e4d;
+@color-yellow-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-yellow-minBtn-hover: fadeout(@color-yellow-minBtn, 10%);
 @color-yellow-maxBtn-hover: fadeout(@color-yellow-maxBtn, 10%);
 @color-yellow-closeBtn-hover: fadeout(@color-yellow-closeBtn, 10%);
@@ -200,13 +209,16 @@
 @color-orange-player-progress-bar1: darken(@color-orange-theme_2, 12%);
 @color-orange-player-progress-bar2: lighten(@color-orange-theme, 12%);
 @color-orange-player-status-text: lighten(@color-orange-theme_2-font, 10%);
+@color-orange-player-detail-play-btn: lighten(@color-orange-theme, 7%);
 @color-orange-tab-btn-background: fadeout(lighten(@color-orange-theme, 10%), 80%);
 @color-orange-tab-btn-background-hover: @color-orange-theme_2-hover;
 @color-orange-tab-border-top: fadeout(lighten(@color-orange-theme, 5%), 50%);
 @color-orange-tab-border-bottom: lighten(@color-orange-theme, 5%);
+@color-orange-hideBtn: #3bc2b2;
 @color-orange-minBtn: #85c43b;
 @color-orange-maxBtn: #e7aa36;
 @color-orange-closeBtn: #ea6e4d;
+@color-orange-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-orange-minBtn-hover: fadeout(@color-orange-minBtn, 10%);
 @color-orange-maxBtn-hover: fadeout(@color-orange-maxBtn, 10%);
 @color-orange-closeBtn-hover: fadeout(@color-orange-closeBtn, 10%);
@@ -249,13 +261,16 @@
 @color-blue-player-progress-bar1: darken(@color-blue-theme_2, 12%);
 @color-blue-player-progress-bar2: lighten(@color-blue-theme, 12%);
 @color-blue-player-status-text: lighten(@color-blue-theme_2-font, 10%);
+@color-blue-player-detail-play-btn: lighten(@color-blue-theme, 7%);
 @color-blue-tab-btn-background: fadeout(lighten(@color-blue-theme, 10%), 80%);
 @color-blue-tab-btn-background-hover: @color-blue-theme_2-hover;
 @color-blue-tab-border-top: fadeout(lighten(@color-blue-theme, 5%), 50%);
 @color-blue-tab-border-bottom: lighten(@color-blue-theme, 5%);
+@color-blue-hideBtn: #3bc2b2;
 @color-blue-minBtn: #85c43b;
 @color-blue-maxBtn: #e7aa36;
 @color-blue-closeBtn: #ea6e4d;
+@color-blue-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-blue-minBtn-hover: fadeout(@color-blue-minBtn, 10%);
 @color-blue-maxBtn-hover: fadeout(@color-blue-maxBtn, 10%);
 @color-blue-closeBtn-hover: fadeout(@color-blue-closeBtn, 10%);
@@ -298,15 +313,18 @@
 @color-red-player-progress-bar1: darken(@color-red-theme_2, 12%);
 @color-red-player-progress-bar2: lighten(@color-red-theme, 12%);
 @color-red-player-status-text: lighten(@color-red-theme_2-font, 10%);
+@color-red-player-detail-play-btn: lighten(@color-red-theme, 7%);
 @color-red-tab-border-top: fadeout(lighten(@color-red-theme, 25%), 70%);
 @color-red-tab-border-bottom: lighten(@color-red-theme, 35%);
 @color-red-tab-btn-background: fadeout(lighten(@color-red-theme, 10%), 80%);
 @color-red-tab-btn-background-hover: @color-red-theme_2-hover;
 @color-red-tab-border-top: fadeout(lighten(@color-red-theme, 5%), 50%);
 @color-red-tab-border-bottom: lighten(@color-red-theme, 5%);
+@color-red-hideBtn: #3bc2b2;
 @color-red-minBtn: #85c43b;
 @color-red-maxBtn: #e7aa36;
 @color-red-closeBtn: #ea6e4d;
+@color-red-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-red-minBtn-hover: fadeout(@color-red-minBtn, 10%);
 @color-red-maxBtn-hover: fadeout(@color-red-maxBtn, 10%);
 @color-red-closeBtn-hover: fadeout(@color-red-closeBtn, 10%);
@@ -349,13 +367,16 @@
 @color-pink-player-progress-bar1: darken(@color-pink-theme_2, 12%);
 @color-pink-player-progress-bar2: lighten(@color-pink-theme, 2%);
 @color-pink-player-status-text: lighten(@color-pink-theme_2-font, 10%);
+@color-pink-player-detail-play-btn: lighten(@color-pink-theme, 7%);
 @color-pink-tab-btn-background: fadeout(lighten(@color-pink-theme, 10%), 70%);
 @color-pink-tab-btn-background-hover: @color-pink-theme_2-hover;
 @color-pink-tab-border-top: fadeout(lighten(@color-pink-theme, 5%), 40%);
 @color-pink-tab-border-bottom: @color-pink-theme;
+@color-pink-hideBtn: #3bc2b2;
 @color-pink-minBtn: #85c43b;
 @color-pink-maxBtn: #e7aa36;
 @color-pink-closeBtn: #ea6e4d;
+@color-pink-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-pink-minBtn-hover: fadeout(@color-pink-minBtn, 10%);
 @color-pink-maxBtn-hover: fadeout(@color-pink-maxBtn, 10%);
 @color-pink-closeBtn-hover: fadeout(@color-pink-closeBtn, 10%);
@@ -398,13 +419,16 @@
 @color-purple-player-progress-bar1: darken(@color-purple-theme_2, 12%);
 @color-purple-player-progress-bar2: lighten(@color-purple-theme, 12%);
 @color-purple-player-status-text: lighten(@color-purple-theme_2-font, 10%);
+@color-purple-player-detail-play-btn: lighten(@color-purple-theme, 7%);
 @color-purple-tab-btn-background: fadeout(lighten(@color-purple-theme, 10%), 80%);
 @color-purple-tab-btn-background-hover: @color-purple-theme_2-hover;
 @color-purple-tab-border-top: fadeout(lighten(@color-purple-theme, 5%), 50%);
 @color-purple-tab-border-bottom: lighten(@color-purple-theme, 5%);
+@color-purple-hideBtn: #3bc2b2;
 @color-purple-minBtn: #85c43b;
 @color-purple-maxBtn: #e7aa36;
 @color-purple-closeBtn: #ea6e4d;
+@color-purple-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-purple-minBtn-hover: fadeout(@color-purple-minBtn, 10%);
 @color-purple-maxBtn-hover: fadeout(@color-purple-maxBtn, 10%);
 @color-purple-closeBtn-hover: fadeout(@color-purple-closeBtn, 10%);
@@ -447,13 +471,16 @@
 @color-grey-player-progress-bar1: darken(@color-grey-theme_2, 12%);
 @color-grey-player-progress-bar2: lighten(@color-grey-theme, 12%);
 @color-grey-player-status-text: lighten(@color-grey-theme_2-font, 10%);
+@color-grey-player-detail-play-btn: lighten(@color-grey-theme, 7%);
 @color-grey-tab-btn-background: fadeout(lighten(@color-grey-theme, 10%), 80%);
 @color-grey-tab-btn-background-hover: @color-grey-theme_2-hover;
 @color-grey-tab-border-top: fadeout(lighten(@color-grey-theme, 5%), 50%);
 @color-grey-tab-border-bottom: lighten(@color-grey-theme, 5%);
+@color-grey-hideBtn: #3bc2b2;
 @color-grey-minBtn: #85c43b;
 @color-grey-maxBtn: #e7aa36;
 @color-grey-closeBtn: #ea6e4d;
+@color-grey-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-grey-minBtn-hover: fadeout(@color-grey-minBtn, 10%);
 @color-grey-maxBtn-hover: fadeout(@color-grey-maxBtn, 10%);
 @color-grey-closeBtn-hover: fadeout(@color-grey-closeBtn, 10%);
@@ -497,15 +524,18 @@
 @color-ming-player-progress-bar1: darken(@color-ming-theme_2, 12%);
 @color-ming-player-progress-bar2: lighten(@color-ming-theme, 12%);
 @color-ming-player-status-text: lighten(@color-ming-theme_2-font, 10%);
+@color-ming-player-detail-play-btn: lighten(@color-ming-theme, 10%);
 @color-ming-tab-border-top: fadeout(lighten(@color-ming-theme, 25%), 70%);
 @color-ming-tab-border-bottom: lighten(@color-ming-theme, 35%);
 @color-ming-tab-btn-background: fadeout(lighten(@color-ming-theme, 10%), 80%);
 @color-ming-tab-btn-background-hover: @color-ming-theme_2-hover;
 @color-ming-tab-border-top: fadeout(lighten(@color-ming-theme, 5%), 50%);
 @color-ming-tab-border-bottom: lighten(@color-ming-theme, 5%);
+@color-ming-hideBtn: #3bc2b2;
 @color-ming-minBtn: #85c43b;
 @color-ming-maxBtn: #e7aa36;
 @color-ming-closeBtn: #ea6e4d;
+@color-ming-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-ming-minBtn-hover: fadeout(@color-ming-minBtn, 10%);
 @color-ming-maxBtn-hover: fadeout(@color-ming-maxBtn, 10%);
 @color-ming-closeBtn-hover: fadeout(@color-ming-closeBtn, 10%);
@@ -548,13 +578,16 @@
 @color-mid_autumn-player-progress-bar1: darken(@color-mid_autumn-theme_2, 12%);
 @color-mid_autumn-player-progress-bar2: lighten(@color-mid_autumn-theme, 12%);
 @color-mid_autumn-player-status-text: lighten(@color-mid_autumn-theme_2-font, 10%);
+@color-mid_autumn-player-detail-play-btn: lighten(@color-mid_autumn-theme, 7%);
 @color-mid_autumn-tab-btn-background: fadeout(lighten(@color-mid_autumn-theme, 10%), 80%);
 @color-mid_autumn-tab-btn-background-hover: @color-mid_autumn-theme_2-hover;
 @color-mid_autumn-tab-border-top: fadeout(lighten(@color-mid_autumn-theme, 5%), 50%);
 @color-mid_autumn-tab-border-bottom: lighten(@color-mid_autumn-theme, 5%);
+@color-mid_autumn-hideBtn: #3bc2b2;
 @color-mid_autumn-minBtn: #85c43b;
 @color-mid_autumn-maxBtn: #e7aa36;
 @color-mid_autumn-closeBtn: #ea6e4d;
+@color-mid_autumn-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-mid_autumn-minBtn-hover: fadeout(@color-mid_autumn-minBtn, 10%);
 @color-mid_autumn-maxBtn-hover: fadeout(@color-mid_autumn-maxBtn, 10%);
 @color-mid_autumn-closeBtn-hover: fadeout(@color-mid_autumn-closeBtn, 10%);
@@ -597,13 +630,16 @@
 @color-naruto-player-progress-bar1: darken(@color-naruto-theme_2, 12%);
 @color-naruto-player-progress-bar2: lighten(@color-naruto-theme, 12%);
 @color-naruto-player-status-text: lighten(@color-naruto-theme_2-font, 10%);
+@color-naruto-player-detail-play-btn: lighten(@color-naruto-theme, 7%);
 @color-naruto-tab-btn-background: fadeout(lighten(@color-naruto-theme, 10%), 80%);
 @color-naruto-tab-btn-background-hover: @color-naruto-theme_2-hover;
 @color-naruto-tab-border-top: fadeout(lighten(@color-naruto-theme, 5%), 50%);
 @color-naruto-tab-border-bottom: lighten(@color-naruto-theme, 5%);
+@color-naruto-hideBtn: #3bc2b2;
 @color-naruto-minBtn: #85c43b;
 @color-naruto-maxBtn: #e7aa36;
 @color-naruto-closeBtn: #ea6e4d;
+@color-naruto-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-naruto-minBtn-hover: fadeout(@color-naruto-minBtn, 10%);
 @color-naruto-maxBtn-hover: fadeout(@color-naruto-maxBtn, 10%);
 @color-naruto-closeBtn-hover: fadeout(@color-naruto-closeBtn, 10%);
@@ -646,13 +682,16 @@
 @color-happy_new_year-player-progress-bar1: darken(@color-happy_new_year-theme_2, 12%);
 @color-happy_new_year-player-progress-bar2: lighten(@color-happy_new_year-theme, 9%);
 @color-happy_new_year-player-status-text: lighten(@color-happy_new_year-theme_2-font, 10%);
+@color-happy_new_year-player-detail-play-btn: lighten(@color-happy_new_year-theme, 7%);
 @color-happy_new_year-tab-btn-background: fadeout(lighten(@color-happy_new_year-theme, 10%), 80%);
 @color-happy_new_year-tab-btn-background-hover: @color-happy_new_year-theme_2-hover;
 @color-happy_new_year-tab-border-top: fadeout(lighten(@color-happy_new_year-theme, 5%), 50%);
 @color-happy_new_year-tab-border-bottom: lighten(@color-happy_new_year-theme, 5%);
+@color-happy_new_year-hideBtn: #64a130;
 @color-happy_new_year-minBtn: #c4943b;
 @color-happy_new_year-maxBtn: #e7aa36;
 @color-happy_new_year-closeBtn: #ea6e4d;
+@color-happy_new_year-hideBtn-hover: fadeout(@color-hideBtn, 10%);
 @color-happy_new_year-minBtn-hover: fadeout(@color-happy_new_year-minBtn, 10%);
 @color-happy_new_year-maxBtn-hover: fadeout(@color-happy_new_year-maxBtn, 10%);
 @color-happy_new_year-closeBtn-hover: fadeout(@color-happy_new_year-closeBtn, 10%);
diff --git a/src/renderer/components/core/Aside.vue b/src/renderer/components/core/Aside.vue
index 07146070..849ce95c 100644
--- a/src/renderer/components/core/Aside.vue
+++ b/src/renderer/components/core/Aside.vue
@@ -66,6 +66,11 @@ export default {
   display: flex;
   flex-flow: column nowrap;
 }
+:global(.nd) {
+  .aside {
+    -webkit-app-region: no-drag;
+  }
+}
 .logo {
   box-sizing: border-box;
   padding: 12% 13%;
diff --git a/src/renderer/components/core/Icons.vue b/src/renderer/components/core/Icons.vue
index 6a8492ef..10ce2f23 100644
--- a/src/renderer/components/core/Icons.vue
+++ b/src/renderer/components/core/Icons.vue
@@ -70,6 +70,9 @@ svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/19
     g#icon-down
       // 0 0 451.847 451.847
       path(fill='currentColor' d='M225.923,354.706c-8.098,0-16.195-3.092-22.369-9.263L9.27,151.157c-12.359-12.359-12.359-32.397,0-44.751c12.354-12.354,32.388-12.354,44.748,0l171.905,171.915l171.906-171.909c12.359-12.354,32.391-12.354,44.744,0c12.365,12.354,12.365,32.392,0,44.751L248.292,345.449C242.115,351.621,234.018,354.706,225.923,354.706z')
+    g#icon-back
+      // 0 0 512 512
+      path(fill='currentColor' d='M511.563,434.259c-1.728-142.329-124.42-258.242-277.087-263.419V95.999c0-17.645-14.342-31.999-31.974-31.999 c-7.931,0-15.591,3.042-21.524,8.562c0,0-134.828,124.829-173.609,163.755C2.623,241.109,0,248.088,0,255.994 c0,7.906,2.623,14.885,7.369,19.687c38.781,38.915,173.609,163.745,173.609,163.745c5.933,5.521,13.593,8.562,21.524,8.562 c17.631,0,31.974-14.354,31.974-31.999v-74.591c153.479,2.156,255.792,50.603,255.792,95.924c0,5.896,4.767,10.666,10.658,10.666 c0.167,0.021,0.333,0.01,0.416,0c5.891,0,10.658-4.771,10.658-10.666C512,436.259,511.854,435.228,511.563,434.259z')
     g#icon-refresh
       // 0 0 512 512
       path(fill='currentColor' d='M440.65 12.57l4 82.77A247.16 247.16 0 0 0 255.83 8C134.73 8 33.91 94.92 12.29 209.82A12 12 0 0 0 24.09 224h49.05a12 12 0 0 0 11.67-9.26 175.91 175.91 0 0 1 317-56.94l-101.46-4.86a12 12 0 0 0-12.57 12v47.41a12 12 0 0 0 12 12H500a12 12 0 0 0 12-12V12a12 12 0 0 0-12-12h-47.37a12 12 0 0 0-11.98 12.57zM255.83 432a175.61 175.61 0 0 1-146-77.8l101.8 4.87a12 12 0 0 0 12.57-12v-47.4a12 12 0 0 0-12-12H12a12 12 0 0 0-12 12V500a12 12 0 0 0 12 12h47.35a12 12 0 0 0 12-12.6l-4.15-82.57A247.17 247.17 0 0 0 255.83 504c121.11 0 221.93-86.92 243.55-201.82a12 12 0 0 0-11.8-14.18h-49.05a12 12 0 0 0-11.67 9.26A175.86 175.86 0 0 1 255.83 432z')
diff --git a/src/renderer/components/core/Player.vue b/src/renderer/components/core/Player.vue
index 50fa8ad7..73af8f76 100644
--- a/src/renderer/components/core/Player.vue
+++ b/src/renderer/components/core/Player.vue
@@ -1,10 +1,10 @@
 <template lang="pug">
 div(:class="$style.player")
-  div(:class="$style.left" @click="handleToMusicLocation")
+  div(:class="$style.left" @contextmenu="handleToMusicLocation" @click="showPlayerDetail")
     img(v-if="musicInfo.img" :src="musicInfo.img" @error="imgError")
     svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' height='100%' viewBox='0 0 60 60' space='preserve')
       use(:xlink:href='`#${$style.iconPic}`')
-  div(:class="$style.right")
+  div(:class="$style.right" v-if="!isShowPlayerDetail")
     div(:class="$style.column1")
       div(:class="$style.container")
         div(:class="$style.title" @click="handleCopy(title)" :title="title + $t('core.player.copy_title')") {{title}}
@@ -25,14 +25,21 @@ div(:class="$style.player")
           svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve')
             use(xlink:href='#icon-play')
     div(:class="$style.column2")
-      div(:class="$style.progress")
+      div(:class="$style.progress" v-if="!isShowPlayerDetail")
         //- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ transform: `scaleX(${progress || 0})` }")
         div(:class="[$style.progressBar, $style.progressBar2, isActiveTransition ? $style.barTransition : '']" @transitionend="handleTransitionEnd" :style="{ transform: `scaleX(${progress || 0})` }")
-      div(:class="$style.progressMask" @click='setProgess' ref="dom_progress")
+      div(:class="$style.progressMask" @click='handleSetProgress' ref="dom_progress")
     div(:class="$style.column3")
       span {{nowPlayTimeStr}}
-      span(:class="$style.statusText") {{status}}
+      span(:class="$style.statusText") {{statusText}}
       span {{maxPlayTimeStr}}
+  //- transition(enter-active-class="animated lightSpeedIn"
+  transition(enter-active-class="animated lightSpeedIn"
+      leave-active-class="animated slideOutDown")
+    core-player-detail(v-if="isShowPlayerDetail" :musicInfo="musicInfo"
+                      :lyric="lyric" :list="list" :listId="listId"
+                      :playInfo="{ nowPlayTimeStr, maxPlayTimeStr, progress, nowPlayTime, status }"
+                      :isPlay="isPlay" @action="handlePlayDetailAction")
   svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' style="display: none;")
     defs
       g(:id="$style.iconPic")
@@ -64,19 +71,21 @@ export default {
       nowPlayTime: 0,
       maxPlayTime: 0,
       isPlay: false,
-      status: '^-^',
+      status: '',
+      statusText: '^-^',
       musicInfo: {
         songmid: null,
         img: null,
         lrc: null,
         url: null,
-        name: '^',
-        singer: '^',
+        name: '',
+        singer: '',
+        album: '',
       },
       targetSong: null,
       pregessWidth: 0,
       lyric: {
-        lrc: null,
+        lines: [],
         text: '',
         line: 0,
       },
@@ -98,14 +107,14 @@ export default {
   },
   computed: {
     ...mapGetters(['setting']),
-    ...mapGetters('player', ['list', 'playIndex', 'changePlay', 'listId']),
+    ...mapGetters('player', ['list', 'playIndex', 'changePlay', 'listId', 'isShowPlayerDetail']),
     // pic() {
     //   return this.musicInfo.img ? this.musicInfo.img : ''
     // },
     title() {
       return this.musicInfo.name
         ? `${this.musicInfo.name} - ${this.musicInfo.singer}`
-        : ''
+        : '^-^'
     },
     nowPlayTimeStr() {
       return this.nowPlayTime ? formatPlayTime2(this.nowPlayTime) : '00:00'
@@ -123,7 +132,7 @@ export default {
   mounted() {
     this.init()
     this.$nextTick(() => {
-      this.setProgessWidth()
+      this.setProgressWidth()
     })
     this.handleSaveVolume = debounce(volume => {
       this.setVolume(volume)
@@ -190,6 +199,7 @@ export default {
       'setPlayIndex',
       'fixPlayIndex',
       'resetChangePlay',
+      'visiblePlayerDetail',
     ]),
     ...mapMutations(['setVolume']),
     ...mapMutations('list', ['updateMusicInfo']),
@@ -205,19 +215,20 @@ export default {
 
       this.audio.addEventListener('playing', () => {
         console.log('开始播放')
-        this.status = this.$t('core.player.playing')
+        this.statusText = this.$t('core.player.playing')
+        this.status = ''
         this.startPlay()
       })
       this.audio.addEventListener('pause', () => {
         console.log('暂停播放')
-        this.lyric.lrc.pause()
+        window.lrc.pause()
         this.stopPlay()
-        // this.status = this.$t('core.player.stop')
+        // this.status = this.statusText = this.$t('core.player.stop')
       })
       this.audio.addEventListener('ended', () => {
         console.log('播放完毕')
         this.stopPlay()
-        this.status = this.$t('core.player.end')
+        this.status = this.statusText = this.$t('core.player.end')
         this.handleNext()
       })
       this.audio.addEventListener('error', () => {
@@ -230,12 +241,12 @@ export default {
           if (!this.audioErrorTime) this.audioErrorTime = this.audio.currentTime // 记录出错的播放时间
           this.retryNum++
           this.setUrl(this.list[this.playIndex], true)
-          this.status = this.$t('core.player.refresh_url')
+          this.status = this.statusText = this.$t('core.player.refresh_url')
           return
         }
 
         this.sendProgressEvent(this.progress, 'error')
-        this.status = this.$t('core.player.error')
+        this.status = this.statusText = this.$t('core.player.error')
         this.addDelayNextTimeout()
       })
       this.audio.addEventListener('loadeddata', () => {
@@ -245,10 +256,10 @@ export default {
           this.audioErrorTime = 0
         }
         if (!this.targetSong.interval && this.listId != 'download') this.updateMusicInfo({ id: 'default', index: this.playIndex, data: { interval: formatPlayTime2(this.maxPlayTime) } })
-        this.status = this.$t('core.player.loading')
+        this.status = this.statusText = this.$t('core.player.loading')
       })
       this.audio.addEventListener('loadstart', () => {
-        this.status = this.$t('core.player.loading')
+        this.status = this.statusText = this.$t('core.player.loading')
       })
       this.audio.addEventListener('canplay', () => {
         console.log('加载完成开始播放')
@@ -260,20 +271,20 @@ export default {
         if (this.mediaBuffer.timeout) {
           this.clearBufferTimeout()
         }
-        // if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000)
-        this.status = this.$t('core.player.loading')
+        // if (this.musicInfo.lrc) window.lrc.play(this.audio.currentTime * 1000)
+        this.status = this.statusText = this.$t('core.player.loading')
       })
       // this.audio.addEventListener('canplaythrough', () => {
       //   console.log('音乐加载完毕')
       //   // if (this.musicInfo.lyric.orgLrc) this.musicInfo.lyric.lrc.play(this.audio.currentTime * 1000)
-      //   this.status = '播放中...'
+      //   this.status = this.statusText = '播放中...'
       // })
       this.audio.addEventListener('emptied', () => {
         this.mediaBuffer.playTime = 0
         this.clearBufferTimeout()
 
         // console.log('媒介资源元素突然为空,网络错误 or 切换歌曲?')
-        // this.status = '媒介资源元素突然为空,网络错误?'
+        // this.status = this.statusText = '媒介资源元素突然为空,网络错误?'
       })
 
       this.audio.addEventListener('timeupdate', () => {
@@ -285,17 +296,22 @@ export default {
         // console.log('缓冲中...')
         this.stopPlay()
         this.startBuffering()
-        this.status = this.$t('core.player.buffering')
+        this.status = this.statusText = this.$t('core.player.buffering')
       })
 
-      this.lyric.lrc = new Lyric({
+      window.lrc = new Lyric({
         onPlay: (line, text) => {
           this.lyric.text = text
           this.lyric.line = line
-          this.status = text
+          this.statusText = text
           // console.log(line, text)
         },
-        offset: 150,
+        onSetLyric: lines => { // listening lyrics seting event
+          // console.log(lines) // lines is array of all lyric text
+          this.lyric.lines = lines
+          this.lyric.line = 0
+        },
+        offset: 100,
       })
     },
     async play() {
@@ -314,6 +330,7 @@ export default {
         this.musicInfo.songmid = targetSong.musicInfo.songmid
         this.musicInfo.singer = targetSong.musicInfo.singer
         this.musicInfo.name = targetSong.musicInfo.name
+        this.musicInfo.album = targetSong.albumName
         this.audio.src = filePath
         // console.log(filePath)
         this.setImg(targetSong.musicInfo)
@@ -322,6 +339,7 @@ export default {
         this.musicInfo.songmid = targetSong.songmid
         this.musicInfo.singer = targetSong.singer
         this.musicInfo.name = targetSong.name
+        this.musicInfo.album = targetSong.albumName
         this.setUrl(targetSong)
         this.setImg(targetSong)
         this.setLrc(targetSong)
@@ -341,7 +359,7 @@ export default {
         this.handleNext()
       }, 5000)
     },
-    async handleNext() {
+    async filterList() {
       // if (this.list.listName === null) return
       let list
       if (this.listId == 'download') {
@@ -353,21 +371,23 @@ export default {
       } else if (this.isAPITemp) {
         list = this.list.filter(s => s.source == 'kw')
       } else {
-        list = [...this.list]
+        list = this.list
       }
+      return list
+    },
+    async handlePrev() {
+      // console.log(playIndex)
+      let list = await this.filterList()
       if (!list.length) return this.setPlayIndex(-1)
       let playIndex = this.list === list ? this.playIndex : list.indexOf(this.list[this.playIndex])
-      // console.log(playIndex)
       let index
       switch (this.setting.player.togglePlayMethod) {
-        case 'listLoop':
-          index = this.hanldeListLoop(list, playIndex)
-          break
         case 'random':
           index = this.hanldeListRandom(list, playIndex)
           break
+        case 'listLoop':
         case 'list':
-          index = this.hanldeListNext(list, playIndex)
+          index = playIndex === 0 ? list.length - 1 : playIndex - 1
           break
         default:
           return
@@ -376,44 +396,62 @@ export default {
       if (this.list !== list) index = this.list.indexOf(list[index])
       this.setPlayIndex(index)
     },
-    hanldeListLoop(list, index) {
-      return index === list.length - 1 ? 0 : index + 1
-    },
-    hanldeListNext(list, index) {
-      return index === list.length - 1 ? -1 : index + 1
+    async handleNext() {
+      // if (this.list.listName === null) return
+      let list = await this.filterList()
+      if (!list.length) return this.setPlayIndex(-1)
+      let playIndex = this.list === list ? this.playIndex : list.indexOf(this.list[this.playIndex])
+      // console.log(playIndex)
+      let index
+      switch (this.setting.player.togglePlayMethod) {
+        case 'listLoop':
+          index = playIndex === list.length - 1 ? 0 : playIndex + 1
+          break
+        case 'random':
+          index = this.hanldeListRandom(list, playIndex)
+          break
+        case 'list':
+          index = playIndex === list.length - 1 ? -1 : playIndex + 1
+          break
+        default:
+          return
+      }
+      if (index < 0) return
+      if (this.list !== list) index = this.list.indexOf(list[index])
+      this.setPlayIndex(index)
     },
     hanldeListRandom(list, index) {
       return getRandom(0, list.length)
     },
     startPlay() {
       this.isPlay = true
-      if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000)
+      if (this.musicInfo.lrc) window.lrc.play(this.audio.currentTime * 1000)
       this.setAppTitle()
       this.sendProgressEvent(this.progress, 'normal')
     },
     stopPlay() {
       this.isPlay = false
-      this.lyric.lrc.pause()
+      window.lrc.pause()
       this.sendProgressEvent(this.progress, 'paused')
       this.clearAppTitle()
     },
-    setProgess(e) {
-      if (!this.audio.src) return
-      // this.isActiveTransition = true
-      this.$nextTick(() => {
-        const time = (e.offsetX / this.pregessWidth) * this.maxPlayTime
-        if (this.audioErrorTime) this.audioErrorTime = time
-        if (this.mediaBuffer.playTime) {
-          this.clearBufferTimeout()
-          this.mediaBuffer.playTime = time
-          this.startBuffering()
-        }
-        this.audio.currentTime = time
-
-        if (!this.isPlay) this.audio.play()
-      })
+    handleSetProgress(event) {
+      this.setProgress(event.offsetX / this.pregessWidth)
     },
-    setProgessWidth() {
+    setProgress(pregress) {
+      if (!this.audio.src) return
+      const time = pregress * this.maxPlayTime
+      if (this.audioErrorTime) this.audioErrorTime = time
+      if (this.mediaBuffer.playTime) {
+        this.clearBufferTimeout()
+        this.mediaBuffer.playTime = time
+        this.startBuffering()
+      }
+      this.audio.currentTime = time
+
+      if (!this.isPlay) this.audio.play()
+    },
+    setProgressWidth() {
       this.pregessWidth = parseInt(
         window.getComputedStyle(this.$refs.dom_progress, null).width,
       )
@@ -445,14 +483,14 @@ export default {
     setUrl(targetSong, isRefresh, isRetryed = false) {
       let type = this.getPlayType(this.setting.player.highQuality, targetSong)
       this.musicInfo.url = targetSong.typeUrl[type]
-      this.status = this.$t('core.player.geting_url')
+      this.status = this.statusText = this.$t('core.player.geting_url')
 
       return this.getUrl({ musicInfo: targetSong, type, isRefresh }).then(() => {
         this.audio.src = this.musicInfo.url = targetSong.typeUrl[type]
       }).catch(err => {
         if (err.message == requestMsg.cancelRequest) return
         if (!isRetryed) return this.setUrl(targetSong, isRefresh, true)
-        this.status = err.message
+        this.status = this.statusText = err.message
         this.addDelayNextTimeout()
         return Promise.reject(err)
       })
@@ -477,25 +515,28 @@ export default {
 
       lrcP
         .then(() => {
-          this.lyric.lrc.setLyric(this.musicInfo.lrc)
-          if (this.isPlay && (this.musicInfo.url || this.listId == 'download')) this.lyric.lrc.play(this.audio.currentTime * 1000)
+          window.lrc.setLyric(this.musicInfo.lrc)
+          if (this.isPlay && (this.musicInfo.url || this.listId == 'download')) window.lrc.play(this.audio.currentTime * 1000)
         })
         .catch(() => {
-          this.status = this.$t('core.player.lyric_error')
+          this.status = this.statusText = this.$t('core.player.lyric_error')
         })
     },
     handleRemoveMusic() {
       this.stopPlay()
       this.audio.src = null
       this.audio.removeAttribute('src')
-      this.status = '^-^'
+      this.statusText = '^-^'
       this.musicInfo.img = null
-      this.musicInfo.name = this.musicInfo.singer = '^'
+      this.status = this.musicInfo.name = this.musicInfo.singer = ''
       this.musicInfo.songmid = null
       this.musicInfo.lrc = null
       this.musicInfo.url = null
       this.nowPlayTime = 0
       this.maxPlayTime = 0
+      this.lyric.lines = []
+      this.lyric.line = 0
+      this.lyric.text = 0
     },
     sendProgressEvent(status, mode) {
       // console.log(status)
@@ -512,7 +553,6 @@ export default {
     },
     handleVolumeMsDown(e) {
       this.volumeEvent.isMsDown = true
-      this.volumeEvent.isMsMoved = false
       this.volumeEvent.msDownX = e.clientX
 
       let val = e.offsetX / 70
@@ -538,7 +578,7 @@ export default {
       clipboardWriteText(text)
     },
     handleResize() {
-      this.setProgessWidth()
+      this.setProgressWidth()
     },
     handleToMusicLocation() {
       if (this.listId == 'download') return
@@ -551,6 +591,9 @@ export default {
         },
       })
     },
+    showPlayerDetail() {
+      this.visiblePlayerDetail(true)
+    },
     handleTransitionEnd(e) {
       // console.log(e)
       this.isActiveTransition = false
@@ -593,6 +636,24 @@ export default {
         this.setMediaDeviceId('default')
       })
     },
+    handlePlayDetailAction({ type, data }) {
+      switch (type) {
+        case 'prev':
+          this.handlePrev()
+          break
+        case 'togglePlay':
+          this.togglePlay()
+          break
+        case 'next':
+          this.handleNext()
+          break
+        case 'progress':
+          this.setProgress(data)
+          break
+        case 'volume':
+          break
+      }
+    },
   },
 }
 </script>
@@ -609,13 +670,14 @@ export default {
   border-top: 2px solid @color-theme;
   box-sizing: border-box;
   display: flex;
-  z-index: 1;
+  z-index: 2;
   * {
     box-sizing: border-box;
   }
 }
 .left {
   width: @height-player - 2;
+  height: @height-player - 2;
   color: @color-theme;
   transition: @transition-theme;
   transition-property: color;
@@ -863,7 +925,6 @@ each(@themes, {
 
     .volume-bar {
       background-color: ~'@{color-@{value}-theme}';
-      box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
     }
 
 
@@ -875,7 +936,6 @@ each(@themes, {
     }
     .progress-bar2 {
       background-color: ~'@{color-@{value}-player-progress-bar2}';
-      box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
     }
 
     .column3 {
diff --git a/src/renderer/components/core/PlayerDetail.vue b/src/renderer/components/core/PlayerDetail.vue
new file mode 100644
index 00000000..aa6106ab
--- /dev/null
+++ b/src/renderer/components/core/PlayerDetail.vue
@@ -0,0 +1,706 @@
+<template lang="pug">
+  div(:class="$style.container" @contextmenu="handleContextMenu")
+    //- div(:class="$style.bg" :style="bgStyle")
+    //- div(:class="$style.bg2")
+    div(:class="$style.header")
+      div(:class="$style.control")
+        button(type="button" :class="$style.hide" :title="$t('core.player.hide_detail')" @click="visiblePlayerDetail(false)")
+        button(type="button" :class="$style.min" :title="$t('core.toolbar.min')" @click="min")
+        //- button(type="button" :class="$style.max" @click="max")
+        button(type="button" :class="$style.close" :title="$t('core.toolbar.close')" @click="close")
+
+    div(:class="$style.main")
+      div(:class="$style.left")
+        div(:class="$style.info")
+          div(:class="$style.img")
+            img(:src="musicInfo.img" v-if="musicInfo.img")
+          div(:class="$style.description")
+            p {{$t('core.player.name')}}{{musicInfo.name}}
+            p {{$t('core.player.singer')}}{{musicInfo.singer}}
+            p(v-if="musicInfo.album") {{$t('core.player.album')}}{{musicInfo.album}}
+        //- div(:class="$style.list")
+          ul
+
+      div(:class="$style.right")
+        div(:class="[$style.lyric, lyricEvent.isMsDown ? $style.draging : null]" @mousedown="handleLyricMouseDown" ref="dom_lyric")
+          div(:class="$style.lyricSpace")
+          p(v-for="(info, index) in lyric.lines" :key="index" :class="lyric.line == index ? $style.lrcActive : null") {{info.text}}
+          div(:class="$style.lyricSpace")
+    div(:class="$style.footer")
+      div(:class="$style.left")
+        div(:class="$style.progressContainer")
+          div(:class="$style.progressContent")
+            div(:class="$style.progress")
+              //- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ transform: `scaleX(${progress || 0})` }")
+              div(:class="[$style.progressBar, $style.progressBar2, isActiveTransition ? $style.barTransition : '']" @transitionend="handleTransitionEnd" :style="{ transform: `scaleX(${playInfo.progress || 0})` }")
+            div(:class="$style.progressMask" @click='setProgress' ref="dom_progress")
+        div(:class="$style.timeLabel")
+          span(style="margin-left: 15px") {{playInfo.status}}
+          div
+            span {{playInfo.nowPlayTimeStr}}
+            span(style="margin: 0 5px;") /
+            span {{playInfo.maxPlayTimeStr}}
+      div(:class="$style.playControl")
+        //- div(:class="$style.playBtn")
+        //- div(:class="$style.playBtn")
+        div(:class="$style.playBtn" @click="$emit('action', { type: 'prev' })" style="transform: rotate(180deg);" :title="$t('core.player.prev')")
+          svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve')
+            use(xlink:href='#icon-nextMusic')
+        div(:class="$style.playBtn" :title="isPlay ? $t('core.player.pause') : $t('core.player.play')" @click="$emit('action', { type: 'togglePlay' })")
+          svg(v-if="isPlay" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve')
+            use(xlink:href='#icon-pause')
+          svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve')
+            use(xlink:href='#icon-play')
+        div(:class="$style.playBtn" @click="$emit('action', { type: 'next' })" :title="$t('core.player.next')")
+          svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve')
+            use(xlink:href='#icon-nextMusic')
+</template>
+
+
+<script>
+import { mapGetters, mapMutations } from 'vuex'
+import { rendererSend } from 'common/ipc'
+import { scrollTo } from '../../utils'
+
+let cancelScrollFn = null
+
+export default {
+  props: {
+    musicInfo: {
+      type: Object,
+      default() {
+        return {
+          songmid: null,
+          img: null,
+          lrc: null,
+          url: null,
+          name: '^',
+          singer: '^',
+        }
+      },
+    },
+    lyric: {
+      type: Object,
+      default() {
+        return {
+          line: 0,
+          text: '',
+          lines: [],
+        }
+      },
+    },
+    playInfo: {
+      type: Object,
+      default() {
+        return {
+          nowPlayTimeStr: '00:00',
+          maxPlayTimeStr: '00:00',
+          progress: 0,
+          nowPlayTime: 0,
+          status: 0,
+        }
+      },
+    },
+    list: {
+      type: Array,
+      default() {
+        return []
+      },
+    },
+    listId: {
+      type: String,
+      default() {
+        return ''
+      },
+    },
+    isPlay: {
+      type: Boolean,
+      default: false,
+    },
+  },
+  watch: {
+    'musicInfo.img': {
+      handler(n) {
+        if (n) {
+          this.bgStyle.backgroundImage = `url(${n})`
+        }
+      },
+      immediate: true,
+    },
+    'lyric.lines': {
+      handler() {
+        this.$nextTick(() => {
+          this.dom_lines = this.$refs.dom_lyric.querySelectorAll('p')
+          this.handleScrollLrc()
+        })
+      },
+      immediate: true,
+    },
+    'lyric.line': {
+      handler(n) {
+        this.handleScrollLrc()
+      },
+      immediate: true,
+    },
+    'playInfo.nowPlayTime'(n, o) {
+      if (Math.abs(n - o) > 2) this.isActiveTransition = true
+    },
+  },
+  data() {
+    return {
+      bgStyle: {
+        backgroundImage: null,
+      },
+      dom_lines: [],
+      isActiveTransition: false,
+      pregessWidth: 0,
+      clickTime: 0,
+      volumeEvent: {
+        isMsDown: false,
+        msDownX: 0,
+        msDownValue: 0,
+      },
+      lyricEvent: {
+        isMsDown: false,
+        msDownY: 0,
+        msDownScrollY: 0,
+        isStopScroll: false,
+        timeout: null,
+      },
+    }
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.setProgressWidth()
+    })
+    document.addEventListener('mousemove', this.handleMouseMsMove)
+    document.addEventListener('mouseup', this.handleMouseMsUp)
+    window.addEventListener('resize', this.handleResize)
+  },
+  beforeDestroy() {
+    this.clearLyricScrollTimeout()
+    document.removeEventListener('mousemove', this.handleMouseMsMove)
+    document.removeEventListener('mouseup', this.handleMouseMsUp)
+    window.removeEventListener('resize', this.handleResize)
+  },
+  computed: {
+    ...mapGetters('player', ['isShowPlayerDetail']),
+  },
+  methods: {
+    ...mapMutations('player', [
+      'visiblePlayerDetail',
+    ]),
+    handleResize() {
+      this.setProgressWidth()
+    },
+    handleScrollLrc() {
+      if (!this.dom_lines.length) return
+      if (cancelScrollFn) {
+        cancelScrollFn()
+        cancelScrollFn = null
+      }
+      if (this.lyricEvent.isStopScroll) return
+      let dom_p = this.dom_lines[this.lyric.line]
+      cancelScrollFn = scrollTo(this.$refs.dom_lyric, dom_p ? (dom_p.offsetTop - this.$refs.dom_lyric.clientHeight * 0.38) : 0)
+    },
+    handleTransitionEnd() {
+      this.isActiveTransition = false
+    },
+    setProgress(event) {
+      this.$emit('action', {
+        type: 'progress',
+        data: event.offsetX / this.pregessWidth,
+      })
+    },
+    setProgressWidth() {
+      this.pregessWidth = parseInt(
+        window.getComputedStyle(this.$refs.dom_progress, null).width,
+      )
+    },
+    handleContextMenu() {
+      if (window.performance.now() - this.clickTime > 400) {
+        this.clickTime = window.performance.now()
+        return
+      }
+      this.clickTime = 0
+      this.visiblePlayerDetail(false)
+    },
+    handleLyricMouseDown(e) {
+      // console.log(e)
+      this.lyricEvent.isMsDown = true
+      this.lyricEvent.msDownY = e.clientY
+      this.lyricEvent.msDownScrollY = this.$refs.dom_lyric.scrollTop
+    },
+    handleMouseMsUp(e) {
+      this.lyricEvent.isMsDown = this.volumeEvent.isMsDown = false
+    },
+    handleMouseMsMove(e) {
+      if (this.lyricEvent.isMsDown) {
+        if (!this.lyricEvent.isStopScroll) this.lyricEvent.isStopScroll = true
+        if (cancelScrollFn) {
+          cancelScrollFn()
+          cancelScrollFn = null
+        }
+        this.$refs.dom_lyric.scrollTop = this.lyricEvent.msDownScrollY + this.lyricEvent.msDownY - e.clientY
+        this.startLyricScrollTimeout()
+      }
+
+      // if (this.volumeEvent.isMsDown) {
+      //   let val = this.volumeEvent.msDownValue + (e.clientX - this.volumeEvent.msDownX) / 70
+      //   this.volume = val < 0 ? 0 : val > 1 ? 1 : val
+      //   if (this.audio) this.audio.volume = this.volume
+      // }
+
+      // console.log(val)
+    },
+    startLyricScrollTimeout() {
+      this.clearLyricScrollTimeout()
+      this.lyricEvent.timeout = setTimeout(() => {
+        this.lyricEvent.timeout = null
+        this.lyricEvent.isStopScroll = false
+        if (!this.isPlay) return
+        this.handleScrollLrc()
+      }, 3000)
+    },
+    clearLyricScrollTimeout() {
+      if (!this.lyricEvent.timeout) return
+      clearTimeout(this.lyricEvent.timeout)
+      this.lyricEvent.timeout = null
+    },
+    min() {
+      rendererSend('min')
+    },
+    max() {
+      rendererSend('max')
+    },
+    close() {
+      rendererSend('close')
+    },
+  },
+}
+</script>
+
+
+<style lang="less" module>
+@import '../../assets/styles/layout.less';
+
+@control-btn-width: @height-toolbar * .5;
+
+.container {
+  position: absolute;
+  display: flex;
+  flex-flow: column nowrap;
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  background-color: #fff;
+  z-index: 10;
+  // -webkit-app-region: drag;
+  overflow: hidden;
+  border-radius: 4px;
+  color: @color-theme_2-font;
+  border-left: 12px solid @color-theme;
+}
+.bg {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  background-size: 110% 110%;
+  filter: blur(60px);
+  z-index: -1;
+}
+.bg2 {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  z-index: -1;
+  background-color: rgba(255, 255, 255, .8);
+}
+
+.header {
+  position: relative;
+  flex: 0 0 @height-toolbar;
+  -webkit-app-region: drag;
+}
+.control {
+  position: absolute;
+  right: 0;
+  top: 0;
+  display: flex;
+  align-items: center;
+  height: @height-toolbar;
+  -webkit-app-region: no-drag;
+  padding: 0 @control-btn-width * 0.6;
+  &:hover {
+    button:before {
+      opacity: 1;
+    }
+  }
+
+  button {
+    position: relative;
+    width: @control-btn-width;
+    height: @control-btn-width;
+    background: none;
+    border: none;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    outline: none;
+    padding: 0;
+    cursor: pointer;
+    + button {
+      margin-left: @control-btn-width * .4;
+    }
+
+    &:after {
+      content: ' ';
+      display: block;
+      border-radius: 50%;
+      width: 14px;
+      height: 14px;
+      transition: background-color 0.2s ease-in-out;
+    }
+
+    &:before {
+      display: block;
+      position: absolute;
+      opacity: 0;
+      transition: opacity @transition-theme;
+    }
+
+    &.hide:after {
+      background-color: @color-hideBtn;
+    }
+    &.min:after {
+      background-color: @color-minBtn;
+    }
+    &.max:after {
+      background-color: @color-maxBtn;
+    }
+    &.close:after {
+      background-color: @color-closeBtn;
+    }
+
+    &.hide:hover:after {
+      background-color: @color-hideBtn-hover;
+    }
+    &.min:hover:after {
+      background-color: @color-minBtn-hover;
+      opacity: 1;
+    }
+    &.max:hover:after {
+      background-color: @color-maxBtn-hover;
+    }
+    &.close:hover:after {
+      background-color: @color-closeBtn-hover;
+    }
+  }
+}
+
+.hide {
+  &:before {
+    content: '∨';
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    font-size: 14px;
+    line-height: 1;
+    color: #fff;
+  }
+}
+
+.min {
+  &:before {
+    content: ' ';
+    width: 8px;
+    height: 2px;
+    left: @control-btn-width / 2 - 4;
+    top: @control-btn-width / 2 - 1;
+    background-color: #fff;
+  }
+}
+
+.close {
+  &:before {
+    content: '×';
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    font-size: 14px;
+    line-height: 1;
+    color: #fff;
+  }
+}
+
+.main {
+  flex: auto;
+  min-height: 0;
+  overflow: hidden;
+  display: flex;
+  padding: 0 30px;
+}
+.left {
+  flex: 0 0 40%;
+  overflow: hidden;
+}
+.info {
+  display: flex;
+  flex-flow: column nowrap;
+  align-items: center;
+}
+.img {
+  width: 300px;
+  height: 300px;
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  -webkit-app-region: drag;
+
+  img {
+    max-width: 100%;
+    max-height: 100%;
+    border: 5px solid @color-theme-hover;
+    // border: 5px solid #fff;
+  }
+}
+.description {
+  width: 300px;
+  padding: 15px 0;
+  overflow: hidden;
+  p {
+    line-height: 1.5;
+    font-size: 14px;
+    overflow-wrap: break-word;
+  }
+}
+
+.right {
+  flex: 0 0 60%;
+  // padding: 0 30px;
+  position: relative;
+  &:before {
+    position: absolute;
+    top: 0;
+    left: 0;
+    content: ' ';
+    height: 100px;
+    width: 100%;
+    background-image: linear-gradient(0deg,rgba(255,255,255,0) 0%,#fff 95%);
+    pointer-events: none;
+  }
+  &:after {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    content: ' ';
+    height: 100px;
+    width: 100%;
+    background-image: linear-gradient(-180deg,rgba(255,255,255,0) 0%,#fff 95%);
+    pointer-events: none;
+  }
+}
+.lyric {
+  text-align: center;
+  height: 100%;
+  overflow: hidden;
+  font-size: 16px;
+  cursor: grab;
+  &.draging {
+    cursor: grabbing;
+  }
+  p {
+    padding: 8px 0;
+    line-height: 1.2;
+    overflow-wrap: break-word;
+    transition: @transition-theme;
+    transition-property: color, font-size;
+  }
+}
+.lyric-space {
+  height: 70%;
+}
+.lrc-active {
+  color: @color-theme;
+  font-size: 1.2em;
+}
+.footer {
+  flex: 0 0 100px;
+  overflow: hidden;
+  display: flex;
+  align-items: center;
+}
+.left {
+  flex: auto;
+  display: flex;
+  flex-flow: column nowrap;
+  align-items: center;
+  padding-top: 13px;
+}
+.progress-container {
+  width: 100%;
+  position: relative;
+  padding: 3px 0;
+}
+.progress-content {
+  position: relative;
+  height: 15px;
+  padding: 5px 0;
+  width: 100%;
+}
+.progress {
+  height: 100%;
+  width: 100%;
+  border-radius: 0.2rem;
+  // overflow: hidden;
+  transition: @transition-theme;
+  transition-property: background-color;
+  background-color: @color-player-progress;
+  // background-color: #f5f5f5;
+  position: relative;
+  border-radius: @radius-progress-border;
+}
+.progress-mask {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  cursor: pointer;
+}
+.progress-bar {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  transform-origin: 0;
+  border-top-right-radius: @radius-progress-border;
+  border-bottom-right-radius: @radius-progress-border;
+}
+.progress-bar1 {
+  background-color: @color-player-progress-bar1;
+}
+.progress-bar2 {
+  background-color: @color-player-progress-bar2;
+  box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
+  border-top-right-radius: 20px;
+  border-bottom-right-radius: 20px;
+}
+
+.bar-transition {
+  transition-property: transform;
+  transition-timing-function: ease-out;
+  transition-duration: 0.2s;
+}
+.time-label {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  span {
+    font-size: 13px;
+  }
+}
+
+.play-control {
+  flex: none;
+  height: 100%;
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  padding: 0 25px;
+  color: @color-btn;
+}
+.play-btn {
+  height: 40%;
+  padding: 5px;
+  cursor: pointer;
+  flex: none;
+  transition: @transition-theme;
+  transition-property: color;
+  color: @color-player-detail-play-btn;
+  transition: opacity 0.1s ease;
+  opacity: 1;
+  cursor: pointer;
+
+  +.play-btn {
+    margin-left: 10px;
+  }
+  svg {
+    fill: currentColor;
+    filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.2));
+  }
+  &:hover {
+    opacity: 0.8;
+  }
+  &:active {
+    opacity: 0.6;
+  }
+}
+
+each(@themes, {
+  :global(#container.@{value}) {
+    .container {
+      border-left-color: ~'@{color-@{value}-theme}';
+    }
+    .control {
+      button {
+        // &.hide:after {
+        //   background-color: ~'@{color-@{value}-hideBtn}';
+        // }
+        &.hide:after {
+          background-color: ~'@{color-@{value}-hideBtn}';
+        }
+        &.min:after {
+          background-color: ~'@{color-@{value}-minBtn}';
+        }
+        &.max:after {
+          background-color: ~'@{color-@{value}-maxBtn}';
+        }
+        &.close:after {
+          background-color: ~'@{color-@{value}-closeBtn}';
+        }
+        
+        &.hide:hover:after {
+          background-color: ~'@{color-@{value}-hideBtn-hover}';
+        }
+        &.min:hover:after {
+          background-color: ~'@{color-@{value}-minBtn-hover}';
+        }
+        &.max:hover:after {
+          background-color: ~'@{color-@{value}-maxBtn-hover}';
+        }
+        &.close:hover:after {
+          background-color: ~'@{color-@{value}-closeBtn-hover}';
+        }
+      }
+    }
+    .img {
+      img {
+        border-color: ~'@{color-@{value}-theme-hover}';
+      }
+    }
+    .lrc-active {
+      color: ~'@{color-@{value}-theme}';
+    }
+    .progress {
+      background-color: ~'@{color-@{value}-player-progress}';
+    }
+    .progress-bar1 {
+      background-color: ~'@{color-@{value}-player-progress-bar1}';
+    }
+    .progress-bar2 {
+      background-color: ~'@{color-@{value}-player-progress-bar2}';
+    }
+    .play-btn {
+      color: ~'@{color-@{value}-player-detail-play-btn}';
+    }
+  }
+})
+
+</style>
diff --git a/src/renderer/components/core/Toolbar.vue b/src/renderer/components/core/Toolbar.vue
index f778c611..a0219eb8 100644
--- a/src/renderer/components/core/Toolbar.vue
+++ b/src/renderer/components/core/Toolbar.vue
@@ -141,6 +141,11 @@ export default {
   z-index: 2;
   position: relative;
 }
+:global(.nd) {
+  .toolbar {
+    -webkit-app-region: no-drag;
+  }
+}
 .input {
   -webkit-app-region: no-drag;
   position: absolute;
@@ -166,7 +171,7 @@ export default {
   align-items: center;
   height: 100%;
   -webkit-app-region: no-drag;
-  padding: 0 @control-btn-width / 2;
+  padding: 0 @control-btn-width * 0.6;
   &:hover {
     button:before {
       opacity: 1;
@@ -186,7 +191,7 @@ export default {
     padding: 0;
     cursor: pointer;
     + button {
-      margin-left: @control-btn-width / 2;
+      margin-left: @control-btn-width * 0.4;
     }
 
     &:after {
diff --git a/src/renderer/lang/cns/core/player.json b/src/renderer/lang/cns/core/player.json
index 86ebfc0d..36936ad5 100644
--- a/src/renderer/lang/cns/core/player.json
+++ b/src/renderer/lang/cns/core/player.json
@@ -3,6 +3,7 @@
   "volume": "当前音量:",
   "pause": "暂停",
   "play": "播放",
+  "prev": "上一首",
   "next": "下一首",
   "playing": "播放中...",
   "stop": "暂停播放",
@@ -12,5 +13,9 @@
   "loading": "音乐加载中...",
   "buffering": "缓冲中...",
   "geting_url": "歌曲链接获取中...",
-  "lyric_error": "歌词获取失败"
+  "lyric_error": "歌词获取失败",
+  "hide_detail": "隐藏详情页",
+  "name": "歌曲名:",
+  "singer": "艺术家:",
+  "album": "专辑名:"
 }
diff --git a/src/renderer/lang/cnt/core/player.json b/src/renderer/lang/cnt/core/player.json
index 0b68ff96..513ea11f 100644
--- a/src/renderer/lang/cnt/core/player.json
+++ b/src/renderer/lang/cnt/core/player.json
@@ -3,6 +3,7 @@
   "volume": "當前音量:",
   "pause": "暫停",
   "play": "播放",
+  "prev": "上一首",
   "next": "下一首",
   "playing": "播放中...",
   "stop": "暫停播放",
@@ -12,5 +13,9 @@
   "loading": "音樂加載中...",
   "buffering": "緩衝中...",
   "geting_url": "歌曲鏈接獲取中...",
-  "lyric_error": "歌詞獲取失敗"
+  "lyric_error": "歌詞獲取失敗",
+  "hide_detail": "隱藏詳情頁",
+  "name": "歌曲名:",
+  "singer": "藝術家:",
+  "album": "專輯名:"
 }
diff --git a/src/renderer/lang/en/core/player.json b/src/renderer/lang/en/core/player.json
index 4a4e883f..dff70093 100644
--- a/src/renderer/lang/en/core/player.json
+++ b/src/renderer/lang/en/core/player.json
@@ -3,6 +3,7 @@
   "volume": "Volume: ",
   "pause": "Pause",
   "play": "Play",
+  "prev": "Prev",
   "next": "Next",
   "playing": "Now playing...",
   "stop": "Paused",
@@ -12,5 +13,9 @@
   "loading": "Music loading...",
   "buffering": "Buffering...",
   "geting_url": "Getting music link...",
-  "lyric_error": "Failed to get lyrics"
+  "lyric_error": "Failed to get lyrics",
+  "hide_detail": "Hide detail page",
+  "name": "Name: ",
+  "singer": "Artist: ",
+  "album": "Album: "
 }
diff --git a/src/renderer/store/modules/player.js b/src/renderer/store/modules/player.js
index f3c27621..2af51c1e 100644
--- a/src/renderer/store/modules/player.js
+++ b/src/renderer/store/modules/player.js
@@ -6,6 +6,7 @@ const state = {
   listId: null,
   playIndex: -1,
   changePlay: false,
+  isShowPlayerDetail: false,
 }
 
 let urlRequest
@@ -18,6 +19,7 @@ const getters = {
   listId: state => state.listId,
   changePlay: satte => satte.changePlay,
   playIndex: state => state.playIndex,
+  isShowPlayerDetail: state => state.isShowPlayerDetail,
 }
 
 // actions
@@ -87,6 +89,9 @@ const mutations = {
   resetChangePlay(state) {
     state.changePlay = false
   },
+  visiblePlayerDetail(state, visible) {
+    state.isShowPlayerDetail = visible
+  },
 }
 
 export default {
diff --git a/src/renderer/utils/index.js b/src/renderer/utils/index.js
index 17bbfa6b..4c6f677d 100644
--- a/src/renderer/utils/index.js
+++ b/src/renderer/utils/index.js
@@ -65,6 +65,7 @@ const easeInOutQuad = (t, b, c, d) => {
 export const scrollTo = (element, to, duration = 300, fn = () => {}) => {
   if (!element) return
   const start = element.scrollTop || element.scrollY || 0
+  let cancel = false
   if (to > start) {
     let maxScrollTop = element.scrollHeight - element.clientHeight
     if (to > maxScrollTop) to = maxScrollTop
@@ -87,12 +88,16 @@ export const scrollTo = (element, to, duration = 300, fn = () => {}) => {
       element.scrollTop = val
     }
     if (currentTime < duration) {
+      if (cancel) return fn()
       setTimeout(animateScroll, increment)
     } else {
       fn()
     }
   }
   animateScroll()
+  return () => {
+    cancel = true
+  }
 }
 
 /**