Compare commits

..
200 Commits
Author SHA1 Message Date
lyswhut 5c32160a58 发布0.8.1版本 2019-10-20 10:11:36 +08:00
lyswhut 2fc81c03ec 修复酷我源搜索歌曲结果处理问题 2019-10-20 02:48:28 +08:00
lyswhut cc485df42f 移除192k试听 2019-10-20 00:32:36 +08:00
lyswhut d575dd24ca 发布0.8.0版本 2019-10-19 12:08:53 +08:00
lyswhut e47586cea3 更新请求头 2019-10-19 12:04:15 +08:00
lyswhut 1ce63c7550 修复酷狗歌曲无封面时报错的问题 2019-10-19 11:48:19 +08:00
lyswhut 36d69da3b7 缓存歌单详情列表数据 2019-10-19 11:47:15 +08:00
lyswhut 529be08420 优化音量条交互视觉效果 2019-10-18 21:47:10 +08:00
lyswhut ffe11c7015 更新与分离常见问题 2019-10-18 18:04:28 +08:00
lyswhut 76b796bde7 修复歌单地址无效时无限重复请求的bug 2019-10-18 17:46:33 +08:00
lyswhut f81715f07f 更新歌单导入描述 2019-10-18 13:32:01 +08:00
lyswhut ec3f667b75 修复音量调整Bug,新增拖动调整音量 2019-10-18 12:03:01 +08:00
lyswhut 87f0b4fd17 修复传参问题 2019-10-17 22:41:24 +08:00
lyswhut c7e95c5166 新增各平台通过输入歌单链接或歌单ID打开歌单详情列表 2019-10-17 17:01:49 +08:00
lyswhut 6ea0837cce 新增腾讯、网易云歌单链接打开 2019-10-17 14:05:29 +08:00
lyswhut 59627e048f 分离搜索框业务逻辑,优化搜索框搜索体验 2019-10-17 10:58:09 +08:00
lyswhut 45fea5726b 修复歌曲音质不存在时仍然播放或下载的Bug 2019-10-16 21:26:59 +08:00
lyswhut e539f29f6d 修复音质过滤导致的歌单无法显示问题 2019-10-16 21:00:46 +08:00
lyswhut 1d2810e98f 新增网易云源歌单 2019-10-16 19:54:13 +08:00
lyswhut 1cbafc9750 新增网易云源歌曲搜索 2019-10-16 10:38:42 +08:00
lyswhut e675f7ac06 更新描述 2019-10-16 00:59:19 +08:00
lyswhut 9dc9d3b1b7 修复网易云128k直接试听 2019-10-16 00:40:36 +08:00
lyswhut a104ce688e 修复默认列表没有创建时无法显示收藏列表的Bug 2019-10-13 09:20:44 +08:00
lyswhut 2dd1ceb0b9 更新依赖到最新 2019-10-11 22:52:27 +08:00
lyswhut 9379328d96 修复QQ源歌单无法翻页Bug 2019-10-10 18:43:04 +08:00
lyswhut d8969c327c 改变请求头 2019-10-10 13:24:14 +08:00
lyswhut c3624fb7d6 Create LICENSE 2019-10-08 10:20:46 +08:00
lyswhut b2de230100 remove license file 2019-10-08 10:19:46 +08:00
lyswhut 9d0bf50931 发布0.7.0版本 2019-10-07 22:22:32 +08:00
lyswhut 843e453b21 新增我的音乐列表歌曲源显示 2019-10-07 22:17:13 +08:00
lyswhut f7546b0090 新增咪咕源搜索、歌单、排行榜 2019-10-07 21:51:16 +08:00
lyswhut ed12aac7d8 新增QQ音乐源搜索及其128k试听 2019-10-07 13:07:16 +08:00
lyswhut 15f4053241 修复试听音乐时试听列表id传递错误的Bug 2019-10-07 01:35:20 +08:00
lyswhut 7a334f0099 新增缓存清理功能 2019-10-06 02:14:05 +08:00
lyswhut 4a510393ab 更新依赖到最新 2019-10-05 19:35:44 +08:00
lyswhut 52b0638258 修复strMediaMid获取错误 2019-10-05 16:10:40 +08:00
lyswhut b198b1945d 尝试优化选我的音乐列表内容很多时多选的卡顿问题 2019-10-04 18:04:01 +08:00
lyswhut 10784e95bb 优化选择框动画效果 2019-10-04 10:13:26 +08:00
lyswhut f4c65516d0 修复对多列表滚动条位置恢复的支持 2019-10-03 18:42:26 +08:00
lyswhut 6498305ec8 把我喜欢列表改名成我的收藏 2019-10-03 18:14:59 +08:00
lyswhut 7a91e6efef 完成我喜欢播放列表 2019-10-03 18:11:21 +08:00
lyswhut 4204b62f8f 重构列表功能,使其支持多列表,新增我喜欢列表 2019-10-03 02:00:31 +08:00
lyswhut 70b718d372 发布0.6.2版本 2019-10-01 16:36:44 +08:00
lyswhut 2b5d6f3592 移除static全局路径 2019-10-01 13:04:02 +08:00
lyswhut 8e48d5ba68 解除酷狗封面、歌词对接口服务器的依赖 2019-10-01 12:06:18 +08:00
lyswhut 3cec62d1a9 Merge branch 'openKg' into dev 2019-10-01 10:40:19 +08:00
lyswhut b1650bcbee Merge branch 'master' into dev 2019-09-30 20:21:57 +08:00
lyswhut 492e3d8965 恢复酷狗其他音质播放 2019-09-30 20:20:28 +08:00
lyswhut 6d7be78b70 开放腾讯源128k音源直接播放 2019-09-30 15:24:00 +08:00
lyswhut bac9439f8e 新增QQ音乐源搜索 2019-09-30 14:49:27 +08:00
lyswhut 5b612bfa0f 修复百度源搜索结果为空时的接口处理Bug 2019-09-30 13:14:55 +08:00
lyswhut 1f77b3dd4b 新增QQ音乐源歌单 2019-09-30 13:04:35 +08:00
lyswhut b0d7a53d15 Update issue templates 2019-09-29 17:09:36 +08:00
lyswhut c029eccdd3 修复当试听列表为空时,无法切到其他界面的Bug 2019-09-29 15:48:47 +08:00
lyswhut 7236f8929c 修正火影皮肤名字 2019-09-28 17:17:35 +08:00
lyswhut aa114f86e3 发布0.6.1版本 2019-09-28 10:45:26 +08:00
lyswhut 95db349973 移除酷我音源 ape 音质 2019-09-27 11:51:20 +08:00
lyswhut 2acef590b4 更新常见问题内容 2019-09-27 10:55:56 +08:00
lyswhut 29855b2b6e 修复请求超时的逻辑处理Bug 2019-09-27 10:53:24 +08:00
lyswhut ea10dd68a3 更新常见问题 2019-09-26 13:34:07 +08:00
lyswhut e1942c66ac 更新依赖到最新 2019-09-26 00:07:13 +08:00
lyswhut 6bd18e8078 优化更新弹窗 2019-09-26 00:06:02 +08:00
lyswhut f3dd306f54 优化播放组件渲染性能 2019-09-25 13:05:30 +08:00
lyswhut 3cf39ea48b 优化列表展开动画 2019-09-25 09:02:12 +08:00
lyswhut d57f212c62 分离背景位置控制 2019-09-25 00:57:12 +08:00
lyswhut b273c52124 移除192k音质 2019-09-24 13:34:14 +08:00
lyswhut ce0a9afc33 调整皮肤tablist颜色 2019-09-24 13:27:05 +08:00
lyswhut 8437fe1716 新增 《海贼王》皮肤 2019-09-23 22:12:14 +08:00
lyswhut f63f645696 新增DNS缓存功能 2019-09-23 13:24:32 +08:00
lyswhut f7267806df 新增列表滚动条位置恢复 2019-09-22 11:33:37 +08:00
lyswhut bebf1906a2 修正fetch单词的拼写错误 2019-09-22 10:08:37 +08:00
lyswhut 58e61e0bda 发布0.6.0版本 2019-09-21 21:09:15 +08:00
lyswhut 42aa62cff0 调整失败更新提示 2019-09-21 21:07:36 +08:00
lyswhut bcf7b3fccb 修改更新失败提示 2019-09-21 21:04:45 +08:00
lyswhut ff09378c6d 去重id 2019-09-21 20:53:03 +08:00
lyswhut 99b0595e50 新增搜索页面接口限制 2019-09-21 20:34:40 +08:00
lyswhut 5321469c1d 完成聚合搜索功能 2019-09-21 20:05:56 +08:00
lyswhut ef9eb83a75 完善聚合搜索逻辑 2019-09-21 13:41:19 +08:00
lyswhut c12bbc7f0f 重构搜索页面逻辑 2019-09-20 22:09:18 +08:00
lyswhut a16c4c3657 更新依赖到最新 2019-09-19 22:30:15 +08:00
lyswhut c8240448c6 内置百度源歌曲图片、歌词获取 2019-09-17 21:36:46 +08:00
lyswhut d19569131c 将网络代理密码框设置为password类型 2019-09-16 21:50:41 +08:00
lyswhut 37eec4c971 更换接口地址 2019-09-16 14:08:29 +08:00
lyswhut 65fe679ee2 修复切换歌单列表详情时的UI Bug 2019-09-16 13:31:06 +08:00
lyswhut a16b825c3b 修复代理bug 2019-09-16 00:24:30 +08:00
lyswhut b9aedc9165 新增代理功能 2019-09-16 00:00:07 +08:00
lyswhut 8bdbbc562c 修复试听列表没有歌曲时会显示列表加载中的Bug 2019-09-14 11:00:42 +08:00
lyswhut 696f6c096c 优化从《梦里嫦娥》皮肤切换到其他皮肤时侧栏动画的切换效果 2019-09-13 14:51:28 +08:00
lyswhut d0ef6aef1c 发布0.5.5版本 2019-09-13 14:07:39 +08:00
lyswhut 7204b51439 新增:《月里嫦娥》皮肤 2019-09-13 14:04:52 +08:00
lyswhut 6075a9d22f 更新常见问题 2019-09-12 16:24:28 +08:00
lyswhut b15cfa1892 新增复制播放标题的功能 2019-09-12 01:35:19 +08:00
lyswhut 955375f423 添加mac退出快捷键 2019-09-11 21:43:30 +08:00
lyswhut 2aa13a499a 修复mac不能复制粘贴的问题 2019-09-11 20:50:34 +08:00
lyswhut fb0e61f3b0 修复等待事件未清空的bug 2019-09-10 21:49:09 +08:00
lyswhut 16149fdd38 修复:文件名包含特殊符号时导致下载失败的Bug 2019-09-10 13:00:55 +08:00
lyswhut a432080141 修复 MAC 系统下软件关闭时再次从 dock 打开时报错的Bug 2019-09-10 09:03:57 +08:00
lyswhut 73072daa46 尝试修复Mac系统点击关闭时再打开的BUG 2019-09-10 02:03:09 +08:00
lyswhut 2f57345bd2 更新更新错误时的弹窗下载地址 2019-09-09 09:49:27 +08:00
lyswhut 06075d561d 降级node-downloader-helper包 2019-09-09 08:32:15 +08:00
lyswhut db8ad7cde5 发布0.5.4版本 2019-09-09 08:29:04 +08:00
lyswhut cacf39da69 暂时移除FLAC格式的meta信息修改 2019-09-09 08:26:18 +08:00
lyswhut 4dc7937321 修复更新弹窗可能导致的bug 2019-09-09 01:32:14 +08:00
lyswhut 1c462ce152 更新依赖包到最新,发布0.5.3版本 2019-09-09 01:05:30 +08:00
lyswhut 2014d2495e 发布0.5.2版本 2019-09-09 00:40:27 +08:00
lyswhut dc5493f39f 更新免责声明 2019-09-09 00:40:00 +08:00
lyswhut 67ff5e5a4a 优化列表加载体验,移除失效源 2019-09-09 00:33:24 +08:00
lyswhut 22d56d31a9 更新关于软件说明 2019-09-08 01:59:43 +08:00
lyswhut 785427674a 修复下拉列表间隙 2019-09-08 00:41:45 +08:00
lyswhut 581f6c6e1c 更新常见问题说明 2019-09-07 20:03:08 +08:00
lyswhut 7d0be85e84 新增下拉按钮图标 2019-09-07 19:14:47 +08:00
lyswhut 04e75ab540 新增常见问题说明 2019-09-07 13:09:05 +08:00
lyswhut c117e33d24 新增强迫症设置 2019-09-07 12:46:47 +08:00
lyswhut cafdad6f9f 修复下载管理的一些Bug 2019-09-07 10:43:17 +08:00
lyswhut 05e212682c 更新选项名称 2019-09-06 13:26:11 +08:00
lyswhut 3f876202c1 尝试修复Mac系统报错问题 2019-09-06 11:47:39 +08:00
lyswhut 57c75799a9 发布0.5.1版本 2019-09-05 22:43:11 +08:00
lyswhut 5cbf2d8047 新增下载列表定位文件功能 2019-09-05 22:41:10 +08:00
lyswhut 3dd9ad9ead 新增右上角最小化/关闭按钮鼠标滑过符号 2019-09-05 21:36:00 +08:00
lyswhut 864bef483f 修复更新弹窗无法弹出的问题 2019-09-05 13:54:26 +08:00
lyswhut 9ea9fefe1d 修复百度源歌单全部分类无法加载的问题 2019-09-05 13:34:41 +08:00
lyswhut 0b953a1517 修复打包部署顺序Bug 2019-09-05 01:54:47 +08:00
lyswhut 80db293aca 发布0.5.0版本 2019-09-05 01:48:11 +08:00
lyswhut 809be41e64 新增:封面嵌入、歌词下载;修复Bug 2019-09-05 01:41:36 +08:00
lyswhut 8a6ee68e58 优化更新检查失败文字提示 2019-09-04 15:09:46 +08:00
lyswhut da764721d9 新增单例应用功能 2019-09-04 13:08:32 +08:00
lyswhut 0586639e07 略微整理更新超时机制 2019-09-04 11:28:54 +08:00
lyswhut 0aeab00289 优化歌单列表动画 2019-09-04 09:10:06 +08:00
lyswhut 9564097f46 修复歌单无法翻页Bug 2019-09-04 08:58:29 +08:00
lyswhut d4750f4c6e 添加标签背景动画 2019-09-04 01:24:43 +08:00
lyswhut f1cc2fe72e 缓存electron-builder数据 2019-09-04 00:44:19 +08:00
lyswhut 6d90539e89 调换打包顺序 2019-09-04 00:41:54 +08:00
lyswhut 21f5ed6afd 发布0.4.0版本 2019-09-04 00:32:24 +08:00
lyswhut 4cbbf2ac81 新增版本更新超时功能 2019-09-04 00:31:34 +08:00
lyswhut a035a2525a 完成歌单功能 2019-09-04 00:03:29 +08:00
lyswhut 9727601752 切换接口地址 2019-09-03 17:02:31 +08:00
lyswhut 9e9053c0eb 完善歌单功能 2019-09-03 02:49:48 +08:00
lyswhut 507d495f3f 修复http post请求封装的bug 2019-09-02 01:36:03 +08:00
lyswhut a2f7547cdb 修复歌单接口bug,完善歌单页面 2019-09-02 00:57:57 +08:00
lyswhut d48308d913 完成歌曲列表分离 2019-09-01 19:24:38 +08:00
lyswhut 78c0badd95 重构歌曲列表 2019-09-01 18:09:54 +08:00
lyswhut c81c33a430 发布0.3.5版本 2019-08-30 13:11:02 +08:00
lyswhut 1786376a9e 优化请求,修复加载bug 2019-08-29 13:41:44 +08:00
lyswhut aa973ea984 Update README.md
update
2019-08-29 08:37:18 +08:00
lyswhut 67aeeb0b2f Update README.md
更新readme
2019-08-29 08:36:21 +08:00
lyswhut ee2b01673b 发布0.3.3版本 2019-08-29 00:58:34 +08:00
lyswhut c392f7ee8d 修复接口失效问题 2019-08-29 00:53:05 +08:00
lyswhut 4adc950e29 新增百度源接口封装 2019-08-27 23:25:34 +08:00
lyswhut 78db5b55ec 统一调用方法 2019-08-27 23:25:18 +08:00
lyswhut ad40fdcb55 新增酷狗歌单接口 2019-08-26 14:29:52 +08:00
lyswhut c5ca510a75 新增TODO列表 2019-08-26 11:11:26 +08:00
lyswhut 13c64a95fa 修复手动定位播放进度条时存在偏差的问题 2019-08-25 00:52:47 +08:00
lyswhut 5d74a78bb7 更新readme 2019-08-25 00:38:08 +08:00
lyswhut d638bae7c4 优化音量条的视觉效果 2019-08-25 00:23:47 +08:00
lyswhut 9faf8ed55e 新增酷狗排行榜其他音质下载 2019-08-24 21:07:12 +08:00
lyswhut 3aa23b0f9d 发布0.3.2版本 2019-08-24 19:36:34 +08:00
lyswhut 063bbc9f06 新增酷狗排行榜其他音质下载 2019-08-24 19:36:00 +08:00
lyswhut 16bb23b8a7 修复音量条主题适配 2019-08-24 19:00:22 +08:00
lyswhut a26bef6317 发布v0.3.0版本 2019-08-24 16:19:46 +08:00
lyswhut 21c41d5af1 新增音量调整 2019-08-24 16:18:04 +08:00
lyswhut b270096591 优化更新弹窗功能,新增非安装版弹窗提醒 2019-08-24 14:24:07 +08:00
lyswhut be689f088d 更新readme 2019-08-24 10:31:53 +08:00
lyswhut 2410409342 更新readme 2019-08-23 20:42:05 +08:00
lyswhut a24d682747 重命名appveyor文件 2019-08-23 20:12:36 +08:00
lyswhut 2ada44a2fc 修复取消任务栏进度条可能导致的bug 2019-08-23 20:10:09 +08:00
lyswhut d6c7fb8dcc 新增MAC、Linux构建并兼容Linux 2019-08-23 19:28:54 +08:00
lyswhut bb40a8612a 去除切换到临时接口功能 2019-08-22 23:57:10 +08:00
lyswhut 1182a6eba4 删除重复部分 2019-08-22 23:52:00 +08:00
lyswhut 0d303889ef 修正readme文字无法加粗的问题 2019-08-22 23:26:17 +08:00
lyswhut 2b3aab2faf 更新到v0.2.3版本 2019-08-22 23:02:59 +08:00
lyswhut 893d20b4e9 使用临时接口时,试听列表中的下载按钮仍然能点击的Bug 2019-08-22 00:42:42 +08:00
lyswhut 32e421356f 移除专辑栏隐藏设置 2019-08-22 00:29:32 +08:00
lyswhut 9858170e61 发布0.2.2版本 2019-08-21 22:52:34 +08:00
lyswhut 16711e33e8 更新描述文本 2019-08-21 20:07:40 +08:00
lyswhut f534e11acb 修复下载过程中出错重试5次都失败后不会自动开始下一个任务的Bug 2019-08-21 13:18:59 +08:00
lyswhut 2ff0f4b102 发布0.2.1版本 2019-08-20 21:28:06 +08:00
lyswhut 5b2a44e3bd 添加发布页面链接 2019-08-20 17:24:02 +08:00
lyswhut 0b06206d34 优化readme中的图片 2019-08-20 17:12:23 +08:00
lyswhut cb93dfa218 完善readme 2019-08-20 16:57:43 +08:00
lyswhut c92517960e 发布0.2.0版本 2019-08-20 13:35:59 +08:00
lyswhut 3a615a4a87 新增百度音源 2019-08-20 13:18:11 +08:00
lyswhut 78d2541c14 新增网易云、酷狗排行榜音乐直接试听下载 2019-08-20 01:19:01 +08:00
lyswhut b8d07b365b 修复更新弹窗历史版本描述多余的换行问题 2019-08-19 21:52:56 +08:00
lyswhut 2d5848db94 发布0.1.6版本 2019-08-19 19:10:06 +08:00
lyswhut d4c88edb8b 修复列表多选音源限制Bug 2019-08-19 19:08:19 +08:00
lyswhut 90288da36d 更新change日志,发布0.1.5版本 2019-08-19 18:21:22 +08:00
lyswhut 601e1a67a1 新增列表多选操作功能 2019-08-19 18:19:39 +08:00
lyswhut 8817318a53 整理http请求 2019-08-18 22:48:04 +08:00
lyswhut 153836ea01 更新徽章 2019-08-18 22:35:17 +08:00
lyswhut 24c88b828c 修正许可证字段内容 2019-08-18 21:41:17 +08:00
lyswhut a580cedcb1 更新到0.1.4版本 2019-08-18 20:36:17 +08:00
lyswhut ba8991a034 重构CheckBox组件 2019-08-18 15:18:05 +08:00
lyswhut 6e07719c2c 原接口已挂,暂时切换到临时接口 2019-08-17 21:43:53 +08:00
lyswhut 214745ae87 新增win32应用构建 2019-08-17 14:45:59 +08:00
lyswhut 90ed66f0f4 修复安装包许可协议乱码问题 2019-08-17 10:41:14 +08:00
lyswhut f736a60018 添加更新日志链接 2019-08-17 03:30:49 +08:00
lyswhut f4d340f4b1 修复更新弹窗的内容显示问题 2019-08-17 02:57:35 +08:00
lyswhut bccadcf13d 新增:直接播放QQ音乐排行榜音乐 2019-08-17 02:22:43 +08:00
lyswhut 43bc2b6b90 修正与更新软件说明 2019-08-17 00:13:10 +08:00
lyswhut 14f7bd10a3 初始提交 2019-08-16 17:27:23 +08:00
178 changed files with 32630 additions and 2 deletions
+21
View File
@@ -0,0 +1,21 @@
platform:
- x64
cache:
- node_modules
- '%APPDATA%\npm-cache'
- '%LOCALAPPDATA%\electron\Cache'
- '%LOCALAPPDATA%\electron-builder\Cache'
install:
- ps: Install-Product node 12 x64
- npm install
build_script:
- npm run publish:gh
test: off
branches:
only:
- master
+22
View File
@@ -0,0 +1,22 @@
{
"presets": [
[
"@babel/preset-env",
{
"corejs": "3",
"useBuiltIns": "usage"
}
],
[
"minify",
{
"builtIns": false,
"evaluate": false,
"mangle": false
}
]
],
"plugins": [
"@babel/plugin-syntax-dynamic-import"
]
}
+9
View File
@@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
+24
View File
@@ -0,0 +1,24 @@
{
"extends": "standard",
"plugins": [
"html"
],
"parser": "babel-eslint",
"rules": {
"no-new": "off",
"camelcase": "off",
"no-return-assign": "off",
"space-before-function-paren": ["error", "never"],
"no-var": "error",
"no-fallthrough": "off",
"prefer-promise-reject-errors": "off",
"eqeqeq": "off",
"no-multiple-empty-lines": [1, {"max": 2}],
"comma-dangle": [2, "always-multiline"],
"standard/no-callback-literal": "off",
"prefer-const": "off"
},
"settings": {
"html/html-extensions": [".html", ".vue"]
}
}
+17
View File
@@ -0,0 +1,17 @@
---
name: 功能请求
about: 为这个项目提出一个想法
title: 例如:添加xxx功能、优化xxx功能
labels: ''
assignees: ''
---
**描述您想要的解决方案**
简洁明了地描述您要发生的事情。
**描述您考虑过的替代方案**
对您考虑过的所有替代解决方案或功能的简洁明了的描述。
**其他内容**
在此处添加有关功能请求的任何其他上下文或屏幕截图。
+31
View File
@@ -0,0 +1,31 @@
---
name: 报告Bug
about: 创建报告以帮助我们改进
title: 例如:音乐无法播放
labels: ''
assignees: ''
---
**描述错误**
清楚简洁地说明错误是什么。
**重现**
重现行为的步骤:
1.转到“ ...”
2.点击“ ....”
3.向下滚动到“ ....”
4.看到错误
**预期行为**
对您期望发生的事情的简洁明了的描述。
**截图**
如果适用,请添加屏幕截图以帮助解释您的问题。
**环境:**
  -操作系统及版本:[例如:Windows 10 64位 18362.156]
  -软件安装包及版本:[例如:Windows 64位绿色版 1.0.0]
**其他内容**
在此处添加有关该问题的任何其他上下文。
+73
View File
@@ -0,0 +1,73 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
node_modules.bak*/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
build
dist
publish/assets
publish/utils/githubToken.js
src/**/*-internal.js
+43
View File
@@ -0,0 +1,43 @@
matrix:
include:
- os: osx
osx_image: xcode10.2
language: node_js
node_js: "12"
env:
- ELECTRON_CACHE=$HOME/.cache/electron
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
- os: linux
language: node_js
node_js: "12"
dist: trusty
services: docker
language: generic
cache:
directories:
- node_modules
- $HOME/.cache/electron
- $HOME/.cache/electron-builder
- $HOME/.npm/_prebuilds
notifications:
email: false
script:
- |
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
npm install && npm run publish:gh:linux
else
npm run publish:gh:mac
fi
before_cache:
- rm -rf $HOME/.cache/electron-builder/wine
# only run this script on pull requests and merges into
# the 'master' and 'prod' branches
branches:
only:
- master
+393
View File
@@ -0,0 +1,393 @@
# lx-music-desktop change log
All notable changes to this project will be documented in this file.
Project versioning adheres to [Semantic Versioning](http://semver.org/).
Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
## [0.8.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.8.0...v0.8.1) - 2019-10-20
#### 修复
- 修复酷我源搜索歌曲结果未添加128k音质导致播放128k音质时显示“该歌曲没有可播放的音频”的问题
## [0.8.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.7.0...v0.8.0) - 2019-10-19
#### 新增
- 新增网易云源歌曲搜索
- 新增网易云源歌单
- 新增各平台通过输入歌单链接或歌单ID打开歌单详情列表,目前只适配了**网页版歌单链接**,其他方式的歌单链接可能无法解析,但你可想办法获取歌单ID后输入打开。注:各平台歌单ID均为纯数字,若遇到链接里存在歌单ID但无法解析的歌单链接,可以到GitHub提交issue或发送邮件或加群830125506反馈!
- 新增音量调整滑动功能,现在支持鼠标左右拖动调整音量了
#### 优化
- 优化搜索框搜索体验
- 优化音量条交互视觉效果
- 缓存歌单详情列表数据
#### 修复
- 修复QQ源歌单无法翻页Bug
- 修复默认列表没有创建时无法显示收藏列表的Bug
- 修复网易云128k直接试听
- 修复歌曲音质不存在时仍然播放或下载的Bug
- 修复调整音量时,调整的位置与鼠标点击的位置不一致的问题
## [0.7.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.6.2...v0.7.0) - 2019-10-07
#### 新增
- 新增“我的收藏”本地播放列表
- 新增缓存清理功能,可到**设置-其他**查看与清理软件缓存
- 新增QQ音乐源搜索
- 新增咪咕源搜索
- 新增咪咕源歌单
- 新增咪咕源排行榜
- 新增我的音乐列表歌曲源显示,默认关闭,可到**设置-列表设置**开启
#### 优化
- 优化选择框动画效果
- 尝试优化选我的音乐列表内容很多时多选的卡顿问题
#### 修复
- 修复列表延迟显示的Bug
- 修复QQ音源128k音质试听
## [0.6.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.6.1...v0.6.2) - 2019-10-01
祝贺祖国成立70周年~!
#### 新增
- 新增QQ音乐源歌单
#### 修复
- 修正火影皮肤名字
- 修复当试听列表为空时,无法切到其他界面的Bug
- 修复百度源搜索结果为空时的接口处理Bug
- 恢复**酷狗**其他音质播放
## [0.6.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.6.0...v0.6.1) - 2019-09-28
### 新增
- 新增试听列表**滚动条位置恢复**设置(可自动恢复到上次离开时的列表滚动位置),本功能默认开启,若不需要可到设置-列表设置将其关闭
- 新增 **《海贼王》** 皮肤,喜欢个性化的可以试试~
### 优化
- 新增DNS解析缓存,加快请求速度
- 优化代码逻辑,减少软件对系统资源的占用
- 优化新版本信息检测,尽量减少弹出版本获取失败弹窗弹出的概率
- 优化下拉列表动画效果
### 修复
- 修复请求超时的逻辑处理Bug,尝试修复请求无法取消导致的正在播放的歌曲与界面显示的信息不一致的问题
- 修复其他一些小Bug
### 移除
- 移除 `192k` 音质
- 移除酷我音源 `ape` 音质,无损推荐 `flac` 格式
## [0.6.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.5.5...v0.6.0) - 2019-09-21
### 新增
- 新增音乐**聚合搜索**,目前支持酷我、酷狗、百度源搜索
- 新增代理功能
### 优化
- 优化从《梦里嫦娥》皮肤切换到其他皮肤时侧栏动画的切换效果
### 修复
- 修复试听列表没有歌曲时会显示列表加载中的Bug
- 修复切换歌单列表详情时的UI Bug
## [0.5.5](https://github.com/lyswhut/lx-music-desktop/compare/v0.5.4...v0.5.5) - 2019-09-13
### 新增
- 月是故乡明,祝大家中秋快乐🥮~~新增个性皮肤 **《月里嫦娥》**,时间仓促,皮肤还不是很完善,可以试试喜不喜欢~😉
- 新增 MAC 版本退出快捷键支持
- 新增点击播放器中的歌曲标题可以复制标题的功能(遇到好听的歌曲方便分享)
### 修复
- 修复 MAC 系统下软件关闭时再次从 dock 打开时报错的Bug
- 修复下载的歌曲文件名中包含命名规则不允许的符号时下载失败的问题(若歌曲名包含这些符号会自动将其移除)
- 修复 MAC 版本不能复制粘贴的问题
## [0.5.4](https://github.com/lyswhut/lx-music-desktop/compare/v0.5.3...v0.5.4) - 2019-09-09
### 移除
- 下载的FLAC文件在修改歌曲信息后,软件无法播放,但使用本地播放器可以播放
- 为了稳妥起见,暂时移除FLAC格式的meta信息修改
- MP3格式无此问题
## [0.5.3](https://github.com/lyswhut/lx-music-desktop/compare/v0.5.2...v0.5.3) - 2019-09-09
### 优化
- 更新所有依赖包到最新
### 修复
- 修复试听酷狗源的音乐仍然获取320k音质导致获取失败的Bug
## [0.5.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.5.1...v0.5.2) - 2019-09-09
### 新增
- 新增强迫症设置-离开搜索界面时是否清空搜索框
- 设置-关于板块新增常见问题链接
- 歌单左上角的分类按钮添加一个**向下图标**,方便识别该按钮为下拉框(该按钮可选择歌单类型,请自行尝试)
### 优化
- 略微优化最小化按钮字符
- 优化试听列表的加载体验,当歌曲数过多时列表将延迟加载
### 修复
- 修复下载管理的一些Bug
### 移除
- 因接口失效,移除网易云音源,酷狗音源仅支持播放128k音质
## [0.5.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.5.0...v0.5.1) - 2019-09-05
### 新增
- 新增右上角最小化/关闭按钮鼠标滑过符号
- 新增下载列表定位文件按钮
### 修复
- 修复百度源歌单全部分类无法加载的问题
- 修复更新弹窗无法弹出的问题
## [0.5.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.4.0...v0.5.0) - 2019-09-05
### 新增
- 新增**封面嵌入**(默认开启,可到设置-下载设置关闭)
- 新增**歌词下载**(默认关闭,可到设置-下载设置开启)
- 新增单例应用功能(实现软件单开功能,禁止软件多开)
### 优化
- 优化歌单列表动画
### 修复
- 修复歌单无法翻页的问题
- 修复在某些情况下,添加下载歌曲导致下载列表崩溃的问题
- 修复版本更新弹窗Bug
- 修复酷狗歌单推荐歌单出现在其他分类中的Bug
## [0.4.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.5...v0.4.0) - 2019-09-04
### 新增
- 新增**歌单**功能,目前支持酷我、酷狗、百度源歌单
- 在设置界面-关于洛雪音乐说明部分新增**最新版网盘下载地址**与**打赏地址**
- 新增酷狗 电音热歌榜、DJ热歌榜
- 新增版本更新超时功能,对于部分无法访问GitHub的用户做更新超时提醒
### 移除
- **注意**:0.4.0以前的版本即将失效,请更新到0.4.0版本
## [0.3.5](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.4...v0.3.5) - 2019-08-30
### 新增
- 新增**测试接口**,该接口同样速度较慢,但软件的大部分功能可用,**请自行切换到该接口**,找接口辛苦,且用且珍惜!
### 优化
- 取消需要刷新URL时windows任务栏进度显示错误状态(现显示为暂停状态)
### 修复
- 修复使用临时接口时在试听列表双击灰色歌曲仍然会进行播放的Bug
- 修复歌词加载Bug
## [0.3.4](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.3...v0.3.4) - 2019-08-29
### 优化
- 减少接口不稳定带来的影响,适当增加请求等待时间
### 修复
- 修复播放过程中URL过期不会刷新URL的问题
## [0.3.3](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.2...v0.3.3) - 2019-08-29
### 修复
- **messoer**的接口已经关闭,暂时切换到临时接口使用,部分功能受限。。。
- 修复设置界面更新出错时仍然显示更新下载中的问题
- 修复手动定位播放进度条时存在偏差的问题
- 屏蔽播放器中没有歌曲时对进度条的点击
## [0.3.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.1...v0.3.2) - 2019-08-24
### 新增
- 新增酷狗排行榜其他音质下载
## [0.3.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.0...v0.3.1) - 2019-08-24
### 修复
- 修复音量条主题适配
## [0.3.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.2.3...v0.3.0) - 2019-08-24
### 新增
- 新增**MAC**及**Linux**版本(需要的可自行下载)
- 新增音量调整
- 新增任务栏播放进度条控制选项(现在可在设置界面关闭在任务栏显示的播放进度)
- 新增更新出错时的弹窗提示
- 从该版本起,非安装版也会有更新弹窗提醒了,但仍然需要手动下载新版本更新,版本信息可到设置页面查看
### 修复
- 强制把临时接口设置回 `messoer` 接口
## [0.2.3](https://github.com/lyswhut/lx-music-desktop/compare/v0.2.2...v0.2.3) - 2019-08-22
### 新增
- 新增任务栏程序标题改变功能(播放歌曲时任务栏标题将显示当前播放的歌曲)
### 修复
- 使用临时接口时,试听列表中的下载按钮仍然能点击的Bug
- 修复某些情况下歌曲链接未能缓存的问题
### 移除
- 移除临时接口(因服务器被攻击,本接口已关闭)
- 移除列表栏设置的隐藏专辑栏选项(感觉这个设置并没有什么luan用,并且还会打破布局)
## [0.2.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.2.1...v0.2.2) - 2019-08-21
### 修复
- 修复下载过程中出错重试5次都失败后不会自动开始下一个任务的Bug
- 修复播放到一半URL过期时不会刷新URL直接播放下一首的问题
## [0.2.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.2.0...v0.2.1) - 2019-08-20
### 优化
- 新增歌曲URL存储,当URL无效时才重新获取,以减少接口不稳定的影响
### 修复
- 修复歌曲加载无法加载时自动切换混乱的Bug
- 修复移除列表最后一首歌曲时播放器不停止播放的问题
## [0.2.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.6...v0.2.0) - 2019-08-20
### 新增
- 新增**百度音乐**排行榜及其音乐直接试听与下载
- 新增网易云排行榜音乐直接试听与下载(目前仅支持128k音质)
- 新增酷狗排行榜音乐直接试听与下载(目前仅支持128k音质)
### 修复
- 修复更新弹窗历史版本描述多余的换行问题
- 修复歌曲无法播放的情况下歌词仍会播放的问题
## [0.1.6](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.5...v0.1.6) - 2019-08-19
### 修复
- 修复列表多选音源限制Bug
## [0.1.5](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.4...v0.1.5) - 2019-08-19
### 新增
- 新增搜索列表批量试听与下载功能
- 新增排行榜列表批量试听与下载功能
- 新增试听列表批量移除与下载功能
- 新增下载列表批量开始、暂停与移除功能
### 优化
- 优化歌曲切换机制
## [0.1.4](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.3...v0.1.4) - 2019-08-18
### 新增
- 新增音乐来源切换,可到设置页面-基本设置 look look !
- 为搜索结果列表添加多选功能。
P.S:暂时没想好多选后的操作按钮放哪...
### 优化
- 重构与改进checkbox组件,使其支持不定选中状态
- 完善上一个版本的http请求封装并切换部分请求到该方法上
- 优化其他一些细节
## [0.1.3](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.2...v0.1.3) - 2019-08-17
### 新增
- 新增win32应用构建
### 修复
- 修复安装包许可协议乱码问题
- **messoer 提供的接口已挂**,暂时切换到临时接口!
### 移除
- 由于messoer接口无法使用,QQ音乐排行榜直接播放/下载功能暂时关闭
## [0.1.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.1...v0.1.2) - 2019-08-17
### 修复
- 修复更新弹窗的内容显示问题
## [0.1.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.1.0...v0.1.1) - 2019-08-17
### 新增
- QQ音乐排行榜直接试听与下载(该接口貌似不太稳定,且用且珍惜!)
### 优化
- 优化http请求机制
- 更新关于本软件说明
### 修复
- 修复当上一个歌曲链接正在获取时切换歌曲请求不会取消的问题
- 修复切换歌曲时仍然播放上一首歌曲的问题
## [0.1.0] - 2019-8-16
* 0.1.0版本发布
+60
View File
@@ -0,0 +1,60 @@
# lx-music-desktop 常见问题
在阅读本常见问题后,仍然无法解决你的问题,请提交issue或者加企鹅群`830125506`反馈,反馈时请**注明**已阅读常见问题!
## 软件为什么没有桌面歌词与自定义列表功能
洛雪音乐的最初定位不是作为播放器开发的,它主要用于**查找歌曲**,软件的播放功能仅用于试听,不建议用作为常用播放器使用,因此无桌面、界面歌词,不可自定义列表。
## 歌曲无法试听与下载
该问题解决顺序如下:
1. 尝试更新到最新版本
2. 尝试切换其他歌曲(或直接搜索该歌曲),若全部歌曲都无法试听与下载则进行下一步
3. 尝试到设置-接口来源切换到其他接口
4. 尝试切换网络,比如用手机开热点(目前存在某些网络无法访问接口服务器的情况)
5. 若还不行请到这个链接查看详情:<https://github.com/lyswhut/lx-music-desktop/issues/5>
6. 若没有在第5条链接中的第一条评论中看到接口无法使用的说明,则应该是你网络无法访问接口服务器的问题,如果接口有问题我会在那里说明。
想要知道是不是自己网络的问题可以看看`https://ts.tempmusic.tk`能不能在浏览器打开,浏览器显示404是正常的,如果不是404那就证明所在网络无法访问接口服务器。
若网页无法打开或打来不是404,则应该是DNS的问题,可以尝试以下办法:
1. 改成自动获取试试
2. 手动把DNS改一下,不要用360的DNS,可以把DNS改成`114.114.114.114`、`8.8.8.8`
3. 软件设置里面的代理不要勾起来
## 软件安装包说明
软件发布页及网盘中有多个类型的安装文件,以下是对这些类型文件的说明:
文件名带 `win_` 的是在Windows系统上运行的版本,<br>
其中安装版(Setup)可自动更新软件,<br>
绿色版(green)为免安装版,自动更新功能不可用;
以 **`.dmg`** 结尾的文件为 MAC 版本;
以 **`.AppImage`**、**`.deb`** 结尾的为 Linux 版本。
带有`x64`的为64位的系统版本,带`x86`的为32位的系统版本;若两个都带有的则为集合版,安装时会自动根据系统位数选择对应的版本安装。
## 软件更新
软件启动时若发现新版本时会自动从本仓库下载安装包,下载完毕会弹窗提示更新。<br>
若下载未完成时软件被关闭,下次启动软件会再次自动下载。<br>
若还是**更新失败**,可能是无法访问GitHub导致的,这时需要手动更新,即下载最新安装包直接覆盖安装即可。<br>
注意:**绿色版**的软件自动更新功能**不可用**,建议使用安装版!!<br>
注意:**Mac版**、**Linux deb**版不支持自动更新!
## Windows 7 下界面异常
当 win7 没有开启**透明效果**时界面将会显示异常,开启方法请自行百度。<br>
对于一些开启透明效果后仍然无法正常显示界面的系统,我也不知道是什么原因导致的,只能说正常的系统是没有这个问题的,如果你知道原因导致的欢迎反馈!
## 安装版安装失败,提示应用未安装
对于部分电脑出现安装失败的问题我也不懂什么原因,,可以尝试清理下安装文件,或者重启电脑试试。
## 缺少`xxx.dll`
这个是电脑缺少某些dll导致的,正常的系统是没有这个问题的,解决办法需自行百度弹出的错误信息看下别人是怎么解决的。
+89 -2
View File
@@ -1,2 +1,89 @@
# lx-music-desktop
洛雪音乐助手桌面版
<p align="center"><a href="https://github.com/lyswhut/lx-music-desktop"><img width="200" src="https://github.com/lyswhut/lx-music-desktop/blob/master/doc/images/icon.png" alt="lx-music logo"></a></p>
<p align="center">
<a href="https://github.com/lyswhut/lx-music-desktop/releases"><img src="https://img.shields.io/github/release/lyswhut/lx-music-desktop" alt="Release version"></a>
<a href="https://ci.appveyor.com/project/lyswhut/lx-music-desktop"><img src="https://ci.appveyor.com/api/projects/status/flrsqd5ymp8fnte5?svg=true" alt="Build status"></a>
<a href="https://travis-ci.org/lyswhut/lx-music-desktop"><img src="https://travis-ci.org/lyswhut/lx-music-desktop.svg?branch=master" alt="Build status"></a>
<!-- <a href="https://github.com/lyswhut/lx-music-desktop/releases"><img src="https://img.shields.io/github/downloads/lyswhut/lx-music-desktop/latest/total" alt="Downloads"></a> -->
<a href="https://github.com/lyswhut/lx-music-desktop/tree/dev"><img src="https://img.shields.io/github/package-json/v/lyswhut/lx-music-desktop/dev" alt="Dev branch version"></a>
<!-- <a href="https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE"><img src="https://img.shields.io/github/license/lyswhut/lx-music-desktop" alt="License"></a> -->
</p>
<!-- [![GitHub release][1]][2]
[![Build status][3]][4]
[![GitHub Releases Download][5]][6]
[![dev branch][7]][8]
[![GitHub license][9]][10] -->
<!-- [1]: https://img.shields.io/github/release/lyswhut/lx-music-desktop
[2]: https://github.com/lyswhut/lx-music-desktop/releases
[3]: https://ci.appveyor.com/api/projects/status/flrsqd5ymp8fnte5?svg=true
[4]: https://ci.appveyor.com/project/lyswhut/lx-music-desktop
[5]: https://img.shields.io/github/downloads/lyswhut/lx-music-desktop/latest/total
[5]: https://img.shields.io/github/downloads/lyswhut/lx-music-desktop/total
[6]: https://github.com/lyswhut/lx-music-desktop/releases
[7]: https://img.shields.io/github/package-json/v/lyswhut/lx-music-desktop/dev
[8]: https://github.com/lyswhut/lx-music-desktop/tree/dev
[9]: https://img.shields.io/github/license/lyswhut/lx-music-desktop
[10]: https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE -->
<h2 align="center">洛雪音乐助手桌面版</h2>
### 说明
一个基于 Electron + Vue 开发的音乐软件。
所用技术栈:
- Electron 6.x
- Vue 2.x
已支持的平台:
- Windows 7 及以上
- Mac OS
- Linux
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-desktop/blob/master/CHANGELOG.md)<br>
软件下载请转到:[发布页面](https://github.com/lyswhut/lx-music-desktop/releases)<br>
或者到网盘下载(网盘内有MAC、windows版):`https://www.lanzous.com/b906260/` 密码:`glqw`<br>
使用常见问题请转至:[常见问题](https://github.com/lyswhut/lx-music-desktop#常见问题)
### 源码使用方法
环境要求:Node.js 12.x
```bash
# 开发模式
npm run dev
# 构建免安装版
npm run pack:dir
# 构建安装包(windows版)
npm run pack
```
### UI界面
<p><a href="https://github.com/lyswhut/lx-music-desktop"><img width="100%" src="https://github.com/lyswhut/lx-music-desktop/blob/master/doc/images/app.png" alt="lx-music UI"></a></p>
### 常见问题
常见问题已移至:<https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md>
### 致谢
感谢 [@messoer](https://github.com/messoer) 曾经提供的部分音乐API!
### 免责声明
本项目**不开发或者破解直接获取音频数据**的功能,所有音频数据均来自**第三方接口**!<br>
本软件仅用于**测试 `electron 6.x` 在各种系统上的兼容性**及用于**对比各大音乐平台歌单、排行榜等数据列表的差异性**,使用本软件产生的**任何涉及版权相关的数据**请于**24小时内删除**。<br>
本软件仅用于学习交流使用,禁止用于商业用途,使用本软件所造成的的后果由使用者承担!<br>
若对此有疑问请 mail to: lyswhut@qq.com
### 许可证
[Apache License 2.0](https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE)
+8
View File
@@ -0,0 +1,8 @@
const isDev = process.env.NODE_ENV === 'development'
module.exports = {
modules: {
localIdentName: isDev ? '[folder]-[name]--[local]--[hash:base64:5]' : '[hash:base64:5]',
},
localsConvention: 'camelCase',
}
+39
View File
@@ -0,0 +1,39 @@
const path = require('path')
module.exports = {
target: 'electron-main',
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron'),
},
resolve: {
alias: {
common: path.join(__dirname, '../../src/common'),
},
extensions: ['*', '.js', '.json', '.node'],
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-formatter-friendly'),
},
},
exclude: /node_modules/,
enforce: 'pre',
},
// {
// test: /\.js$/,
// loader: 'babel-loader',
// exclude: /node_modules/,
// },
],
},
performance: {
maxEntrypointSize: 300000,
},
}
+22
View File
@@ -0,0 +1,22 @@
const path = require('path')
const merge = require('webpack-merge')
const webpack = require('webpack')
const baseConfig = require('./webpack.config.base')
module.exports = merge(baseConfig, {
mode: 'development',
entry: {
main: path.join(__dirname, '../../src/main/index.dev.js'),
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"',
},
__static: `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`,
}),
new webpack.NoEmitOnErrorsPlugin(),
],
})
+29
View File
@@ -0,0 +1,29 @@
const path = require('path')
const merge = require('webpack-merge')
const webpack = require('webpack')
const baseConfig = require('./webpack.config.base')
const { dependencies } = require('../../package.json')
module.exports = merge(baseConfig, {
mode: 'production',
entry: {
main: path.join(__dirname, '../../src/main/index.js'),
},
externals: [
...Object.keys(dependencies || {}),
],
node: {
__dirname: false,
__filename: false,
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"',
},
}),
],
})
+84
View File
@@ -0,0 +1,84 @@
process.env.NODE_ENV = 'production'
const chalk = require('chalk')
const del = require('del')
const webpack = require('webpack')
const Multispinner = require('multispinner')
const mainConfig = require('./main/webpack.config.prod')
const rendererConfig = require('./renderer/webpack.config.prod')
const errorLog = chalk.bgRed.white(' ERROR ') + ' '
const okayLog = chalk.bgGreen.white(' OKAY ') + ' '
function build() {
del.sync(['dist/electron', 'build'])
const tasks = ['main', 'renderer']
const m = new Multispinner(tasks, {
preText: 'building',
postText: 'process',
})
let results = ''
m.on('success', () => {
process.stdout.write('\x1B[2J\x1B[0f')
console.log(`\n\n${results}`)
console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`)
process.exit()
})
pack(mainConfig).then(result => {
results += result + '\n\n'
m.success('main')
}).catch(err => {
m.error('main')
console.log(`\n ${errorLog}failed to build main process`)
console.error(`\n${err}\n`)
process.exit(1)
})
pack(rendererConfig).then(result => {
results += result + '\n\n'
m.success('renderer')
}).catch(err => {
m.error('renderer')
console.log(`\n ${errorLog}failed to build renderer process`)
console.error(`\n${err}\n`)
process.exit(1)
})
}
function pack(config) {
return new Promise((resolve, reject) => {
config.mode = 'production'
webpack(config, (err, stats) => {
if (err) reject(err.stack || err)
else if (stats.hasErrors()) {
let err = ''
stats.toString({
chunks: false,
modules: false,
colors: true,
})
.split(/\r?\n/)
.forEach(line => {
err += ` ${line}\n`
})
reject(err)
} else {
resolve(stats.toString({
chunks: false,
colors: true,
}))
}
})
})
}
build()
@@ -0,0 +1,102 @@
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HTMLPlugin = require('html-webpack-plugin')
const vueLoaderConfig = require('../vue-loader.config')
module.exports = {
target: 'electron-renderer',
entry: {
renderer: path.join(__dirname, '../../src/renderer/main.js'),
},
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron'),
publicPath: './',
},
resolve: {
alias: {
'@': path.join(__dirname, '../../src/renderer'),
common: path.join(__dirname, '../../src/common'),
},
extensions: ['*', '.js', '.json', '.vue', '.node'],
},
module: {
rules: [
{
test: /\.(vue|js)$/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-formatter-friendly'),
},
},
exclude: /node_modules/,
enforce: 'pre',
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig,
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.pug$/,
oneOf: [
// Use pug-plain-loader handle .vue file
{
resourceQuery: /vue/,
use: ['pug-plain-loader'],
},
// Use pug-loader handle .pug file
{
use: ['pug-loader'],
},
],
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'imgs/[name]--[folder].[ext]',
},
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name]--[folder].[ext]',
},
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'fonts/[name]--[folder].[ext]',
},
},
],
},
performance: {
maxEntrypointSize: 300000,
},
plugins: [
new HTMLPlugin({
filename: 'index.html',
template: path.join(__dirname, '../../src/index.pug'),
isProd: process.env.NODE_ENV == 'production',
browser: process.browser,
__dirname,
}),
new VueLoaderPlugin(),
],
}
@@ -0,0 +1,56 @@
const path = require('path')
const webpack = require('webpack')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const merge = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
const { mergeCSSLoaderDev } = require('../utils')
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'eval-source-map',
module: {
rules: [
{
test: /\.css$/,
oneOf: mergeCSSLoaderDev(),
},
{
test: /\.less$/,
oneOf: mergeCSSLoaderDev({
loader: 'less-loader',
options: {
sourceMap: true,
},
}),
},
{
test: /\.styl(:?us)?$/,
oneOf: mergeCSSLoaderDev({
loader: 'stylus-loader',
options: {
sourceMap: true,
},
}),
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new FriendlyErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"',
ELECTRON_DISABLE_SECURITY_WARNINGS: 'true',
},
__static: `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`,
}),
],
performance: {
hints: false,
},
})
@@ -0,0 +1,86 @@
const path = require('path')
const webpack = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const merge = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
const { mergeCSSLoaderProd } = require('../utils')
const { dependencies } = require('../../package.json')
let whiteListedModules = ['vue']
module.exports = merge(baseConfig, {
mode: 'production',
devtool: false,
externals: [
...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
],
module: {
rules: [
{
test: /\.css$/,
oneOf: mergeCSSLoaderProd(),
},
{
test: /\.less$/,
oneOf: mergeCSSLoaderProd({
loader: 'less-loader',
options: {
sourceMap: true,
},
}),
},
{
test: /\.styl(:?us)?$/,
oneOf: mergeCSSLoaderProd({
loader: 'stylus-loader',
options: {
sourceMap: true,
},
}),
},
],
},
plugins: [
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../../src/static'),
to: path.join(__dirname, '../../dist/electron/static'),
ignore: ['.*'],
},
]),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"',
},
}),
new MiniCssExtractPlugin({
filename: '[name].css',
}),
new webpack.NamedChunksPlugin(),
],
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: false, // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({}),
],
},
performance: {
hints: 'warning',
},
node: {
__dirname: false,
__filename: false,
},
})
+170
View File
@@ -0,0 +1,170 @@
process.env.NODE_ENV = 'development'
const chalk = require('chalk')
const electron = require('electron')
const path = require('path')
// const { say } = require('cfonts')
const { spawn } = require('child_process')
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const webpackHotMiddleware = require('webpack-hot-middleware')
const mainConfig = require('./main/webpack.config.dev')
const rendererConfig = require('./renderer/webpack.config.dev')
const { logStats } = require('./utils')
let electronProcess = null
let manualRestart = false
let hotMiddleware
function startRenderer() {
return new Promise((resolve, reject) => {
// rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer)
// rendererConfig.mode = 'development'
const compiler = webpack(rendererConfig)
hotMiddleware = webpackHotMiddleware(compiler, {
log: false,
heartbeat: 2500,
})
compiler.hooks.compilation.tap('compilation', compilation => {
// console.log(Object.keys(compilation.hooks))
compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
compiler.hooks.done.tap('done', stats => {
// logStats('Renderer', 'Compile done')
// logStats('Renderer', stats)
})
const server = new WebpackDevServer(
compiler,
{
contentBase: path.join(__dirname, '../'),
quiet: true,
hot: true,
historyApiFallback: true,
clientLogLevel: 'warning',
overlay: {
errors: true,
},
before(app, ctx) {
app.use(hotMiddleware)
ctx.middleware.waitUntilValid(() => {
resolve()
})
},
}
)
server.listen(9080)
})
}
function startMain() {
return new Promise((resolve, reject) => {
// mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
// mainConfig.mode = 'development'
const compiler = webpack(mainConfig)
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
logStats('Main', chalk.white.bold('compiling...'))
hotMiddleware.publish({ action: 'compiling' })
done()
})
compiler.watch({}, (err, stats) => {
if (err) {
console.log(err)
return
}
// logStats('Main', stats)
if (electronProcess && electronProcess.kill) {
manualRestart = true
process.kill(electronProcess.pid)
electronProcess = null
startElectron()
setTimeout(() => {
manualRestart = false
}, 5000)
}
resolve()
})
})
}
function startElectron() {
let args = [
'--inspect=5858',
// 'NODE_ENV=development',
path.join(__dirname, '../dist/electron/main.js'),
]
// detect yarn or npm and process commandline args accordingly
if (process.env.npm_execpath.endsWith('yarn.js')) {
args = args.concat(process.argv.slice(3))
} else if (process.env.npm_execpath.endsWith('npm-cli.js')) {
args = args.concat(process.argv.slice(2))
}
electronProcess = spawn(electron, args)
electronProcess.stdout.on('data', data => {
electronLog(data, 'blue')
})
electronProcess.stderr.on('data', data => {
electronLog(data, 'red')
})
electronProcess.on('close', () => {
if (!manualRestart) process.exit()
})
}
function electronLog(data, color) {
let log = data.toString()
if (/[0-9A-z]+/.test(log)) {
console.log(chalk[color](log))
}
}
function greeting() {
/* const cols = process.stdout.columns
let text = ''
if (cols > 104) text = 'electron-vue'
else if (cols > 76) text = 'electron-|vue'
else text = false
if (text) {
say(text, {
colors: ['yellow'],
font: 'simple3d',
space: false,
})
} else console.log(chalk.yellow.bold('\n electron-vue')) */
console.log(chalk.blue('getting ready...') + '\n')
}
function init() {
greeting()
Promise.all([startRenderer(), startMain()])
.then(() => {
startElectron()
})
.catch(err => {
console.error(err)
})
}
init()
+103
View File
@@ -0,0 +1,103 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const cssLoaderConfig = require('./css-loader.config')
const chalk = require('chalk')
// merge css-loader in development
exports.mergeCSSLoaderDev = beforeLoader => {
const loader = [
// 这里匹配 `<style module>`
{
resourceQuery: /module/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: Object.assign({
sourceMap: true,
}, cssLoaderConfig),
},
{
loader: 'postcss-loader',
options: {
sourceMap: true,
},
},
],
},
// 这里匹配普通的 `<style>` 或 `<style scoped>`
{
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: true,
},
},
],
},
]
if (beforeLoader) {
loader[0].use.push(beforeLoader)
loader[1].use.push(beforeLoader)
}
return loader
}
// merge css-loader in production
exports.mergeCSSLoaderProd = beforeLoader => {
const loader = [
// 这里匹配 `<style module>`
{
resourceQuery: /module/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: cssLoaderConfig,
},
'postcss-loader',
],
},
// 这里匹配普通的 `<style>` 或 `<style scoped>`
{
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
],
},
]
if (beforeLoader) {
loader[0].use.push(beforeLoader)
loader[1].use.push(beforeLoader)
}
return loader
}
exports.logStats = (proc, data) => {
let log = ''
log += chalk.yellow.bold(`${proc} Process:`)
log += '\n'
if (typeof data === 'object') {
data.toString({
colors: true,
chunks: false,
}).split(/\r?\n/).forEach(line => {
log += ' ' + line + '\n'
})
} else {
log += ` ${data}\n`
}
console.log(log)
}
+12
View File
@@ -0,0 +1,12 @@
const isDev = process.env.NODE_ENV === 'development'
module.exports = {
// preserveWhitepace: true,
compilerOptions: {
whitespace: 'preserve',
},
extractCSS: !isDev,
// cssModules: {
// localIndetName: '',
// },
}
+97
View File
@@ -0,0 +1,97 @@
const path = require('path')
const webpack = require('webpack')
const HTMLPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const vueLoaderConfig = require('../vue-loader.config')
module.exports = {
target: 'web',
entry: path.join(__dirname, '../../src/renderer/main.js'),
output: {
path: path.join(__dirname, '../../dist/web'),
},
resolve: {
alias: {
'@': path.join(__dirname, '../../src/renderer'),
common: path.join(__dirname, '../../src/common'),
},
extensions: ['*', '.js', '.vue', '.json', '.css'],
},
module: {
rules: [
{
test: /\.(vue|js)$/,
use: {
loader: 'eslint-loader',
options: {
formatter: require('eslint-formatter-friendly'),
},
},
exclude: /node_modules/,
enforce: 'pre',
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig,
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.pug$/,
oneOf: [
// Use pug-plain-loader handle .vue file
{
resourceQuery: /vue/,
use: ['pug-plain-loader'],
},
// Use pug-loader handle .pug file
{
use: ['pug-loader'],
},
],
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
name: 'imgs/[name].[ext]',
},
},
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
name: 'fonts/[name].[ext]',
},
},
},
],
},
performance: {
maxEntrypointSize: 300000,
},
plugins: [
new VueLoaderPlugin(),
new HTMLPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../../src/index.pug'),
nodeModules: false,
isProd: process.env.NODE_ENV == 'production',
browser: process.browser,
__dirname,
}),
new webpack.DefinePlugin({
'process.env.IS_WEB': 'true',
}),
],
}
+57
View File
@@ -0,0 +1,57 @@
const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const baseConfig = require('./webpack.config.base')
const { mergeCSSLoaderDev } = require('../utils')
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'eval-source-map',
output: {
filename: '[name].js',
path: path.join(__dirname, '../../dist/web'),
},
module: {
rules: [
{
test: /\.css$/,
oneOf: mergeCSSLoaderDev(),
},
{
test: /\.less$/,
oneOf: mergeCSSLoaderDev({
loader: 'less-loader',
options: {
sourceMap: true,
},
}),
},
{
test: /\.styl$/,
oneOf: mergeCSSLoaderDev({
loader: 'stylus-loader',
options: {
sourceMap: true,
},
}),
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"',
},
}),
new FriendlyErrorsPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
],
performance: {
hints: false,
},
})
+94
View File
@@ -0,0 +1,94 @@
const path = require('path')
const webpack = require('webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const merge = require('webpack-merge')
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const baseConfig = require('./webpack.config.base')
const { mergeCSSLoaderProd } = require('../utils')
module.exports = merge(baseConfig, {
mode: 'production',
devtool: false,
output: {
filename: '[name].[chunkhash:8].js',
},
module: {
rules: [
{
test: /\.css$/,
oneOf: mergeCSSLoaderProd(),
},
{
test: /\.less$/,
oneOf: mergeCSSLoaderProd({
loader: 'less-loader',
options: {
sourceMap: true,
},
}),
},
{
test: /\.styl$/,
oneOf: mergeCSSLoaderProd({
loader: 'stylus-loader',
options: {
sourceMap: true,
},
}),
},
],
},
plugins: [
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../../src/static'),
to: path.join(__dirname, '../dist/web/static'),
ignore: ['.*'],
},
]),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"',
},
}),
new MiniCssExtractPlugin({
filename: '[name].[contentHash:8].css',
}),
new webpack.NamedChunksPlugin(),
],
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: false, // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({}),
],
splitChunks: {
cacheGroups: {
// chunks: 'all',
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
enforce: true,
chunks: 'all',
},
styles: {
name: 'styles',
test: /\.(css|less)$/,
chunks: 'all',
enforce: true,
},
},
},
runtimeChunk: true,
},
performance: {
hints: 'warning',
},
})
Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

+219
View File
@@ -0,0 +1,219 @@
{\rtf1\adeflang1025\ansi\ansicpg936\uc2\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe2052\themelang1033\themelangfe2052\themelangcs0{\fonttbl{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;}
{\f13\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'cb\'ce\'cc\'e5{\*\falt SimSun};}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}
{\f44\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}@\'cb\'ce\'cc\'e5;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fdbmajor\f31501\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df Light;}{\fhimajor\f31502\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df Light;}
{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}
{\fdbminor\f31505\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df{\*\falt DengXian};}{\fhiminor\f31506\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}\'b5\'c8\'cf\'df{\*\falt DengXian};}
{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f65\fbidi \fmodern\fcharset238\fprq1 Courier New CE;}{\f66\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;}
{\f68\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f69\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f70\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);}{\f71\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}
{\f72\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f73\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f177\fbidi \fnil\fcharset0\fprq2 SimSun Western{\*\falt SimSun};}{\f487\fbidi \fnil\fcharset0\fprq2 @SimSun Western;}
{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31520\fbidi \fnil\fcharset0\fprq2 DengXian Light Western;}
{\fdbmajor\f31518\fbidi \fnil\fcharset238\fprq2 DengXian Light CE;}{\fdbmajor\f31519\fbidi \fnil\fcharset204\fprq2 DengXian Light Cyr;}{\fdbmajor\f31521\fbidi \fnil\fcharset161\fprq2 DengXian Light Greek;}
{\fhimajor\f31530\fbidi \fnil\fcharset0\fprq2 DengXian Light Western;}{\fhimajor\f31528\fbidi \fnil\fcharset238\fprq2 DengXian Light CE;}{\fhimajor\f31529\fbidi \fnil\fcharset204\fprq2 DengXian Light Cyr;}
{\fhimajor\f31531\fbidi \fnil\fcharset161\fprq2 DengXian Light Greek;}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}
{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}
{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31560\fbidi \fnil\fcharset0\fprq2 DengXian Western{\*\falt DengXian};}
{\fdbminor\f31558\fbidi \fnil\fcharset238\fprq2 DengXian CE{\*\falt DengXian};}{\fdbminor\f31559\fbidi \fnil\fcharset204\fprq2 DengXian Cyr{\*\falt DengXian};}{\fdbminor\f31561\fbidi \fnil\fcharset161\fprq2 DengXian Greek{\*\falt DengXian};}
{\fhiminor\f31570\fbidi \fnil\fcharset0\fprq2 DengXian Western{\*\falt DengXian};}{\fhiminor\f31568\fbidi \fnil\fcharset238\fprq2 DengXian CE{\*\falt DengXian};}{\fhiminor\f31569\fbidi \fnil\fcharset204\fprq2 DengXian Cyr{\*\falt DengXian};}
{\fhiminor\f31571\fbidi \fnil\fcharset161\fprq2 DengXian Greek{\*\falt DengXian};}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}
{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}
{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}
{\f45\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f46\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f48\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f49\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}
{\f50\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f51\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f52\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}
{\f53\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;
\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;}
{\*\defchp \fs21\kerning2\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{
\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs21\lang1033\langfe2052\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp2052
\snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\*
\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv
\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs21\lang1033\langfe2052\kerning2\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp2052
\snext11 \ssemihidden \sunhideused Normal Table;}{\s15\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af2\afs22\alang1025 \ltrch\fcs0
\fs21\lang1033\langfe2052\kerning2\loch\f31505\hich\af2\dbch\af31505\cgrid\langnp1033\langfenp2052 \sbasedon0 \snext15 \slink16 \sunhideused Plain Text;}{\*\cs16 \additive \rtlch\fcs1 \af2 \ltrch\fcs0 \loch\f31505\hich\af2 \sbasedon10 \slink15 \slocked
\'b4\'bf\'ce\'c4\'b1\'be \'d7\'d6\'b7\'fb;}}{\*\rsidtbl \rsid927107\rsid3950508\rsid11081282\rsid12910709\rsid13643782\rsid14384001}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0
\mnaryLim1}{\info{\author lysyw}{\operator lysyw}{\creatim\yr2019\mo8\dy17\hr10\min22}{\revtim\yr2019\mo8\dy17\hr10\min22}{\version2}{\edmins0}{\nofpages1}{\nofwords8}{\nofchars49}{\nofcharsws56}{\vern111}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.co
m/office/word/2003/wordml}}\paperw11906\paperh16838\margl2253\margr2253\margt1440\margb1440\gutter0\ltrsect
\deftab420\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\formshade\horzdoc\dgmargin\dghspace180\dgvspace156
\dghorigin2253\dgvorigin1440\dghshow0\dgvshow2\jcompress\lnongrid
\viewkind1\viewscale100\splytwnine\ftnlytwnine\htmautsp\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct\asianbrkrule\rsidroot3950508\newtblstyruls
\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat {\upr{\*\fchars
!%),.:\'3b>?]\'7d\'a1\'e9\'a1\'a7\'a1\'e3\'a1\'a4\'a1\'a6\'a1\'a5\'a8\'44\'a1\'ac\'a1\'af\'a1\'b1\'a1\'ad\'a1\'eb\'a1\'e4\'a1\'e5?\'a1\'e6\'a1\'c3\'a1\'a2\'a1\'a3\'a1\'a8\'a1\'b5\'a1\'b7\'a1\'b9\'a1\'bb\'a1\'bf\'a1\'b3\'a1\'bd\'a8\'95\'a6\'e1\'a6\'e3\'a6\'e7\'a6\'e5\'a6\'eb\'a9\'77\'a9\'79\'a9\'7b\'a3\'a1\'a3\'a2\'a3\'a5\'a3\'a7\'a3\'a9\'a3\'ac\'a3\'ae\'a3\'ba\'a3\'bb\'a3\'bf\'a3\'dd\'a3\'e0\'a3\'fc\'a3\'fd\'a1\'ab\'a1\'e9
}{\*\ud\uc0{\*\fchars
!%),.:\'3b>?]\'7d{\uc2\u162 \'a1\'e9\'a1\'a7\'a1\'e3\'a1\'a4\'a1\'a6\'a1\'a5\'a8D\'a1\'ac\'a1\'af\'a1\'b1\'a1\'ad\'a1\'eb\'a1\'e4\'a1\'e5}{\uc1\u8250 ?\'a1\'e6\'a1\'c3\'a1\'a2\'a1\'a3\'a1\'a8\'a1\'b5\'a1\'b7\'a1\'b9\'a1\'bb\'a1\'bf\'a1\'b3\'a1\'bd\'a8\'95\'a6\'e1\'a6\'e3\'a6\'e7\'a6\'e5\'a6\'eb\'a9w\'a9y\'a9\'7b\'a3\'a1\'a3\'a2\'a3\'a5\'a3\'a7\'a3\'a9\'a3\'ac\'a3\'ae\'a3\'ba\'a3\'bb\'a3\'bf\'a3\'dd\'a3\'e0\'a3\'fc\'a3\'fd\'a1\'ab\'a1\'e9}
}}}{\upr{\*\lchars $([\'7b\'a1\'ea\'a3\'a4\'a1\'a4\'a1\'ae\'a1\'b0\'a1\'b4\'a1\'b6\'a1\'b8\'a1\'ba\'a1\'be\'a1\'b2\'a1\'bc\'a8\'94\'a9\'76\'a9\'78\'a9\'7a\'a1\'e7\'a3\'a8\'a3\'ae\'a3\'db\'a3\'fb\'a1\'ea\'a3\'a4}{\*\ud\uc0{\*\lchars
$([\'7b{\uc2\u163 \'a1\'ea\u165 \'a3\'a4\'a1\'a4\'a1\'ae\'a1\'b0\'a1\'b4\'a1\'b6\'a1\'b8\'a1\'ba\'a1\'be\'a1\'b2\'a1\'bc\'a8\'94\'a9v\'a9x\'a9z\'a1\'e7\'a3\'a8\'a3\'ae\'a3\'db\'a3\'fb\'a1\'ea\'a3\'a4}}}}\fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1
\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery851\footery992\colsx425\endnhere\sectlinegrid312\sectspecifyl\sectrsid2109456\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang
{\pntxta \dbch .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta \dbch .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta \dbch )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl6
\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}{\*\pnseclvl9
\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb \dbch (}{\pntxta \dbch )}}\pard\plain \ltrpar\s15\qj \li0\ri0\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12910709 \rtlch\fcs1 \af2\afs22\alang1025 \ltrch\fcs0
\fs21\lang1033\langfe2052\kerning2\loch\af31505\hich\af2\dbch\af31505\cgrid\langnp1033\langfenp2052 {\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \loch\af13\hich\af13\dbch\f13 \'b1\'be\'b3\'cc\'d0\'f2
\'bd\'f6\'d3\'c3\'d3\'da\'d1\'a7\'cf\'b0\'bd\'bb\'c1\'f7\'ca\'b9\'d3\'c3\'a3\'a1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \loch\af13\hich\af13\dbch\f13 \'c7\'eb\'ce\'f0\'d3\'c3\'d3\'da\'c9\'cc\'d2\'b5\'d3\'c3\'cd\'be\'a3\'a1\'a3\'a1}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709
\par }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \loch\af13\hich\af13\dbch\f13 \'ca\'b9\'d3\'c3\'b1\'be\'c8\'ed\'bc\'fe\'d4\'ec\'b3\'c9\'b5\'c4\'d2\'bb\'c7\'d0\'ba\'f3\'b9\'fb\'d3\'c9\'ca\'b9\'d3\'c3\'d5\'df
\'b3\'d0\'b5\'a3\'a3\'a1}{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709
\par
\par \hich\af13\dbch\af13\loch\f13 By: }{\rtlch\fcs1 \af13 \ltrch\fcs0 \loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709 \loch\af13\hich\af13\dbch\f13 \'c2\'e4\'d1\'a9\'ce\'de\'ba\'db}{\rtlch\fcs1 \af13 \ltrch\fcs0
\loch\af13\hich\af13\dbch\af13\insrsid12910709\charrsid12910709
\par
\par
\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a
9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad
5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6
b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0
0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6
a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f
c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512
0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462
a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865
6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b
4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b
4757e8d3f729e245eb2b260a0238fd010000ffff0300504b0304140006000800000021006ab3c999d7060000941a0000160000007468656d652f7468656d652f
7468656d65312e786d6cec595b6b1b47147e2ff43f2cfbaee8b6ab8b891c748ddbd8498894943c8ea59176e2d91db133b2234220244fa55028a4250f0d94bef4
a194061a68681ffa5feae290b63fa2676657ab1969543bc68550621bb33bfb9d33df9c73f63bb3bb97afdc0fa97388634e58d4708b970aae83a3211b9168d270
6f0f7ab99aeb7081a211a22cc20d778eb97b65fbc30f2ea32d11e0103b601ff12dd4700321a65bf93c1fc230e297d81447706dcce21009388d27f9518c8ec06f
48f3a542a1920f11895c274221b8bd311e932176fe78f5eb9b6f9ffdfee833f873b7177374294c14092e078634eecb19b061a8b0a383a244f0396fd3d83944b4
e1c274237634c0f785eb50c4055c68b805f5e3e6b72fe7d1566a44c5065bcdaea77e52bbd46074505273c693fd6c52cff3bd4a33f3af0054ace3bad56ea55bc9
fc29001a0e61a50917d367b5d4f652ac064a0e2dbe3bd54eb968e035ffe535ce4d5ffe1a78054afc7b6bf85eaf0d5134f00a94e0fd35bcdfaab73aa67f054af0
95357cb5d0ec7855c3bf02059444076be8825f29b717abcd20634677acf0baeff5aaa5d4f91205d59055979c62cc22b1a9d642748fc53d0048204582448e984f
f1180da1985ffff0e9eb5f7e7376c92480ba9ba28871182d940abd4219fecb5f4f1da984a22d8c3463490b88f0b52149c7e1c3984c45c3fd18bcba1ae4e4d5ab
e3c72f8f1fff7cfce4c9f1e31fd3b9952bc36e074513ddeeafefbef8fbf923e7cf9fbef9ebe997c9d4ab78aee38da559ddc38a979138f9eac5eb972f4e9e7dfe
e6fba716efcd18edebf001093177aee323e7160b61819609f07efc7616830011dda2194d388a909cc5e2bf2b02037d7d8e28b2e05ad88ce39d1894c606bc3abb
6710ee07f14c108bc76b416800f718a32d165ba3704dcea58579308b26f6c9e3998ebb85d0a16dee368a8c2c77675390586273d90eb041f326459140131c61e1
c86bec0063cbeaee1262c4758f0c63c6d958387789d342c41a9201d937aa6969b44342c8cbdc4610f26dc466ef8ed362d4b6ea0e3e3491706f206a213fc0d408
e355341328b4b91ca090ea01df4522b091eccfe3a18eeb7201999e60ca9cee08736eb3b911c37ab5a45f43206ed6b4efd179682263410e6c3e7711633ab2c30e
da010aa7366c9f44818efd881f408922e7261336f81e33ef10790e7940d1c674df21d848f7e96a701b1456a7b42c107965165b72791533a37efb733a4658490d
e8bf21eb21894ed3f81575f7ff3b75070d3df9fab9654117a3e876c7463ade52cb9b31b1de4c3b2b0abe09b7aadb6d168fc8bb2fdb1d348b6e62b853d67bd77b
d57eafdaeeff5eb537ddcf17afd54b7906e596bbd664b3aeb6eee1c69dfb9850da17738a77b9dabc73684aa31e0c4a3bf5f08ab327b9690087f24e86090cdc24
46cac68999f88488a01fa0296cf18bae7432e1a9eb0977a68cc3ce5f0d5b7d4b3c9d857b6c943cb0168bf2e134110f8ec472bce067e3f0b0211274a5ba7c08cb
dc2bb613f5b0bc20206ddf8684369949a26c21515d0cca20a94773089a85845ad985b0a85b58d4a4fb45aad65800b52c2bb06b7260afd5707d0f4cc0081eaa10
c52399a724d58becaa645e64a63705d3a800d8432c2a6099e9bae4ba7179727549a99d21d30609addc4c122a32aa87f1008d705a9d72f42c34de36d7f5654a0d
7a32146a3e28ad258d6aeddf589c37d760b7aa0d34d2958246ce51c3ad947d2899219a36dc313cf8c3613885dae172b78be8045ea20d459cdcf0e7519669cc45
07f12009b8129d440d422270ec5012365cb9fc2c0d34521aa2b8154b2008ef2cb93ac8cabb460e926e26198fc77828f4b46b2332d2c929287ca215d6abcafcfc
6069c96690ee7e303a72f6e92cbe85a0c4fc6a5106704438bc002a26d11c1178a19909d9b2fe561a532abbfa1b455543c938a2d300a51d4517f304aea43ca3a3
ceb2186867e99a21a05a48d246b83f910d560faad14db3ae9170d8d8754f379291d34473d9330d55915dd3ae62c60c8b36b012cbf335798dd522c4a0697a874f
a47b5572eb0bad5bd927645d02029ec5cfd275cfd010346acbc90c6a92f1ba0c4bcd4e47cddeb158e029d4ced22434d5af2cdcaec42deb11d6e960f05c9d1fec
56ab1686c68b7da58ab4fa00a27f9c60fbf7403c3af01a78460557a9844f0f31820d515fed4912d9805be4be486f0d3872663169b80f0a7ed36b97fc76ae50f3
bb39afec157235bf59ce357dbf5cecfac542a7557a088d450461d14f3ebef4e02d149da79f60d4f8da679870f1a2edd2908579a63eb3e41571f519a658b27d86
19c80f2cae4340741e544abd7ab9deaae4eae5662fe7755ab55cbd5d69e53a9576b5d3ebb4fd5abdf7d0750e15d86b96db5ea55bcb558aed76ceab1424fd5a3d
57f54aa5a6576dd6ba5ef361ba8d819527f291c602c2ab786dff030000ffff0300504b0304140006000800000021000dd1909fb60000001b0100002700000074
68656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384
e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d26
2452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe51
4173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c0200001300000000000000
000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b000000000000
00000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000
190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d00140006000800000021006ab3c999d7060000941a000016000000
00000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b010000
2700000000000000000000000000e10900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000dc0a00000000}
{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d
617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169
6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363
656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e}
{\*\latentstyles\lsdstimax377\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9;
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3;
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6;
\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;
\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;
\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;
\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;
\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;
\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;
\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;
\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;
\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;
\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;
\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;
\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6;
\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;
\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;
\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis;
\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography;
\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4;
\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4;
\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1;
\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1;
\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2;
\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2;
\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3;
\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4;
\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4;
\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5;
\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5;
\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6;
\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6;
\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark;
\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1;
\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1;
\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2;
\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3;
\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3;
\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4;
\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4;
\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5;
\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5;
\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6;
\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Link;
\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Link Error;}}{\*\datastore 0105000002000000180000004d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000
d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000908a
0599a254d501feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000105000000000000}}
+5
View File
@@ -0,0 +1,5 @@
This program is only for learning to communicate!
Do not use for commercial purposes! !
All consequences of using this software are borne by the user!
By: lyswhut
+5
View File
@@ -0,0 +1,5 @@
本程序仅用于学习交流使用!
请勿用于商业用途!!
使用本软件造成的一切后果由使用者承担!
By: 落雪无痕
+14726
View File
File diff suppressed because it is too large Load Diff
+216
View File
@@ -0,0 +1,216 @@
{
"name": "lx-music-desktop",
"version": "0.8.1",
"description": "一个免费的音乐下载助手",
"main": "./dist/electron/main.js",
"productName": "lx-music-desktop",
"scripts": {
"pack": "node build-config/pack.js && npm run pack:win",
"pack:win": "npm run pack:win:setup && npm run pack:win:7z",
"pack:win:setup": "cross-env TARGET=win_安装版 ARCH=x64_x86 electron-builder -w=nsis --x64 --ia32",
"pack:win:portable": "npm run pack:win:portable:x64_x86 && npm run pack:win:portable:x64 && npm run pack:win:portable:x86",
"pack:win:portable:x64_x86": "cross-env TARGET=便携版 ARCH=x64_x86 electron-builder -w=portable --x64 --ia32",
"pack:win:portable:x64": "cross-env TARGET=便携版 ARCH=x64 electron-builder -w=portable --x64",
"pack:win:portable:x86": "cross-env TARGET=便携版 ARCH=x86 electron-builder -w=portable --ia32",
"pack:win:7z": "npm run pack:win:7z:x64 && npm run pack:win:7z:x86",
"pack:win:7z:x64": "cross-env TARGET=win_绿色版 ARCH=x64 electron-builder -w=7z --x64",
"pack:win:7z:x86": "cross-env TARGET=win_绿色版 ARCH=x86 electron-builder -w=7z --ia32",
"publish": "node publish",
"publish:gh": "node build-config/pack.js && npm run publish:win",
"publish:win": "npm run publish:win:7z && npm run publish:win:setup",
"publish:win:setup": "cross-env TARGET=Setup ARCH=x64_x86 electron-builder -w=nsis --x64 --ia32 -p onTagOrDraft",
"publish:win:portable": "npm run publish:win:portable:x64_x86 && npm run publish:win:portable:x64 && npm run publish:win:portable:x86",
"publish:win:portable:x64_x86": "cross-env TARGET=portable ARCH=x64_x86 electron-builder -w=portable --x64 --ia32 -p onTagOrDraft",
"publish:win:portable:x64": "cross-env TARGET=portable ARCH=x64 electron-builder -w=portable --x64 -p onTagOrDraft",
"publish:win:portable:x86": "cross-env TARGET=portable ARCH=x86 electron-builder -w=portable --ia32 -p onTagOrDraft",
"publish:win:7z": "npm run publish:win:7z:x64 && npm run publish:win:7z:x86",
"publish:win:7z:x64": "cross-env TARGET=green ARCH=win_x64 electron-builder -w=7z --x64 -p always",
"publish:win:7z:x86": "cross-env TARGET=green ARCH=win_x86 electron-builder -w=7z --ia32 -p onTagOrDraft",
"publish:gh:mac": "node build-config/pack.js && npm run publish:mac",
"publish:mac": "npm run publish:mac:dmg",
"publish:mac:dmg": "electron-builder -m=dmg -p onTagOrDraft",
"publish:gh:linux": "node build-config/pack.js && npm run publish:linux",
"publish:linux": "npm run publish:linux:deb && npm run publish:linux:appImage",
"publish:linux:appImage": "cross-env ARCH=x64 electron-builder -l=AppImage -p onTagOrDraft",
"publish:linux:deb": "npm run publish:linux:deb:x64 && npm run publish:linux:deb:x86",
"publish:linux:deb:x64": "cross-env ARCH=x64 electron-builder -l=deb --x64 -p onTagOrDraft",
"publish:linux:deb:x86": "cross-env ARCH=x86 electron-builder -l=deb --ia32 -p onTagOrDraft",
"pack:linux": "node build-config/pack.js && electron-builder -l",
"pack:dir": "node build-config/pack.js && electron-builder --dir",
"dev": "node build-config/runner-dev.js",
"clean:electron": "rimraf dist/electron",
"clean:web": "rimraf dist/web",
"clean": "rimraf dist && rimraf build",
"build:main": "cross-env NODE_ENV=production webpack --config build-config/main/webpack.config.prod.js --progress --hide-modules",
"build:renderer": "cross-env NODE_ENV=production webpack --config build-config/renderer/webpack.config.prod.js --progress --hide-modules",
"build:web": "npm run clean:web && cross-env NODE_ENV=production webpack --config build-config/web/webpack.config.prod.js --progress --hide-modules",
"build": "npm run clean:electron && npm run build:main && npm run build:renderer",
"lint": "eslint --ext .js,.vue -f ./node_modules/eslint-formatter-friendly src",
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-formatter-friendly --fix src"
},
"browserslist": [
"Chrome >= 76"
],
"engines": {
"node": ">= 12"
},
"build": {
"appId": "cn.toside.music.desktop",
"directories": {
"output": "./build"
},
"files": [
"dist/electron/**/*"
],
"extraResources": [
"./licenses"
],
"win": {
"icon": "./resources/icons/256x256.ico",
"legalTrademarks": "lyswhut",
"artifactName": "${productName} v${version} ${env.ARCH} ${env.TARGET}.${ext}"
},
"mac": {
"icon": "./resources/icons/512x512.png",
"category": "public.app-category.music"
},
"linux": {
"maintainer": "lyswhut <lyswuhut@qq.com>",
"artifactName": "${productName} v${version} ${env.ARCH}.${ext}"
},
"nsis": {
"oneClick": false,
"language": "2052",
"allowToChangeInstallationDirectory": true,
"differentialPackage": true,
"license": "./licenses/license.rtf",
"shortcutName": "lx-music"
},
"dmg": {
"contents": [
{
"x": 110,
"y": 150,
"name": "lx-music"
},
{
"x": 240,
"y": 150,
"type": "link",
"path": "/Applications",
"name": "lx-music"
}
],
"title": "洛雪音乐助手 v${version}"
},
"appImage": {
"license": "./licenses/license_zh.txt",
"category": "Audio"
},
"publish": [
{
"provider": "github",
"owner": "lyswhut",
"repo": "lx-music-desktop"
}
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/lyswhut/lx-music-desktop.git"
},
"keywords": [
"music-player",
"electron-app",
"vuejs2"
],
"author": {
"name": "lyswhut",
"email": "lyswuhut@qq.com"
},
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/lyswhut/lx-music-desktop/issues"
},
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
"devDependencies": {
"@babel/core": "^7.6.4",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/polyfill": "^7.6.0",
"@babel/preset-env": "^7.6.3",
"autoprefixer": "^9.6.5",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"babel-minify-webpack-plugin": "^0.3.1",
"babel-preset-minify": "^0.5.1",
"cfonts": "^2.4.5",
"chalk": "^2.4.2",
"changelog-parser": "^2.8.0",
"copy-webpack-plugin": "^5.0.4",
"core-js": "^3.3.2",
"cos-nodejs-sdk-v5": "^2.5.12",
"cross-env": "^6.0.3",
"css-loader": "^3.2.0",
"del": "^5.1.0",
"electron": "^6.0.12",
"electron-builder": "^21.2.0",
"electron-debug": "^3.0.1",
"electron-devtools-installer": "^2.2.4",
"eslint": "^6.5.1",
"eslint-config-standard": "^14.1.0",
"eslint-formatter-friendly": "^7.0.0",
"eslint-loader": "^3.0.2",
"eslint-plugin-html": "^6.0.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"file-loader": "^4.2.0",
"friendly-errors-webpack-plugin": "^1.7.0",
"html-webpack-plugin": "^3.2.0",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"markdown-it": "^10.0.0",
"mini-css-extract-plugin": "^0.8.0",
"multispinner": "^0.2.1",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"pug": "^2.0.4",
"pug-loader": "^2.4.0",
"pug-plain-loader": "^1.0.0",
"raw-loader": "^3.1.0",
"rimraf": "^3.0.0",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2",
"terser-webpack-plugin": "^2.1.3",
"url-loader": "^2.2.0",
"vue-loader": "^15.7.1",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.9",
"webpack-dev-server": "^3.8.2",
"webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^4.2.2"
},
"dependencies": {
"axios": "^0.19.0",
"crypto-js": "^3.1.9-1",
"dnscache": "^1.0.2",
"electron-log": "^3.0.8",
"electron-store": "^5.0.0",
"electron-updater": "^4.1.2",
"flac-metadata": "^0.1.1",
"js-htmlencode": "^0.3.0",
"lrc-file-parser": "^0.1.14",
"node-downloader-helper": "^1.0.10",
"node-id3": "^0.1.11",
"request": "^2.88.0",
"vue": "^2.6.10",
"vue-electron": "^1.0.6",
"vue-router": "^3.1.3",
"vuex": "^3.1.1",
"vuex-electron": "^1.0.3",
"vuex-router-sync": "^5.0.0"
}
}
+7
View File
@@ -0,0 +1,7 @@
const autoprefixer = require('autoprefixer')
module.exports = {
plugins: [
autoprefixer(),
],
}
+3
View File
@@ -0,0 +1,3 @@
#### 修复
- 修复酷我源搜索歌曲结果未添加128k音质导致播放128k音质时显示“该歌曲没有可播放的音频”的问题
+50
View File
@@ -0,0 +1,50 @@
const fs = require('fs')
const path = require('path')
const chalk = require('chalk')
const clearAssets = require('./utils/clearAssets')
// const packAssets = require('./utils/packAssets')
// const compileAssets = require('./utils/compileAssets')
const updateVersionFile = require('./utils/updateChangeLog')
// const copyFile = require('./utils/copyFile')
// const githubRelease = require('./utils/githubRelease')
// const { parseArgv } = require('./utils')
const run = async() => {
// const params = parseArgv(process.argv.slice(2))
// const bak = await updateVersionFile(params.ver)
const bak = await updateVersionFile(process.argv.slice(2)[0])
try {
console.log(chalk.blue('Clearing assets...'))
await clearAssets()
console.log(chalk.green('Assets clear completed...'))
// console.log(chalk.blue('Compileing assets...'))
// await compileAssets()
// console.log(chalk.green('Asset compiled successfully.'))
// console.log(chalk.blue('Building assets...'))
// await packAssets()
// console.log(chalk.green('Asset build successfully.'))
// console.log(chalk.blue('Copy files...'))
// await copyFile()
// console.log(chalk.green('Complete copy of all files.'))
// console.log(chalk.blue('Create release...'))
// await githubRelease(params)
// console.log(chalk.green('Release created.'))
console.log(chalk.green('日志更新完成~'))
} catch (error) {
console.log(error)
console.log(chalk.red('程序发布失败'))
console.log(chalk.blue('正在还原版本信息'))
fs.writeFileSync(path.join(__dirname, './version.json'), bak.version_bak + '\n', 'utf-8')
fs.writeFileSync(path.join(__dirname, '../package.json'), bak.pkg_bak + '\n', 'utf-8')
console.log(chalk.blue('版本信息还原完成'))
}
}
run()
+8
View File
@@ -0,0 +1,8 @@
const del = require('del')
// const copyFile = require('./copyFile')
module.exports = () => {
del.sync(['publish/assets/*'])
// return copyFile(false)
}
+25
View File
@@ -0,0 +1,25 @@
const { spawn } = require('child_process')
const { jp } = require('./index')
const chalk = require('chalk')
module.exports = () => new Promise((resolve, reject) => {
const pack = spawn('node', [jp('../../build-config/pack.js')])
// pack.stdout.on('data', (data) => {
// console.log(chalk.blue(data))
// })
pack.stderr.on('data', (data) => {
console.log(chalk.red(data))
})
pack.on('close', code => {
if (code === 0) {
resolve()
} else {
console.log(chalk.red('Asset compilation failed.'))
reject()
}
})
})
+38
View File
@@ -0,0 +1,38 @@
const fs = require('fs')
const chalk = require('chalk')
const { jp, copyFile } = require('./index')
const buildDir = '../../build'
const getBuildFileName = () => {
const names = []
const pathRegExp = [
/latest\.yml$/,
/\.exe$/,
/\.blockmap$/,
]
const files = fs.readdirSync(jp(buildDir), 'utf8')
files.forEach(name => {
pathRegExp.forEach(regexp => {
if (regexp.test(name)) names.push(name)
})
})
return names
}
const copy = names => {
const tasks = names.map(name => copyFile(jp(buildDir, name), jp('../assets', name)))
return Promise.all(tasks)
}
module.exports = (isCopyVersion = true) => {
copy(getBuildFileName()).then(() => {
if (isCopyVersion) fs.writeFileSync(jp('../assets/version.json'), JSON.stringify(require('../version.json')), 'utf8')
}).catch(err => {
console.log(err)
console.log(chalk.red('File copy failed.'))
return Promise.reject(err)
})
}
+153
View File
@@ -0,0 +1,153 @@
const fs = require('fs')
const { jp, sizeFormate } = require('./index')
const chalk = require('chalk')
const COS = require('cos-nodejs-sdk-v5')
const config = require('./cosConfig')
const MultiProgress = require('multi-progress')
const multi = new MultiProgress(process.stderr)
const cos = new COS({
SecretId: config.secretId,
SecretKey: config.secretKey,
KeepAlive: false,
})
const getCosFileList = () => new Promise((resolve, reject) => {
cos.getBucket({
Bucket: config.bucket,
Region: config.region,
Prefix: config.prefix,
}, function(err, data) {
if (err) {
console.log(err)
reject(err)
console.log(chalk.red('COS文件列表获取失败'))
}
resolve(data.Contents.filter(o => o.Key !== config.prefix).map(o => o.Key.replace(config.prefix, '')))
})
})
const getLocalFileList = () => fs.readdirSync(jp('../assets'), 'utf8')
const diffFileList = (localFiles, cosFiles) => {
const removeFiles = []
cosFiles.forEach(file => {
let index = localFiles.indexOf(file)
if (index < 0) return removeFiles.push(file)
localFiles.splice(index, 1)
})
if (cosFiles.includes('latest.yml')) {
removeFiles.push('latest.yml')
localFiles.push('latest.yml')
}
if (cosFiles.includes('version.json')) {
removeFiles.push('version.json')
localFiles.push('version.json')
}
return removeFiles
}
const deleteCosFiles = files => new Promise((resolve, reject) => {
files = files.map(f => ({ Key: config.prefix + f }))
cos.deleteMultipleObject({
Bucket: config.bucket,
Region: config.region,
Objects: files,
}, function(err, data) {
if (err) {
console.log(err)
reject(err)
}
resolve()
})
})
const createProgressBar = (name, spacekLen, total) => multi.newBar(
`${` ${name}`.padEnd(spacekLen, ' ')} :status [:bar] :current/:total :percent :speed`, {
complete: '=',
incomplete: ' ',
width: 30,
total,
})
const uploadFile = (fileName, len) => new Promise((resolve, reject) => {
const filePath = jp('../assets', fileName)
// let size = fs.statSync(filePath).size
let bar = null
let prevLoaded = 0
cos.sliceUploadFile({
Bucket: config.bucket,
Region: config.region,
Key: config.prefix + fileName, /* 必须 */
FilePath: filePath, /* 必须 */
// TaskReady: function(taskId) { /* 非必须 */
// console.log(taskId)
// },
onHashProgress(progressData) { /* 非必须 */
if (!bar) {
bar = createProgressBar(fileName, len, progressData.total)
prevLoaded = 0
}
bar.tick(progressData.loaded - prevLoaded, {
status: '校验中',
speed: sizeFormate(progressData.speed) + '/s',
})
prevLoaded = progressData.loaded
// console.log('校验', fileName, JSON.stringify(progressData))
// console.log('校验', JSON.stringify(progressData))
},
onProgress(progressData) { /* 非必须 */
if (!bar) {
bar = createProgressBar(fileName, len, progressData.total)
prevLoaded = 0
}
bar.tick(progressData.loaded - prevLoaded, {
status: '上传中',
speed: sizeFormate(progressData.speed) + '/s',
})
prevLoaded = progressData.loaded
// console.log('上传', fileName, JSON.stringify(progressData))
// console.log('上传', JSON.stringify(progressData))
},
}, (err, data) => {
if (err) {
console.log(err)
return reject(err)
}
bar.tick({
status: '已完成',
speed: '',
})
resolve(data)
})
})
module.exports = async() => {
console.log(chalk.blue('正在获取COS文件列表...'))
const cosFiles = await getCosFileList()
console.log(chalk.green('COS文件列表获取成功'))
const uploadFiles = getLocalFileList()
const removeFiles = diffFileList(uploadFiles, cosFiles)
if (removeFiles.length) {
console.log(chalk.blue('共需删除') + chalk.yellow(removeFiles.length) + chalk.blue('个文件'))
console.log(chalk.blue('正在从COS删除多余的文件...'))
await deleteCosFiles(removeFiles)
console.log(chalk.green('多余文件删除成功'))
} else {
console.log(chalk.blue('没有在COS发现多余的文件'))
}
if (uploadFiles.length) {
console.log(chalk.blue('共需上传') + chalk.green(uploadFiles.length) + chalk.blue('个文件'))
console.log(chalk.blue('正在上传新文件到COS...'))
let max = Math.max(...uploadFiles.map(f => f.length)) + 2
let tasks = uploadFiles.map(f => uploadFile(f, max))
await Promise.all(tasks)
console.log(''.padEnd(Math.max(2, tasks.length - 2), '\n'))
console.log(chalk.green('所有文件上传完成'))
} else {
console.log(chalk.blue('没有需要上传的文件'))
}
}
+8
View File
@@ -0,0 +1,8 @@
module.exports = {
secretId: '',
secretKey: '',
bucket: '', // 存储桶
region: '', // 区域
prefix: '', // 路径
}
+56
View File
@@ -0,0 +1,56 @@
const fs = require('fs')
const ghRelease = require('gh-release')
const token = require('./githubToken')
const pkg = require('../../package.json')
const { jp } = require('./index')
const changeLog = fs.readFileSync(jp('../changeLog.md'), 'utf-8')
const assetsDir = '../assets'
const getBuildFiles = () => {
const files = []
const pathRegExp = [
/latest\.yml$/,
/\.exe$/,
/\.blockmap$/,
]
const names = fs.readdirSync(jp(assetsDir), 'utf8')
names.forEach(name => {
pathRegExp.forEach(regexp => {
if (regexp.test(name)) files.push(jp(assetsDir, name))
})
})
return files
}
// all options have defaults and can be omitted
const options = {
tag_name: `v${pkg.version}`,
target_commitish: 'master',
name: `v${pkg.version}`,
body: changeLog,
draft: false,
prerelease: false,
repo: pkg.name,
owner: pkg.author,
endpoint: 'https://api.github.com', // for GitHub enterprise, use http(s)://hostname/api/v3
auth: {
token,
},
assets: getBuildFiles(),
}
module.exports = ({ isDraft = false, isPrerelease = false, target_commitish = 'master' }) => new Promise((resolve, reject) => {
options.target_commitish = target_commitish
options.draft = isDraft
options.prerelease = isPrerelease
ghRelease(options, function(err, result) {
if (err) return reject(err)
resolve(result)
console.log(result) // create release response: https://developer.github.com/v3/repos/releases/#response-4
})
})
+62
View File
@@ -0,0 +1,62 @@
const fs = require('fs')
const path = require('path')
exports.jp = (...p) => p.length ? path.join(__dirname, ...p) : __dirname
exports.copyFile = (source, target) => new Promise((resolve, reject) => {
const rd = fs.createReadStream(source)
rd.on('error', err => reject(err))
const wr = fs.createWriteStream(target)
wr.on('error', err => reject(err))
wr.on('close', () => resolve())
rd.pipe(wr)
})
/**
* 时间格式化
* @param {Date} d 格式化的时间
* @param {boolean} b 是否精确到秒
*/
exports.formatTime = (d, b) => {
const _date = d == null ? new Date() : typeof d == 'string' ? new Date(d) : d
const year = _date.getFullYear()
const month = fm(_date.getMonth() + 1)
const day = fm(_date.getDate())
if (!b) return year + '-' + month + '-' + day
return year + '-' + month + '-' + day + ' ' + fm(_date.getHours()) + ':' + fm(_date.getMinutes()) + ':' + fm(_date.getSeconds())
}
function fm(value) {
if (value < 10) return '0' + value
return value
}
exports.sizeFormate = size => {
// https://gist.github.com/thomseddon/3511330
if (!size) return '0 b'
let units = ['b', 'kB', 'MB', 'GB', 'TB']
let number = Math.floor(Math.log(size) / Math.log(1024))
return `${(size / Math.pow(1024, Math.floor(number))).toFixed(2)} ${units[number]}`
}
exports.parseArgv = argv => {
const params = {}
argv.forEach(item => {
const argv = item.split('=')
switch (argv[0]) {
case 'ver':
params.ver = argv[1]
break
case 'draft':
params.isDraft = argv[1] === 'true' || argv[1] === undefined
break
case 'prerelease':
params.isPrerelease = argv[1] === 'true' || argv[1] === undefined
break
case 'target_commitish':
params.target_commitish = argv[1]
break
}
})
return params
}
+10
View File
@@ -0,0 +1,10 @@
const builder = require('electron-builder')
const chalk = require('chalk')
// Promise is returned
module.exports = () => builder.build().catch(error => {
console.log(error)
console.log(chalk.red('Asset build failed.'))
return Promise.reject(error)
})
+64
View File
@@ -0,0 +1,64 @@
const fs = require('fs')
const { jp, formatTime } = require('./index')
const pkgDir = '../../package.json'
const pkg = require(pkgDir)
const version = require('../version.json')
const chalk = require('chalk')
const pkg_bak = JSON.stringify(pkg, null, 2)
const version_bak = JSON.stringify(version, null, 2)
const parseChangelog = require('changelog-parser')
const changelogPath = jp('../../CHANGELOG.md')
const md_renderer = markdownStr => new (require('markdown-it'))({
html: true,
linkify: true,
typographer: true,
breaks: true,
}).render(markdownStr)
const getPrevVer = () => parseChangelog(changelogPath).then(res => {
if (!res.versions.length) throw new Error('CHANGELOG 无法解析到版本号')
return res.versions[0].version
})
const updateChangeLog = async(newVerNum, newChangeLog) => {
let changeLog = fs.readFileSync(changelogPath, 'utf-8')
const prevVer = await getPrevVer()
const log = `## [${newVerNum}](${pkg.repository.url.replace(/^git\+(http.+)\.git$/, '$1')}/compare/v${prevVer}...v${newVerNum}) - ${formatTime()}\n\n${newChangeLog}`
fs.writeFileSync(changelogPath, changeLog.replace(new RegExp(`(## [?0.1.1]?)`), log + '\n$1'), 'utf-8')
}
const renderChangeLog = md => md_renderer(md)
module.exports = async newVerNum => {
if (!newVerNum) {
let verArr = pkg.version.split('.')
verArr[verArr.length - 1] = parseInt(verArr[verArr.length - 1]) + 1
newVerNum = verArr.join('.')
}
const newMDChangeLog = fs.readFileSync(jp('../changeLog.md'), 'utf-8')
const newChangeLog = renderChangeLog(newMDChangeLog)
version.history.unshift({
version: version.version,
desc: version.desc,
})
version.version = newVerNum
version.desc = newChangeLog
pkg.version = newVerNum
console.log(chalk.blue('new version: ') + chalk.green(newVerNum))
fs.writeFileSync(jp('../version.json'), JSON.stringify(version, null, 2) + '\n', 'utf-8')
fs.writeFileSync(jp(pkgDir), JSON.stringify(pkg, null, 2) + '\n', 'utf-8')
await updateChangeLog(newVerNum, newMDChangeLog)
return {
pkg_bak,
version_bak,
changeLog: newChangeLog,
}
}
+122
View File
@@ -0,0 +1,122 @@
{
"version": "0.8.1",
"desc": "<h4>修复</h4>\n<ul>\n<li>修复酷我源搜索歌曲结果未添加128k音质导致播放128k音质时显示“该歌曲没有可播放的音频”的问题</li>\n</ul>\n",
"history": [
{
"version": "0.8.0",
"desc": "<h4>新增</h4>\n<ul>\n<li>新增网易云源歌曲搜索</li>\n<li>新增网易云源歌单</li>\n<li>新增各平台通过输入歌单链接或歌单ID打开歌单详情列表,目前只适配了<strong>网页版歌单链接</strong>,其他方式的歌单链接可能无法解析,但你可想办法获取歌单ID后输入打开。注:各平台歌单ID均为纯数字,若遇到链接里存在歌单ID但无法解析的歌单链接,可以到GitHub提交issue或发送邮件或加群830125506反馈!</li>\n<li>新增音量调整滑动功能,现在支持鼠标左右拖动调整音量了</li>\n</ul>\n<h4>优化</h4>\n<ul>\n<li>优化搜索框搜索体验</li>\n<li>优化音量条交互视觉效果</li>\n<li>缓存歌单详情列表数据</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复QQ源歌单无法翻页Bug</li>\n<li>修复默认列表没有创建时无法显示收藏列表的Bug</li>\n<li>修复网易云128k直接试听</li>\n<li>修复歌曲音质不存在时仍然播放或下载的Bug</li>\n<li>修复调整音量时,调整的位置与鼠标点击的位置不一致的问题</li>\n</ul>\n"
},
{
"version": "0.7.0",
"desc": "<h4>新增</h4>\n<ul>\n<li>新增“我的收藏”本地播放列表</li>\n<li>新增缓存清理功能,可到<strong>设置-其他</strong>查看与清理软件缓存</li>\n<li>新增QQ音乐源搜索</li>\n<li>新增咪咕源搜索</li>\n<li>新增咪咕源歌单</li>\n<li>新增咪咕源排行榜</li>\n<li>新增我的音乐列表歌曲源显示,默认关闭,可到<strong>设置-列表设置</strong>开启</li>\n</ul>\n<h4>优化</h4>\n<ul>\n<li>优化选择框动画效果</li>\n<li>尝试优化选我的音乐列表内容很多时多选的卡顿问题</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修复列表延迟显示的Bug</li>\n<li>修复QQ音源128k音质试听</li>\n</ul>\n"
},
{
"version": "0.6.2",
"desc": "<p>祝贺祖国成立70周年~!</p>\n<h4>新增</h4>\n<ul>\n<li>新增QQ音乐源歌单</li>\n</ul>\n<h4>修复</h4>\n<ul>\n<li>修正火影皮肤名字</li>\n<li>修复当试听列表为空时,无法切到其他界面的Bug</li>\n<li>修复百度源搜索结果为空时的接口处理Bug</li>\n<li>恢复<strong>酷狗</strong>其他音质播放</li>\n</ul>\n"
},
{
"version": "0.6.1",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增试听列表<strong>滚动条位置恢复</strong>设置(可自动恢复到上次离开时的列表滚动位置),本功能默认开启,若不需要可到设置-列表设置将其关闭</li>\n<li>新增 <strong>《海贼王》</strong> 皮肤,喜欢个性化的可以试试~</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>新增DNS解析缓存,加快请求速度</li>\n<li>优化代码逻辑,减少软件对系统资源的占用</li>\n<li>优化新版本信息检测,尽量减少弹出版本获取失败弹窗弹出的概率</li>\n<li>优化下拉列表动画效果</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复请求超时的逻辑处理Bug,尝试修复请求无法取消导致的正在播放的歌曲与界面显示的信息不一致的问题</li>\n<li>修复其他一些小Bug</li>\n</ul>\n<h3>移除</h3>\n<ul>\n<li>移除 <code>192k</code> 音质</li>\n<li>移除酷我音源 <code>ape</code> 音质,无损推荐 <code>flac</code> 格式</li>\n</ul>\n"
},
{
"version": "0.6.0",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增音乐<strong>聚合搜索</strong>,目前支持酷我、酷狗、百度源搜索</li>\n<li>新增代理功能</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>优化从《梦里嫦娥》皮肤切换到其他皮肤时侧栏动画的切换效果</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复试听列表没有歌曲时会显示列表加载中的Bug</li>\n<li>修复切换歌单列表详情时的UI Bug</li>\n</ul>\n"
},
{
"version": "0.5.5",
"desc": "<h3>新增</h3>\n<ul>\n<li>月是故乡明,祝大家中秋快乐🥮~~新增个性皮肤**《月里嫦娥》**,时间仓促,皮肤还不是很完善,可以试试喜不喜欢~😉</li>\n<li>新增 MAC 版本退出快捷键支持</li>\n<li>新增点击播放器中的歌曲标题可以复制标题的功能(遇到好听的歌曲方便分享)</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复 MAC 系统下软件关闭时再次从 dock 打开时报错的Bug</li>\n<li>修复下载的歌曲文件名中包含命名规则不允许的符号时下载失败的问题(若歌曲名包含这些符号会自动将其移除)</li>\n<li>修复 MAC 版本不能复制粘贴的问题</li>\n</ul>\n"
},
{
"version": "0.5.4",
"desc": "<h3>移除</h3>\n<ul>\n<li>下载的FLAC文件在修改歌曲信息后,软件无法播放,但使用本地播放器可以播放</li>\n<li>为了稳妥起见,暂时移除FLAC格式的meta信息修改</li>\n<li>MP3格式无此问题</li>\n</ul>\n"
},
{
"version": "0.5.3",
"desc": "<h3>优化</h3>\n<ul>\n<li>更新所有依赖包到最新</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复试听酷狗源的音乐仍然获取320k音质导致获取失败的Bug</li>\n</ul>\n"
},
{
"version": "0.5.2",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增强迫症设置-离开搜索界面时是否清空搜索框</li>\n<li>设置-关于板块新增常见问题链接</li>\n<li>歌单左上角的分类按钮添加一个<strong>向下图标</strong>,方便识别该按钮为下拉框(该按钮可选择歌单类型,请自行尝试)</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>略微优化最小化按钮字符</li>\n<li>优化试听列表的加载体验,当歌曲数过多时列表将延迟加载</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复下载管理的一些Bug</li>\n</ul>\n<h3>移除</h3>\n<ul>\n<li>因接口失效,移除网易云音源,酷狗音源仅支持播放128k音质</li>\n</ul>\n"
},
{
"version": "0.5.1",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增右上角最小化/关闭按钮鼠标滑过符号</li>\n<li>新增下载列表定位文件按钮</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复百度源歌单全部分类无法加载的问题</li>\n<li>修复更新弹窗无法弹出的问题</li>\n</ul>\n"
},
{
"version": "0.5.0",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>封面嵌入</strong>(默认开启,可到设置-下载设置关闭)</li>\n<li>新增<strong>歌词下载</strong>(默认关闭,可到设置-下载设置开启)</li>\n<li>新增单例应用功能(实现软件单开功能,禁止软件多开)</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>优化歌单列表动画</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复歌单无法翻页的问题</li>\n<li>修复在某些情况下,添加下载歌曲导致下载列表崩溃的问题</li>\n<li>修复版本更新弹窗Bug</li>\n<li>修复酷狗歌单推荐歌单出现在其他分类中的Bug</li>\n</ul>\n"
},
{
"version": "0.4.0",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>歌单</strong>功能,目前支持酷我、酷狗、百度源歌单</li>\n<li>在设置界面-关于洛雪音乐说明部分新增<strong>最新版网盘下载地址</strong>与<strong>打赏地址</strong></li>\n<li>新增酷狗 电音热歌榜、DJ热歌榜</li>\n<li>新增版本更新超时功能,对于部分无法访问GitHub的用户做更新超时提醒</li>\n</ul>\n<h3>移除</h3>\n<ul>\n<li><strong>注意</strong>:0.4.0以前的版本即将失效,请更新到0.4.0版本</li>\n</ul>\n"
},
{
"version": "0.3.5",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>测试接口</strong>,该接口同样速度较慢,但软件的大部分功能可用,<strong>请自行切换到该接口</strong>,找接口辛苦,且用且珍惜!</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>取消需要刷新URL时windows任务栏进度显示错误状态(现显示为暂停状态)</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复使用临时接口时在试听列表双击灰色歌曲仍然会进行播放的Bug</li>\n<li>修复歌词加载Bug</li>\n</ul>\n"
},
{
"version": "0.3.4",
"desc": "<h3>优化</h3>\n<ul>\n<li>减少接口不稳定带来的影响,适当增加请求等待时间</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复播放过程中URL过期不会刷新URL的问题</li>\n</ul>\n"
},
{
"version": "0.3.3",
"desc": "<h3>修复</h3>\n<ul>\n<li><strong>messoer</strong>的接口已经关闭,暂时切换到临时接口使用,部分功能受限。。。</li>\n<li>修复设置界面更新出错时仍然显示更新下载中的问题</li>\n<li>修复手动定位播放进度条时存在偏差的问题</li>\n<li>屏蔽播放器中没有歌曲时对进度条的点击</li>\n</ul>\n"
},
{
"version": "0.3.2",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增酷狗排行榜其他音质下载</li>\n</ul>\n"
},
{
"version": "0.3.1",
"desc": "<h3>修复</h3>\n<ul>\n<li>修复音量条主题适配</li>\n</ul>\n"
},
{
"version": "0.3.0",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>MAC</strong>及<strong>Linux</strong>版本(需要的可自行下载)</li>\n<li>新增音量调整</li>\n<li>新增任务栏播放进度条控制选项(现在可在设置界面关闭在任务栏显示的播放进度)</li>\n<li>新增更新出错时的弹窗提示</li>\n<li>从该版本起,非安装版也会有更新弹窗提醒了,但仍然需要手动下载新版本更新,版本信息可到设置页面查看</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>强制把临时接口设置回 <code>messoer</code> 接口</li>\n</ul>\n"
},
{
"version": "0.2.3",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增任务栏程序标题改变功能(播放歌曲时任务栏标题将显示当前播放的歌曲)</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>使用临时接口时,试听列表中的下载按钮仍然能点击的Bug</li>\n<li>修复某些情况下歌曲链接未能缓存的问题</li>\n</ul>\n<h3>移除</h3>\n<ul>\n<li>移除临时接口(因服务器被攻击,本接口已关闭)</li>\n<li>移除列表栏设置的隐藏专辑栏选项(感觉这个设置并没有什么luan用,并且还会打破布局)</li>\n</ul>\n"
},
{
"version": "0.2.2",
"desc": "<h3>修复</h3>\n<ul>\n<li>修复下载过程中出错重试5次都失败后不会自动开始下一个任务的Bug</li>\n<li>修复播放到一半URL过期时不会刷新URL直接播放下一首的问题</li>\n</ul>\n"
},
{
"version": "0.2.1",
"desc": "<h3>优化</h3>\n<ul>\n<li>新增歌曲URL存储,当URL无效时才重新获取,以减少接口不稳定的影响</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复歌曲加载无法加载时自动切换混乱的Bug</li>\n<li>修复移除列表最后一首歌曲时播放器不停止播放的问题</li>\n</ul>\n"
},
{
"version": "0.2.0",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>百度音乐</strong>排行榜及其音乐直接试听与下载</li>\n<li>新增网易云排行榜音乐直接试听与下载(目前仅支持128k音质)</li>\n<li>新增酷狗排行榜音乐直接试听与下载(目前仅支持128k音质)</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复更新弹窗历史版本描述多余的换行问题</li>\n<li>修复歌曲无法播放的情况下歌词仍会播放的问题</li>\n</ul>\n"
},
{
"version": "0.1.6",
"desc": "<h3>修复</h3>\n<ul>\n<li>修复列表多选音源限制Bug</li>\n</ul>\n"
},
{
"version": "0.1.5",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增搜索列表批量试听与下载功能</li>\n<li>新增排行榜列表批量试听与下载功能</li>\n<li>新增试听列表批量移除与下载功能</li>\n<li>新增下载列表批量开始、暂停与移除功能</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>优化歌曲切换机制</li>\n</ul>\n"
},
{
"version": "0.1.4",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增音乐来源切换,可到设置页面-基本设置 look look !</li>\n<li>为搜索结果列表添加多选功能。<br>\nP.S:暂时没想好多选后的操作按钮放哪…</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>重构与改进checkbox组件,使其支持不定选中状态</li>\n<li>完善上一个版本的http请求封装并切换部分请求到该方法上</li>\n<li>优化其他一些细节</li>\n</ul>\n"
},
{
"version": "0.1.3",
"desc": "<h3>新增</h3>\n<ul>\n<li>新增win32应用构建</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复安装包许可协议乱码问题</li>\n<li><strong>messoer 提供的接口已挂</strong>,暂时切换到临时接口!</li>\n</ul>\n<h3>移除</h3>\n<ul>\n<li>由于messoer接口无法使用,QQ音乐排行榜直接播放/下载功能暂时关闭</li>\n</ul>\n"
},
{
"version": "0.1.2",
"desc": "<h3>修复</h3>\n<ul>\n<li>修复更新弹窗的内容显示问题</li>\n</ul>\n"
},
{
"version": "0.1.1",
"desc": "<h3>新增</h3>\n<ul>\n<li>QQ音乐排行榜直接试听与下载(该接口貌似不太稳定,且用且珍惜!)</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>优化http请求机制</li>\n<li>更新关于本软件说明</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复当上一个歌曲链接正在获取时切换歌曲请求不会取消的问题</li>\n<li>修复切换歌曲时仍然播放上一首歌曲的问题</li>\n</ul>\n"
},
{
"version": "0.1.0",
"desc": "0.1.0版本发布"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+19
View File
@@ -0,0 +1,19 @@
const { ipcMain, ipcRenderer } = require('electron')
export const mainSend = (name, params) => {
ipcMain.send(name, params)
}
export const mainOn = (name, callback) => {
ipcMain.on(name, callback)
}
export const rendererSend = (name, params) => {
ipcRenderer.send(name, params)
}
export const rendererOn = (name, callback) => {
ipcRenderer.on(name, callback)
}
+8
View File
@@ -0,0 +1,8 @@
const log = require('electron-log')
exports.isLinux = process.platform == 'linux'
exports.isWin = process.platform == 'win32'
exports.isMac = process.platform == 'darwin'
exports.log = log
+15
View File
@@ -0,0 +1,15 @@
html(lang="cn")
head
meta(charset="UTF-8")
meta(name="viewport" content="width=device-width, initial-scale=1.0")
meta(http-equiv="X-UA-Compatible" content="ie=edge")
title 洛雪音乐助手
body
#root
//- if htmlWebpackPlugin.options.isProd
//- script.
//- window.__static = '!{require('path').join(htmlWebpackPlugin.options.__dirname, '/static').replace(/\\/g, '\\\\')}'
//- if !htmlWebpackPlugin.options.browser && htmlWebpackPlugin.options.isProd
script.
window.__static = require('path').join(__dirname, '/resources').replace(/\\/g, '\\\\')
+13
View File
@@ -0,0 +1,13 @@
const { mainOn } = require('../../common/icp')
const { app } = require('electron')
const { name: defaultName } = require('../../../package.json')
mainOn('appName', (event, params) => {
if (params == null) {
app.setName(defaultName)
} else {
app.setName(params.name)
}
})
+6
View File
@@ -0,0 +1,6 @@
require('./request')
// require('./appName')
require('./progressBar')
require('./trafficLight')
require('./musicMeta')
+6
View File
@@ -0,0 +1,6 @@
const { mainOn } = require('../../common/icp')
const { setMeta } = require('../utils/musicMeta')
mainOn('setMusicMeta', (event, { filePath, meta }) => {
setMeta(filePath, meta)
})
+10
View File
@@ -0,0 +1,10 @@
const { mainOn } = require('../../common/icp')
mainOn('progress', (event, params) => {
// console.log(params)
global.mainWindow && global.mainWindow.setProgressBar(params.status, {
mode: params.mode || 'normal',
})
})
+45
View File
@@ -0,0 +1,45 @@
const request = require('request')
const { mainOn } = require('../../common/icp')
const tasks = []
mainOn('request', (event, options) => {
// console.log(args)
if (!options) return
let index = fetchData(options, (err, resp) => {
tasks[index] = null
if (err) {
console.log(err)
event.sender.send('response', err.message, null)
return
}
event.sender.send('response', null, resp.body)
})
event.returnValue = index
})
mainOn('cancelRequest', (event, index) => {
if (index == null) return
let r = tasks[index]
if (r == null) return
r.abort()
tasks[index] = null
})
const fetchData = (options, callback) => pushTask(tasks, request(options.url, {
method: options.method,
headers: options.headers,
Origin: options.origin,
}, (err, resp) => {
if (err) return callback(err, null)
callback(null, resp)
}))
const pushTask = (tasks, newTask) => {
for (const [index, task] of tasks.entries()) {
if (task == null) {
return tasks[index].push(newTask)
}
}
}
+11
View File
@@ -0,0 +1,11 @@
const { mainOn } = require('../../common/icp')
const { dialog } = require('electron')
module.exports = win => {
mainOn('selectPath', (event, params) => {
let path = dialog.showOpenDialog(win, params.options)
if (path === undefined) return
event.sender.send(params.eventName, path)
})
}
+22
View File
@@ -0,0 +1,22 @@
// const { app } = require('electron')
const { mainOn } = require('../../common/icp')
mainOn('min', event => {
if (global.mainWindow) {
global.mainWindow.minimize()
}
})
// mainOn('max', event => {
// if (global.mainWindow) {
// global.mainWindow.maximize()
// }
// })
mainOn('close', event => {
if (global.mainWindow) {
// global.mainWindowdow.destroy()
// console.log('close')
// app.quit()
global.mainWindow.close()
}
})
+25
View File
@@ -0,0 +1,25 @@
/**
* This file is used specifically and only for development. It installs
* `electron-debug` & `vue-devtools`. There shouldn't be any need to
* modify this file, but it can be used to extend your development
* environment.
*/
const electron = require('electron')
const electronDebug = require('electron-debug')
const { default: installExtension, VUEJS_DEVTOOLS } = require('electron-devtools-installer')
// Install `electron-debug` with `devtron`
electronDebug({
showDevTools: true,
devToolsMode: 'undocked',
})
// Install `vue-devtools`
electron.app.on('ready', () => {
installExtension(VUEJS_DEVTOOLS)
.then(name => console.log(`Added Extension: ${name}`))
.catch(err => console.log('An error occurred: ', err))
})
// Require `main` process to boot app
require('./index')
+130
View File
@@ -0,0 +1,130 @@
const { app, BrowserWindow, Menu } = require('electron')
const path = require('path')
// 单例应用程序
if (!app.requestSingleInstanceLock()) {
app.quit()
return
}
app.on('second-instance', (event, argv, cwd) => {
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
} else {
app.quit()
}
})
require('./events')
const autoUpdate = require('./utils/autoUpdate')
const { isLinux, isMac } = require('../common/utils')
const isDev = process.env.NODE_ENV !== 'production'
/**
* Set `__static` path to static files in production
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
*/
let mainWindow
let winURL
let isFirstCheckedUpdate = true
if (isDev) {
global.__static = path.join(__dirname, '../static')
winURL = 'http://localhost:9080'
} else {
global.__static = path.join(__dirname, '/static')
winURL = `file://${__dirname}/index.html`
}
function createWindow() {
/**
* Initial window options
*/
mainWindow = global.mainWindow = new BrowserWindow({
height: 590,
useContentSize: true,
width: 920,
frame: false,
transparent: !isLinux,
// icon: path.join(global.__static, isWin ? 'icons/256x256.ico' : 'icons/512x512.png'),
resizable: false,
maximizable: false,
fullscreenable: false,
webPreferences: {
// contextIsolation: true,
webSecurity: !isDev,
nodeIntegration: true,
},
})
mainWindow.loadURL(winURL)
mainWindow.on('close', () => {
mainWindow.setProgressBar(-1)
})
mainWindow.on('closed', () => {
mainWindow = global.mainWindow = null
})
// mainWindow.webContents.openDevTools()
if (!isDev) {
autoUpdate(isFirstCheckedUpdate)
isFirstCheckedUpdate = false
}
}
if (isMac) {
const template = [
{
label: app.getName(),
submenu: [
{ label: '关于洛雪音乐', role: 'about' },
{ type: 'separator' },
{ label: '隐藏', role: 'hide' },
{ label: '显示其他', role: 'hideothers' },
{ label: '显示全部', role: 'unhide' },
{ type: 'separator' },
{ label: '退出', accelerator: 'Command+Q', click: () => app.quit() },
],
},
{
label: '窗口',
role: 'window',
submenu: [
{ label: '最小化', role: 'minimize' },
{ label: '关闭', role: 'close' },
],
},
{
label: '编辑',
submenu: [
{ label: '撤销', accelerator: 'CmdOrCtrl+Z', role: 'undo' },
{ label: '恢复', accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' },
{ type: 'separator' },
{ label: '剪切', accelerator: 'CmdOrCtrl+X', role: 'cut' },
{ label: '复制', accelerator: 'CmdOrCtrl+C', role: 'copy' },
{ label: '粘贴', accelerator: 'CmdOrCtrl+V', role: 'paste' },
{ label: '选择全部', accelerator: 'CmdOrCtrl+A', role: 'selectAll' },
],
},
]
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
} else {
Menu.setApplicationMenu(null)
}
app.on('ready', createWindow)
app.on('window-all-closed', () => {
if (!isMac) app.quit()
})
app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
})
+112
View File
@@ -0,0 +1,112 @@
const { log } = require('../../common/utils')
const { autoUpdater } = require('electron-updater')
const { mainOn } = require('../../common/icp')
autoUpdater.logger = log
// autoUpdater.autoDownload = false
autoUpdater.logger.transports.file.level = 'info'
log.info('App starting...')
// -------------------------------------------------------------------
// Open a window that displays the version
//
// THIS SECTION IS NOT REQUIRED
//
// This isn't required for auto-updates to work, but it's easier
// for the app to show a window than to have to click "About" to see
// that updates are working.
// -------------------------------------------------------------------
// let win
function sendStatusToWindow(text) {
log.info(text)
// global.mainWindow.webContents.send('message', text)
}
// -------------------------------------------------------------------
// Auto updates
//
// For details about these events, see the Wiki:
// https://github.com/electron-userland/electron-builder/wiki/Auto-Update#events
//
// The app doesn't need to listen to any events except `update-downloaded`
//
// Uncomment any of the below events to listen for them. Also,
// look in the previous section to see them being used.
// -------------------------------------------------------------------
// autoUpdater.on('checking-for-update', () => {
// })
// autoUpdater.on('update-available', (ev, info) => {
// })
// autoUpdater.on('update-not-available', (ev, info) => {
// })
// autoUpdater.on('error', (ev, err) => {
// })
// autoUpdater.on('download-progress', (ev, progressObj) => {
// })
// autoUpdater.on('update-downloaded', (ev, info) => {
// // Wait 5 seconds, then quit and install
// // In your application, you don't need to wait 5 seconds.
// // You could call autoUpdater.quitAndInstall(); immediately
// // setTimeout(function() {
// // autoUpdater.quitAndInstall()
// // }, 5000)
// })
let waitEvent = []
const handleSendEvent = action => {
if (global.mainWindow) {
setTimeout(() => { // 延迟发送事件,过早发送可能渲染进程还没启动完成
global.mainWindow.webContents.send(action.type, action.info)
}, 2000)
} else {
waitEvent.push(action)
}
}
module.exports = isFirstCheckedUpdate => {
if (!isFirstCheckedUpdate) {
if (waitEvent.length) {
waitEvent.forEach((event, index) => {
setTimeout(() => { // 延迟发送事件,过早发送可能渲染进程还没启动完成
global.mainWindow.webContents.send(event.type, event.info)
}, 2000 * (index + 1))
})
waitEvent = []
}
return
}
autoUpdater.on('checking-for-update', () => {
sendStatusToWindow('Checking for update...')
})
autoUpdater.on('update-available', info => {
sendStatusToWindow('Update available.')
handleSendEvent({ type: 'update-available', info })
})
autoUpdater.on('update-not-available', info => {
sendStatusToWindow('Update not available.')
handleSendEvent({ type: 'update-not-available' })
})
autoUpdater.on('error', () => {
sendStatusToWindow('Error in auto-updater.')
handleSendEvent({ type: 'update-error' })
})
autoUpdater.on('download-progress', progressObj => {
sendStatusToWindow('Download progress...')
})
autoUpdater.on('update-downloaded', info => {
sendStatusToWindow('Update downloaded.')
handleSendEvent({ type: 'update-downloaded' })
})
mainOn('quit-update', () => {
setTimeout(() => {
autoUpdater.quitAndInstall(true, true)
}, 1000)
})
autoUpdater.checkForUpdates()
}
+38
View File
@@ -0,0 +1,38 @@
const fs = require('fs')
const flac = require('flac-metadata')
module.exports = (filenPath, meta) => {
const reader = fs.createReadStream(filenPath)
const tempPath = filenPath + '.lxmtemp'
const writer = fs.createWriteStream(tempPath)
const processor = new flac.Processor()
if (meta.APIC) delete meta.APIC
const comments = []
for (const key in meta) {
comments.push(`${key.toUpperCase()}=${meta[key]}`)
}
const vendor = 'lx-music-desktop'
processor.on('preprocess', function(mdb) {
// Remove existing VORBIS_COMMENT block, if any.
if (mdb.type === flac.Processor.MDB_TYPE_VORBIS_COMMENT) {
mdb.remove()
}
// Inject new VORBIS_COMMENT block.
if (mdb.removed || mdb.isLast) {
let mdbVorbis = flac.data.MetaDataBlockVorbisComment.create(mdb.isLast, vendor, comments)
this.push(mdbVorbis.publish())
}
})
reader.pipe(processor).pipe(writer).on('finish', () => {
fs.unlink(filenPath, err => {
if (err) return console.log(err.message)
fs.rename(tempPath, filenPath, err => {
if (err) console.log(err.message)
})
})
})
}
+17
View File
@@ -0,0 +1,17 @@
const NodeID3 = require('node-id3')
const path = require('path')
const fs = require('fs')
const request = require('request')
const extReg = /^(\.(?:jpe?g|png)).*$/
module.exports = (filePath, meta) => {
if (!meta.APIC) return NodeID3.write(meta, filePath)
let picPath = filePath.replace(/\.mp3$/, '') + path.extname(meta.APIC).replace(extReg, '$1')
request(meta.APIC).pipe(fs.createWriteStream(picPath)).on('finish', () => {
meta.APIC = picPath
NodeID3.write(meta, filePath)
fs.unlink(picPath, err => {
if (err) console.log(err.message)
})
})
}
+14
View File
@@ -0,0 +1,14 @@
const path = require('path')
const mp3Meta = require('./mp3Meta')
const flacMeta = require('./flacMeta')
exports.setMeta = (filePath, meta) => {
switch (path.extname(filePath)) {
case '.mp3':
mp3Meta(filePath, meta)
break
case '.flac':
flacMeta(filePath, meta)
break
}
}
+275
View File
@@ -0,0 +1,275 @@
<template lang="pug">
#container(v-if="isProd && !isLinux" :class="theme" @mouseenter="enableIgnoreMouseEvents" @mouseleave="dieableIgnoreMouseEvents")
core-aside#left
#right
core-toolbar#toolbar
core-view#view
core-player#player
core-icons
material-version-modal(v-show="version.showModal")
#container(v-else :class="theme")
core-aside#left
#right
core-toolbar#toolbar
core-view#view
core-player#player
core-icons
material-version-modal(v-show="version.showModal")
</template>
<script>
import dnscache from 'dnscache'
import { mapMutations, mapGetters, mapActions } from 'vuex'
import { rendererOn } from '../common/icp'
import { isLinux } from '../common/utils'
window.ELECTRON_DISABLE_SECURITY_WARNINGS = process.env.ELECTRON_DISABLE_SECURITY_WARNINGS
dnscache({
enable: true,
ttl: 21600,
cachesize: 1000,
})
let win
let body
if (!isLinux) {
win = require('electron').remote.getCurrentWindow()
body = document.body
}
export default {
data() {
return {
isProd: process.env.NODE_ENV === 'production',
isLinux,
globalObj: {
apiSource: 'test',
proxy: {},
},
updateTimeout: null,
}
},
computed: {
...mapGetters(['electronStore', 'setting', 'theme', 'version']),
...mapGetters('list', ['defaultList', 'loveList']),
...mapGetters('download', {
downloadList: 'list',
downloadStatus: 'downloadStatus',
}),
},
mounted() {
document.body.classList.add(this.isLinux ? 'noTransparent' : 'transparent')
this.init()
},
watch: {
setting: {
handler(n) {
this.electronStore.set('setting', n)
},
deep: true,
},
defaultList: {
handler(n) {
// console.log(n)
this.electronStore.set('list.defaultList', n)
},
deep: true,
},
loveList: {
handler(n) {
// console.log(n)
this.electronStore.set('list.loveList', n)
},
deep: true,
},
downloadList: {
handler(n) {
this.electronStore.set('download.list', n)
},
deep: true,
},
'globalObj.apiSource'(n) {
if (n != this.setting.apiSource) {
this.setSetting(Object.assign({}, this.setting, {
apiSource: n,
}))
}
},
},
methods: {
...mapActions(['getVersionInfo']),
...mapMutations(['setNewVersion', 'setVersionModalVisible']),
...mapMutations('list', ['initList']),
...mapMutations('download', ['updateDownloadList']),
...mapMutations(['setSetting']),
init() {
if (this.isProd && !isLinux) {
body.addEventListener('mouseenter', this.dieableIgnoreMouseEvents)
body.addEventListener('mouseleave', this.enableIgnoreMouseEvents)
}
rendererOn('update-available', (e, info) => {
// this.showUpdateModal(true)
this.setNewVersion({
version: info.version,
})
})
rendererOn('update-error', () => {
if (!this.updateTimeout) return
this.setVersionModalVisible({ isError: true })
this.clearUpdateTimeout()
this.$nextTick(() => {
this.showUpdateModal()
})
})
rendererOn('update-downloaded', () => {
this.clearUpdateTimeout()
this.setVersionModalVisible({ isError: false })
this.showUpdateModal()
})
rendererOn('update-not-available', () => {
if (!this.updateTimeout) return
if (this.setting.ignoreVersion) this.setSetting(Object.assign({}, this.setting, { ignoreVersion: null }))
this.clearUpdateTimeout()
this.setNewVersion({
version: this.version.version,
})
})
// 更新超时定时器
this.updateTimeout = setTimeout(() => {
this.updateTimeout = null
this.setVersionModalVisible({ isError: true })
this.$nextTick(() => {
this.showUpdateModal()
})
}, 180000)
this.initData()
this.globalObj.apiSource = this.setting.apiSource
this.globalObj.proxy = Object.assign({}, this.setting.network.proxy)
window.globalObj = this.globalObj
},
enableIgnoreMouseEvents() {
if (isLinux) return
win.setIgnoreMouseEvents(false)
// console.log('content enable')
},
dieableIgnoreMouseEvents() {
if (isLinux) return
// console.log('content disable')
win.setIgnoreMouseEvents(true, { forward: true })
},
initData() { // 初始化数据
this.initPlayList() // 初始化播放列表
this.initDownloadList() // 初始化下载列表
},
initPlayList() {
let defaultList = this.electronStore.get('list.defaultList')
let loveList = this.electronStore.get('list.loveList')
this.initList({ defaultList, loveList })
},
initDownloadList() {
let downloadList = this.electronStore.get('download.list')
if (downloadList) {
downloadList.forEach(item => {
if (item.status == this.downloadStatus.RUN || item.status == this.downloadStatus.WAITING) {
item.status = this.downloadStatus.PAUSE
item.statusText = '暂停下载'
}
})
this.updateDownloadList(downloadList)
}
},
showUpdateModal() {
(this.version.newVersion && this.version.newVersion.history ? Promise.resolve(this.version.newVersion) : this.getVersionInfo().then(body => {
this.setNewVersion(body)
if (body.version !== this.setting.ignoreVersion) this.setSetting(Object.assign({}, this.setting, { ignoreVersion: null }))
return body
})).then(body => {
if (body.version === this.version.version) return
if (this.version.isError && body.version === this.setting.ignoreVersion) return
this.$nextTick(() => {
this.setVersionModalVisible({ isShow: true })
})
})
},
clearUpdateTimeout() {
if (!this.updateTimeout) return
clearTimeout(this.updateTimeout)
this.updateTimeout = null
},
},
beforeDestroy() {
this.clearUpdateTimeout()
if (this.isProd) {
body.removeEventListener('mouseenter', this.dieableIgnoreMouseEvents)
body.removeEventListener('mouseleave', this.enableIgnoreMouseEvents)
}
},
}
</script>
<style lang="less">
@import './assets/styles/index.less';
@import './assets/styles/layout.less';
body {
user-select: none;
height: 100vh;
box-sizing: border-box;
}
.transparent {
padding: @shadow-app;
#container {
box-shadow: 0 0 @shadow-app rgba(0, 0, 0, 0.5);
border-radius: 4px;
background-color: transparent;
}
}
.noTransparent {
background-color: #fff;
}
#container {
position: relative;
display: flex;
height: 100%;
overflow: hidden;
background: @color-theme-bgimg @color-theme-bgposition no-repeat;
background-size: @color-theme-bgsize;
transition: background-color @transition-theme;
background-color: @color-theme;
}
#left {
flex: none;
width: @width-app-left;
}
#right {
flex: auto;
display: flex;
flex-flow: column nowrap;
transition: background-color @transition-theme;
background-color: @color-theme_2;
}
#toolbar, #player {
flex: none;
}
#view {
flex: auto;
height: 0;
}
each(@themes, {
#container.@{value} {
background-color: ~'@{color-@{value}-theme}';
background-image: ~'@{color-@{value}-theme-bgimg}';
background-size: ~'@{color-@{value}-theme-bgsize}';
background-position: ~'@{color-@{value}-theme-bgposition}';
#right {
background-color: ~'@{color-@{value}-theme_2}';
}
}
})
</style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

+689
View File
@@ -0,0 +1,689 @@
@keyframes flipInX {
from {
transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
animation-timing-function: ease-in;
opacity: 0;
}
40% {
transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
animation-timing-function: ease-in;
}
60% {
transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
opacity: 1;
}
80% {
transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
}
to {
transform: perspective(400px);
}
}
@keyframes flipOutX {
from {
transform: perspective(400px);
}
30% {
transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
opacity: 1;
}
to {
transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
opacity: 0;
}
}
@keyframes flipInY {
from {
transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
animation-timing-function: ease-in;
opacity: 0;
}
40% {
transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
animation-timing-function: ease-in;
}
60% {
transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
opacity: 1;
}
80% {
transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
}
to {
transform: perspective(400px);
}
}
@keyframes flipOutY {
from {
transform: perspective(400px);
}
30% {
transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
opacity: 1;
}
to {
transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
opacity: 0;
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes bounceIn {
from,
20%,
40%,
60%,
80%,
to {
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
0% {
opacity: 0;
transform: scale3d(0.3, 0.3, 0.3);
}
20% {
transform: scale3d(1.1, 1.1, 1.1);
}
40% {
transform: scale3d(0.9, 0.9, 0.9);
}
60% {
opacity: 1;
transform: scale3d(1.03, 1.03, 1.03);
}
80% {
transform: scale3d(0.97, 0.97, 0.97);
}
to {
opacity: 1;
transform: scale3d(1, 1, 1);
}
}
@keyframes bounceOut {
20% {
transform: scale3d(0.9, 0.9, 0.9);
}
50%,
55% {
opacity: 1;
transform: scale3d(1.1, 1.1, 1.1);
}
to {
opacity: 0;
transform: scale3d(0.3, 0.3, 0.3);
}
}
@keyframes lightSpeedIn {
from {
transform: translate3d(100%, 0, 0) skewX(-30deg);
opacity: 0;
}
60% {
transform: skewX(20deg);
opacity: 1;
}
80% {
transform: skewX(-5deg);
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes lightSpeedOut {
from {
opacity: 1;
}
to {
transform: translate3d(100%, 0, 0) skewX(30deg);
opacity: 0;
}
}
@keyframes rotateIn {
from {
transform-origin: center;
transform: rotate3d(0, 0, 1, -200deg);
opacity: 0;
}
to {
transform-origin: center;
transform: translate3d(0, 0, 0);
opacity: 1;
}
}
@keyframes rotateInDownLeft {
from {
transform-origin: left bottom;
transform: rotate3d(0, 0, 1, -45deg);
opacity: 0;
}
to {
transform-origin: left bottom;
transform: translate3d(0, 0, 0);
opacity: 1;
}
}
@keyframes rotateInDownRight {
from {
transform-origin: right bottom;
transform: rotate3d(0, 0, 1, 45deg);
opacity: 0;
}
to {
transform-origin: right bottom;
transform: translate3d(0, 0, 0);
opacity: 1;
}
}
@keyframes rotateInUpLeft {
from {
transform-origin: left bottom;
transform: rotate3d(0, 0, 1, 45deg);
opacity: 0;
}
to {
transform-origin: left bottom;
transform: translate3d(0, 0, 0);
opacity: 1;
}
}
@keyframes rotateInUpRight {
from {
transform-origin: right bottom;
transform: rotate3d(0, 0, 1, -90deg);
opacity: 0;
}
to {
transform-origin: right bottom;
transform: translate3d(0, 0, 0);
opacity: 1;
}
}
@keyframes rotateOut {
from {
transform-origin: center;
opacity: 1;
}
to {
transform-origin: center;
transform: rotate3d(0, 0, 1, 200deg);
opacity: 0;
}
}
@keyframes rotateOutDownLeft {
from {
transform-origin: left bottom;
opacity: 1;
}
to {
transform-origin: left bottom;
transform: rotate3d(0, 0, 1, 45deg);
opacity: 0;
}
}
@keyframes rotateOutDownRight {
from {
transform-origin: right bottom;
opacity: 1;
}
to {
transform-origin: right bottom;
transform: rotate3d(0, 0, 1, -45deg);
opacity: 0;
}
}
@keyframes rotateOutUpLeft {
from {
transform-origin: left bottom;
opacity: 1;
}
to {
transform-origin: left bottom;
transform: rotate3d(0, 0, 1, -45deg);
opacity: 0;
}
}
@keyframes rotateOutUpRight {
from {
transform-origin: right bottom;
opacity: 1;
}
to {
transform-origin: right bottom;
transform: rotate3d(0, 0, 1, 90deg);
opacity: 0;
}
}
@keyframes rollIn {
from {
opacity: 0;
transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@keyframes rollOut {
from {
opacity: 1;
}
to {
opacity: 0;
transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
}
}
@keyframes zoomIn {
from {
opacity: 0;
transform: scale3d(0.3, 0.3, 0.3);
}
50% {
opacity: 1;
}
}
@keyframes zoomInDown {
from {
opacity: 0;
transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
60% {
opacity: 1;
transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
}
}
@keyframes zoomInLeft {
from {
opacity: 0;
transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
60% {
opacity: 1;
transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
}
}
@keyframes zoomInRight {
from {
opacity: 0;
transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
60% {
opacity: 1;
transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
}
}
@keyframes zoomInUp {
from {
opacity: 0;
transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
60% {
opacity: 1;
transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
}
}
@keyframes zoomOut {
from {
opacity: 1;
}
50% {
opacity: 0;
transform: scale3d(0.3, 0.3, 0.3);
}
to {
opacity: 0;
}
}
@keyframes zoomOutDown {
40% {
opacity: 1;
transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
to {
opacity: 0;
transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0);
transform-origin: center bottom;
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
}
}
@keyframes zoomOutLeft {
40% {
opacity: 1;
transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0);
}
to {
opacity: 0;
transform: scale(0.1) translate3d(-2000px, 0, 0);
transform-origin: left center;
}
}
@keyframes zoomOutRight {
40% {
opacity: 1;
transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0);
}
to {
opacity: 0;
transform: scale(0.1) translate3d(2000px, 0, 0);
transform-origin: right center;
}
}
@keyframes zoomOutUp {
40% {
opacity: 1;
transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
to {
opacity: 0;
transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0);
transform-origin: center bottom;
animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
}
}
@keyframes slideInDown {
from {
transform: translate3d(0, -100%, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes slideInLeft {
from {
transform: translate3d(-100%, 0, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes slideInRight {
from {
transform: translate3d(100%, 0, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes slideInUp {
from {
transform: translate3d(0, 100%, 0);
visibility: visible;
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes slideOutDown {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(0, 100%, 0);
}
}
@keyframes slideOutLeft {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(-100%, 0, 0);
}
}
@keyframes slideOutRight {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(100%, 0, 0);
}
}
@keyframes slideOutUp {
from {
transform: translate3d(0, 0, 0);
}
to {
visibility: hidden;
transform: translate3d(0, -100%, 0);
}
}
.flipInX {
backface-visibility: visible !important;
animation-name: flipInX;
}
.flipInY {
backface-visibility: visible !important;
animation-name: flipInY;
}
.fadeIn {
animation-name: fadeIn;
}
.bounceIn {
animation-duration: 0.75s;
animation-name: bounceIn;
}
.lightSpeedIn {
animation-name: lightSpeedIn;
animation-timing-function: ease-out;
}
.rotateIn {
animation-name: rotateIn;
}
.rotateInDownLeft {
animation-name: rotateInDownLeft;
}
.rotateInDownRight {
animation-name: rotateInDownRight;
}
.rotateInUpLeft {
animation-name: rotateInUpLeft;
}
.rotateInUpRight {
animation-name: rotateInUpRight;
}
.rollIn {
animation-name: rollIn;
}
.zoomIn {
animation-name: zoomIn;
}
.zoomInDown {
animation-name: zoomInDown;
}
.zoomInLeft {
animation-name: zoomInLeft;
}
.zoomInRight {
animation-name: zoomInRight;
}
.zoomInUp {
animation-name: zoomInUp;
}
.slideInDown {
animation-name: slideInDown;
}
.slideInLeft {
animation-name: slideInLeft;
}
.slideInRight {
animation-name: slideInRight;
}
.slideInUp {
animation-name: slideInUp;
}
.flipOutX {
animation-duration: 0.75s;
animation-name: flipOutX;
backface-visibility: visible !important;
}
.flipOutY {
animation-duration: 0.75s;
backface-visibility: visible !important;
animation-name: flipOutY;
}
.fadeOut {
animation-name: fadeOut;
}
.bounceOut {
animation-duration: 0.75s;
animation-name: bounceOut;
}
.lightSpeedOut {
animation-name: lightSpeedOut;
animation-timing-function: ease-in;
}
.rotateOut {
animation-name: rotateOut;
}
.rotateOutDownLeft {
animation-name: rotateOutDownLeft;
}
.rotateOutDownRight {
animation-name: rotateOutDownRight;
}
.rotateOutUpLeft {
animation-name: rotateOutUpLeft;
}
.rotateOutUpRight {
animation-name: rotateOutUpRight;
}
.hinge {
animation-duration: 2s;
animation-name: hinge;
}
.rollOut {
animation-name: rollOut;
}
.zoomOut {
animation-name: zoomOut;
}
.zoomOutDown {
animation-name: zoomOutDown;
}
.zoomOutLeft {
animation-name: zoomOutLeft;
}
.zoomOutRight {
animation-name: zoomOutRight;
}
.zoomOutUp {
animation-name: zoomOutUp;
}
.slideOutDown {
animation-name: slideOutDown;
}
.slideOutLeft {
animation-name: slideOutLeft;
}
.slideOutRight {
animation-name: slideOutRight;
}
.slideOutUp {
animation-name: slideOutUp;
}
.animated {
animation-duration: 0.5s;
animation-fill-mode: both;
}
.animated-slow {
animation-duration: 0.8s;
animation-fill-mode: both;
}
.animated-fast {
animation-duration: 0.3s;
animation-fill-mode: both;
}
+325
View File
@@ -0,0 +1,325 @@
@red-50: #ffebee;
@red-100: #ffcdd2;
@red-200: #ef9a9a;
@red-300: #e57373;
@red-400: #ef5350;
@red-500: #f44336;
@red-600: #e53935;
@red-700: #d32f2f;
@red-800: #c62828;
@red-900: #b71c1c;
@red-A100: #ff8a80;
@red-A200: #ff5252;
@red-A400: #ff1744;
@red-A700: #d50000;
@red: @red-500;
@pink-50: #fce4ec;
@pink-100: #f8bbd0;
@pink-200: #f48fb1;
@pink-300: #f06292;
@pink-400: #ec407a;
@pink-500: #e91e63;
@pink-600: #d81b60;
@pink-700: #c2185b;
@pink-800: #ad1457;
@pink-900: #880e4f;
@pink-A100: #ff80ab;
@pink-A200: #ff4081;
@pink-A400: #f50057;
@pink-A700: #c51162;
@pink: @pink-500;
@purple-50: #f3e5f5;
@purple-100: #e1bee7;
@purple-200: #ce93d8;
@purple-300: #ba68c8;
@purple-400: #ab47bc;
@purple-500: #9c27b0;
@purple-600: #8e24aa;
@purple-700: #7b1fa2;
@purple-800: #6a1b9a;
@purple-900: #4a148c;
@purple-A100: #ea80fc;
@purple-A200: #e040fb;
@purple-A400: #d500f9;
@purple-A700: #aa00ff;
@purple: @purple-500;
@deep-purple-50: #ede7f6;
@deep-purple-100: #d1c4e9;
@deep-purple-200: #b39ddb;
@deep-purple-300: #9575cd;
@deep-purple-400: #7e57c2;
@deep-purple-500: #673ab7;
@deep-purple-600: #5e35b1;
@deep-purple-700: #512da8;
@deep-purple-800: #4527a0;
@deep-purple-900: #311b92;
@deep-purple-A100: #b388ff;
@deep-purple-A200: #7c4dff;
@deep-purple-A400: #651fff;
@deep-purple-A700: #6200ea;
@deep-purple: @deep-purple-500;
@indigo-50: #e8eaf6;
@indigo-100: #c5cae9;
@indigo-200: #9fa8da;
@indigo-300: #7986cb;
@indigo-400: #5c6bc0;
@indigo-500: #3f51b5;
@indigo-600: #3949ab;
@indigo-700: #303f9f;
@indigo-800: #283593;
@indigo-900: #1a237e;
@indigo-A100: #8c9eff;
@indigo-A200: #536dfe;
@indigo-A400: #3d5afe;
@indigo-A700: #304ffe;
@indigo: @indigo-500;
@blue-50: #e3f2fd;
@blue-100: #bbdefb;
@blue-200: #90caf9;
@blue-300: #64b5f6;
@blue-400: #42a5f5;
@blue-500: #2196f3;
@blue-600: #1e88e5;
@blue-700: #1976d2;
@blue-800: #1565c0;
@blue-900: #0d47a1;
@blue-A100: #82b1ff;
@blue-A200: #448aff;
@blue-A400: #2979ff;
@blue-A700: #2962ff;
@blue: @blue-500;
@light-blue-50: #e1f5fe;
@light-blue-100: #b3e5fc;
@light-blue-200: #81d4fa;
@light-blue-300: #4fc3f7;
@light-blue-400: #29b6f6;
@light-blue-500: #03a9f4;
@light-blue-600: #039be5;
@light-blue-700: #0288d1;
@light-blue-800: #0277bd;
@light-blue-900: #01579b;
@light-blue-A100: #80d8ff;
@light-blue-A200: #40c4ff;
@light-blue-A400: #00b0ff;
@light-blue-A700: #0091ea;
@light-blue: @light-blue-500;
@cyan-50: #e0f7fa;
@cyan-100: #b2ebf2;
@cyan-200: #80deea;
@cyan-300: #4dd0e1;
@cyan-400: #26c6da;
@cyan-500: #00bcd4;
@cyan-600: #00acc1;
@cyan-700: #0097a7;
@cyan-800: #00838f;
@cyan-900: #006064;
@cyan-A100: #84ffff;
@cyan-A200: #18ffff;
@cyan-A400: #00e5ff;
@cyan-A700: #00b8d4;
@cyan: @cyan-500;
@teal-50: #e0f2f1;
@teal-100: #b2dfdb;
@teal-200: #80cbc4;
@teal-300: #4db6ac;
@teal-400: #26a69a;
@teal-500: #009688;
@teal-600: #00897b;
@teal-700: #00796b;
@teal-800: #00695c;
@teal-900: #004d40;
@teal-A100: #a7ffeb;
@teal-A200: #64ffda;
@teal-A400: #1de9b6;
@teal-A700: #00bfa5;
@teal: @teal-500;
@green-50: #e8f5e9;
@green-100: #c8e6c9;
@green-200: #a5d6a7;
@green-300: #81c784;
@green-400: #66bb6a;
@green-500: #4caf50;
@green-600: #43a047;
@green-700: #388e3c;
@green-800: #2e7d32;
@green-900: #1b5e20;
@green-A100: #b9f6ca;
@green-A200: #69f0ae;
@green-A400: #00e676;
@green-A700: #00c853;
@green: @green-500;
@light-green-50: #f1f8e9;
@light-green-100: #dcedc8;
@light-green-200: #c5e1a5;
@light-green-300: #aed581;
@light-green-400: #9ccc65;
@light-green-500: #8bc34a;
@light-green-600: #7cb342;
@light-green-700: #689f38;
@light-green-800: #558b2f;
@light-green-900: #33691e;
@light-green-A100: #ccff90;
@light-green-A200: #b2ff59;
@light-green-A400: #76ff03;
@light-green-A700: #64dd17;
@light-green: @light-green-500;
@lime-50: #f9fbe7;
@lime-100: #f0f4c3;
@lime-200: #e6ee9c;
@lime-300: #dce775;
@lime-400: #d4e157;
@lime-500: #cddc39;
@lime-600: #c0ca33;
@lime-700: #afb42b;
@lime-800: #9e9d24;
@lime-900: #827717;
@lime-A100: #f4ff81;
@lime-A200: #eeff41;
@lime-A400: #c6ff00;
@lime-A700: #aeea00;
@lime: @lime-500;
@yellow-50: #fffde7;
@yellow-100: #fff9c4;
@yellow-200: #fff59d;
@yellow-300: #fff176;
@yellow-400: #ffee58;
@yellow-500: #fec60a;
@yellow-600: #fdd835;
@yellow-700: #fbc02d;
@yellow-800: #f9a825;
@yellow-900: #f57f17;
@yellow-A100: #ffff8d;
@yellow-A200: #ffff00;
@yellow-A400: #ffea00;
@yellow-A700: #ffd600;
@yellow: @yellow-700;
@amber-50: #fff8e1;
@amber-100: #ffecb3;
@amber-200: #ffe082;
@amber-300: #ffd54f;
@amber-400: #ffca28;
@amber-500: #ffc107;
@amber-600: #ffb300;
@amber-700: #ffa000;
@amber-800: #ff8f00;
@amber-900: #ff6f00;
@amber-A100: #ffe57f;
@amber-A200: #ffd740;
@amber-A400: #ffc400;
@amber-A700: #ffab00;
@amber: @amber-500;
@orange-50: #fff3e0;
@orange-100: #ffe0b2;
@orange-200: #ffcc80;
@orange-300: #ffb74d;
@orange-400: #ffa726;
@orange-500: #ff9800;
@orange-600: #fb8c00;
@orange-700: #f57c00;
@orange-800: #ef6c00;
@orange-900: #e65100;
@orange-A100: #ffd180;
@orange-A200: #ffab40;
@orange-A400: #ff9100;
@orange-A700: #ff6d00;
@orange: @orange-500;
@deep-orange-50: #fbe9e7;
@deep-orange-100: #ffccbc;
@deep-orange-200: #ffab91;
@deep-orange-300: #ff8a65;
@deep-orange-400: #ff7043;
@deep-orange-500: #ff5722;
@deep-orange-600: #f4511e;
@deep-orange-700: #e64a19;
@deep-orange-800: #d84315;
@deep-orange-900: #bf360c;
@deep-orange-A100: #ff9e80;
@deep-orange-A200: #ff6e40;
@deep-orange-A400: #ff3d00;
@deep-orange-A700: #dd2c00;
@deep-orange: @deep-orange-500;
@brown-50: #efebe9;
@brown-100: #d7ccc8;
@brown-200: #bcaaa4;
@brown-300: #a1887f;
@brown-400: #8d6e63;
@brown-500: #795548;
@brown-600: #6d4c41;
@brown-700: #5d4037;
@brown-800: #4e342e;
@brown-900: #3e2723;
@brown-A100: #d7ccc8;
@brown-A200: #bcaaa4;
@brown-A400: #8d6e63;
@brown-A700: #5d4037;
@brown: @brown-500;
@grey-50: #fafafa;
@grey-100: #f5f5f5;
@grey-200: #eeeeee;
@grey-300: #e0e0e0;
@grey-400: #bdbdbd;
@grey-500: #9e9e9e; @rgb-grey-500: "158, 158, 158";
@grey-600: #757575;
@grey-700: #616161;
@grey-800: #424242;
@grey-900: #212121;
@grey-A100: #f5f5f5;
@grey-A200: #eeeeee;
@grey-A400: #bdbdbd;
@grey-A700: #616161;
@grey: @grey-500;
@blue-grey-50: #eceff1;
@blue-grey-100: #cfd8dc;
@blue-grey-200: #b0bec5;
@blue-grey-300: #90a4ae;
@blue-grey-400: #78909c;
@blue-grey-500: #607d8b;
@blue-grey-600: #546e7a;
@blue-grey-700: #455a64;
@blue-grey-800: #37474f;
@blue-grey-900: #263238;
@blue-grey-A100: #cfd8dc;
@blue-grey-A200: #b0bec5;
@blue-grey-A400: #78909c;
@blue-grey-A700: #455a64;
@blue-grey: @blue-grey-500;
@black: #000000; @rgb-black: "0,0,0";
@white: #ffffff; @rgb-white: "255,255,255";
+186
View File
@@ -0,0 +1,186 @@
@import './reset.less';
@import './animate.less';
*, *::after, *::before {
-webkit-user-drag: none;
}
.nobreak {
white-space: nowrap;
}
.auto-hidden {
.mixin-ellipsis-1;
}
.center {
text-align: center;
}
.break {
word-break: break-all;
}
table {
width: 100%;
border-spacing: 0;
border-collapse: collapse;
overflow: hidden;
color: rgba(0, 0, 0, 0.87);
th {
font-size: 12px;
text-align: left;
height: 28px;
line-height: 28px;
padding: 5px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
tbody {
tr {
border-top: 1px solid @color-theme_2-line;
// border-top: 1px solid rgba(0, 0, 0, 0.12);
transition: background-color 0.2s ease;
&:hover {
background-color: @color-theme_2-hover;
}
&:first-child {
border-top: none;
}
td {
padding: 5px;
position: relative;
transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
font-size: 13px;
line-height: 1.3;
vertical-align: middle;
}
}
}
}
.badge {
display: inline-block;
padding: 0.25em 0.4em;
font-size: .7em;
// font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 2px;
&.badge-light {
background-color: #f8f9fa;
}
&.badge-secondary {
color: #fff;
background-color: #6c757d;
}
&.badge-info {
color: #fff;
background-color: #4baed5;
}
&.badge-warning {
color: #fff;
background-color: #ffa45a;
}
&.badge-danger {
color: #fff;
background-color: #ff705a;
}
&.badge-success {
color: #fff;
background-color: #32bc63;
}
}
small {
font-size: .8em;
}
.small {
font-size: .9em;
}
strong {
font-weight: bold;
}
.underline {
text-decoration: underline;
}
svg {
transition: @transition-theme;
transition-property: fill;
}
.hover {
cursor: pointer;
transition: color .2s ease;
&:hover {
color: @color-theme;
}
&:active {
color: @color-theme-active;
}
}
.scroll {
&::-webkit-scrollbar {
width: 6px;
height: 6px;
background-color: rgba(0, 0, 0, 0);
}
&::-webkit-scrollbar-track {
background-color: @color-scrollbar-track;
border-radius: 3px;
// background-color: rgba(0, 0, 0, 0.1);
}
&::-webkit-scrollbar-thumb {
border-radius: 3px;
background-color: @color-scrollbar-thumb;
// background-color: rgba(0, 0, 0, 0.2);
transition: all 0.4s ease;
}
&::-webkit-scrollbar-thumb:hover {
border-radius: 3px;
background-color: @color-scrollbar-thumb-hover;
// background-color: rgba(0, 0, 0, 0.4);
transition: all 0.4s ease;
}
}
each(@themes, {
#container.@{value} {
.hover {
&:hover {
color: ~'@{color-@{value}-theme}';
}
&:active {
color: ~'@{color-@{value}-theme-active}';
}
}
.scroll {
&::-webkit-scrollbar {
background-color: rgba(0, 0, 0, 0);
}
&::-webkit-scrollbar-track {
background-color: ~'@{color-@{value}-scrollbar-track}';
}
&::-webkit-scrollbar-thumb {
background-color: ~'@{color-@{value}-scrollbar-thumb}';
}
&::-webkit-scrollbar-thumb:hover {
background-color: ~'@{color-@{value}-scrollbar-thumb-hover}';
}
}
table {
tbody {
tr {
border-top-color: ~'@{color-@{value}-theme_2-line}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
}
}
}
}
})
+30
View File
@@ -0,0 +1,30 @@
@import './variables.less';
/*自动隐藏文字*/
.mixin-ellipsis-1() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.mixin-ellipsis() {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
word-break: break-all;
white-space: normal !important;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
.mixin-ellipsis-2() {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
word-break: break-all;
white-space: normal !important;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
+43
View File
@@ -0,0 +1,43 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
+430
View File
@@ -0,0 +1,430 @@
@import './colors.less';
@themes: green, yellow, blue, red, purple, orange, grey, midAutumn, dhHyrz;
// Colors
// @color-theme: #03a678;
@color-theme: #4daf7c;
@color-theme-bgimg: none;
@color-theme-bgposition: center center;
@color-theme-bgsize: auto auto;
@color-theme-hover: fadeout(lighten(@color-theme, 10%), 30%);
@color-theme-active: fadeout(darken(@color-theme, 20%), 60%);
@color-theme-font: #fff;
@color-theme-font-label: lighten(@color-theme, 35%);
@color-theme_2: rgba(255, 255, 255, .9);
@color-theme_2-background_1: #fff;
@color-theme_2-background_2: fadeout(@color-theme_2-background_1, 2%);
@color-theme_2-hover: fadeout(lighten(@color-theme, 10%), 70%);
@color-theme_2-active: fadeout(darken(@color-theme, 5%), 70%);
@color-theme_2-font: darken(@color-theme_2, 70%);
@color-theme_2-font-label: fadeout(@color-theme_2-font, 50%);
@color-theme_2-line: lighten(@color-theme, 35%);
@color-theme-sidebar: @color-theme;
@color-btn: fadeout(darken(@color-theme, 5%), 15%);
@color-btn-background: fadeout(lighten(@color-theme, 35%), 70%);
@color-pagination-background: fadeout(lighten(@color-theme, 45%), 30%);
@color-pagination-hover: fadeout(lighten(@color-theme, 10%), 70%);
@color-pagination-active: fadeout(darken(@color-theme, 10%), 70%);
@color-pagination-select: fadeout(lighten(@color-theme, 10%), 50%);
@color-search-form-background: fadeout(lighten(@color-theme, 35%), 10%);
// @color-search-list-background: fadeout(lighten(@color-theme, 35%), 10%);
@color-search-list-hover: fadeout(darken(@color-theme, 10%), 70%);
@color-scrollbar-track: fadeout(@color-theme, 80%);
@color-scrollbar-thumb: fadeout(@color-theme, 60%);
@color-scrollbar-thumb-hover: fadeout(@color-theme, 40%);
@color-player-pic-c1: fadeout(@color-theme_2, 50%);
@color-player-pic-c2: darken(@color-theme_2, 30%);
@color-player-progress: darken(@color-theme_2, 6%);
@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-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-minBtn: #85c43b;
@color-maxBtn: #e7aa36;
@color-closeBtn: #ea6e4d;
@color-green-theme: #4daf7c;
@color-green-theme-bgimg: none;
@color-green-theme-bgposition: center center;
@color-green-theme-bgsize: auto auto;
@color-green-theme-hover: fadeout(lighten(@color-green-theme, 10%), 30%);
@color-green-theme-active: fadeout(darken(@color-green-theme, 20%), 60%);
@color-green-theme-font: #fff;
@color-green-theme-font-label: lighten(@color-green-theme, 35%);
@color-green-theme_2: #fff;
@color-green-theme_2-background_1: #fff;
@color-green-theme_2-background_2: fadeout(@color-green-theme_2-background_1, 2%);
@color-green-theme_2-hover: fadeout(lighten(@color-green-theme, 10%), 70%);
@color-green-theme_2-active: fadeout(darken(@color-green-theme, 5%), 70%);
@color-green-theme_2-font: darken(@color-green-theme_2, 70%);
@color-green-theme_2-font-label: fadeout(@color-green-theme_2-font, 50%);
@color-green-theme_2-line: lighten(@color-green-theme, 35%);
@color-green-theme-sidebar: @color-green-theme;
@color-green-btn: fadeout(darken(@color-green-theme, 5%), 15%);
@color-green-btn-background: fadeout(lighten(@color-green-theme, 35%), 70%);
@color-green-pagination-background: fadeout(lighten(@color-green-theme, 45%), 30%);
@color-green-pagination-hover: fadeout(lighten(@color-green-theme, 10%), 70%);
@color-green-pagination-active: fadeout(darken(@color-green-theme, 10%), 70%);
@color-green-pagination-select: fadeout(lighten(@color-green-theme, 10%), 50%);
@color-green-search-form-background: fadeout(lighten(@color-green-theme, 35%), 10%);
@color-green-search-list-hover: fadeout(darken(@color-green-theme, 10%), 70%);
@color-green-scrollbar-track: fadeout(@color-green-theme, 80%);
@color-green-scrollbar-thumb: fadeout(@color-green-theme, 60%);
@color-green-scrollbar-thumb-hover: fadeout(@color-green-theme, 40%);
@color-green-player-pic-c1: fadeout(@color-green-theme_2, 50%);
@color-green-player-pic-c2: darken(@color-green-theme_2, 30%);
@color-green-player-progress: darken(@color-green-theme_2, 6%);
@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-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-yellow-theme: #f2d35b;
@color-yellow-theme-bgimg: none;
@color-yellow-theme-bgposition: center center;
@color-yellow-theme-bgsize: auto auto;
@color-yellow-theme-hover: fadeout(lighten(@color-yellow-theme, 10%), 30%);
@color-yellow-theme-active: fadeout(darken(@color-yellow-theme, 20%), 60%);
@color-yellow-theme-font: #fff;
@color-yellow-theme-font-label: lighten(@color-yellow-theme, 35%);
@color-yellow-theme_2: #fff;
@color-yellow-theme_2-background_1: #fff;
@color-yellow-theme_2-background_2: fadeout(@color-yellow-theme_2-background_1, 2%);
@color-yellow-theme_2-hover: fadeout(lighten(@color-yellow-theme, 10%), 70%);
@color-yellow-theme_2-active: fadeout(darken(@color-yellow-theme, 5%), 70%);
@color-yellow-theme_2-font: darken(@color-yellow-theme_2, 70%);
@color-yellow-theme_2-font-label: fadeout(@color-yellow-theme_2-font, 50%);
@color-yellow-theme_2-line: lighten(@color-yellow-theme, 35%);
@color-yellow-theme-sidebar: @color-yellow-theme;
@color-yellow-btn: fadeout(darken(@color-yellow-theme, 5%), 15%);
@color-yellow-btn-background: fadeout(lighten(@color-yellow-theme, 25%), 70%);
@color-yellow-pagination-background: fadeout(lighten(@color-yellow-theme, 30%), 30%);
@color-yellow-pagination-hover: fadeout(lighten(@color-yellow-theme, 5%), 70%);
@color-yellow-pagination-active: fadeout(darken(@color-yellow-theme, 5%), 70%);
@color-yellow-pagination-select: fadeout(lighten(@color-yellow-theme, 5%), 50%);
@color-yellow-search-form-background: fadeout(lighten(@color-yellow-theme, 35%), 10%);
@color-yellow-search-list-hover: fadeout(darken(@color-yellow-theme, 10%), 70%);
@color-yellow-scrollbar-track: fadeout(@color-yellow-theme, 80%);
@color-yellow-scrollbar-thumb: fadeout(@color-yellow-theme, 60%);
@color-yellow-scrollbar-thumb-hover: fadeout(@color-yellow-theme, 40%);
@color-yellow-player-pic-c1: fadeout(@color-yellow-theme_2, 50%);
@color-yellow-player-pic-c2: darken(@color-yellow-theme_2, 30%);
@color-yellow-player-progress: darken(@color-yellow-theme_2, 6%);
@color-yellow-player-progress-bar1: darken(@color-yellow-theme_2, 12%);
@color-yellow-player-progress-bar2: lighten(@color-yellow-theme, 12%);
@color-yellow-player-status-text: lighten(@color-yellow-theme_2-font, 10%);
@color-yellow-tab-btn-background: fadeout(lighten(@color-yellow-theme, 10%), 80%);
@color-yellow-tab-btn-background-hover: @color-yellow-theme_2-hover;
@color-yellow-tab-border-top: fadeout(lighten(@color-yellow-theme, 5%), 50%);
@color-yellow-tab-border-bottom: lighten(@color-yellow-theme, 5%);
@color-orange-theme: #f5ab35;
@color-orange-theme-bgimg: none;
@color-orange-theme-bgposition: center center;
@color-orange-theme-bgsize: auto auto;
@color-orange-theme-hover: fadeout(lighten(@color-orange-theme, 10%), 30%);
@color-orange-theme-active: fadeout(darken(@color-orange-theme, 20%), 60%);
@color-orange-theme-font: #fff;
@color-orange-theme-font-label: lighten(@color-orange-theme, 35%);
@color-orange-theme_2: #fff;
@color-orange-theme_2-background_1: #fff;
@color-orange-theme_2-background_2: fadeout(@color-orange-theme_2-background_1, 2%);
@color-orange-theme_2-hover: fadeout(lighten(@color-orange-theme, 10%), 70%);
@color-orange-theme_2-active: fadeout(darken(@color-orange-theme, 5%), 70%);
@color-orange-theme_2-font: darken(@color-orange-theme_2, 70%);
@color-orange-theme_2-font-label: fadeout(@color-orange-theme_2-font, 50%);
@color-orange-theme_2-line: lighten(@color-orange-theme, 35%);
@color-orange-theme-sidebar: @color-orange-theme;
@color-orange-btn: fadeout(darken(@color-orange-theme, 5%), 15%);
@color-orange-btn-background: fadeout(lighten(@color-orange-theme, 35%), 70%);
@color-orange-pagination-background: fadeout(lighten(@color-orange-theme, 35%), 30%);
@color-orange-pagination-hover: fadeout(lighten(@color-orange-theme, 10%), 70%);
@color-orange-pagination-active: fadeout(darken(@color-orange-theme, 10%), 70%);
@color-orange-pagination-select: fadeout(lighten(@color-orange-theme, 10%), 50%);
@color-orange-search-form-background: fadeout(lighten(@color-orange-theme, 35%), 10%);
@color-orange-search-list-hover: fadeout(darken(@color-orange-theme, 10%), 70%);
@color-orange-scrollbar-track: fadeout(@color-orange-theme, 80%);
@color-orange-scrollbar-thumb: fadeout(@color-orange-theme, 60%);
@color-orange-scrollbar-thumb-hover: fadeout(@color-orange-theme, 40%);
@color-orange-player-pic-c1: fadeout(@color-orange-theme_2, 50%);
@color-orange-player-pic-c2: darken(@color-orange-theme_2, 30%);
@color-orange-player-progress: darken(@color-orange-theme_2, 6%);
@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-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-blue-theme: #3498db;
@color-blue-theme-bgimg: none;
@color-blue-theme-bgposition: center center;
@color-blue-theme-bgsize: auto auto;
@color-blue-theme-hover: fadeout(lighten(@color-blue-theme, 10%), 30%);
@color-blue-theme-active: fadeout(darken(@color-blue-theme, 20%), 60%);
@color-blue-theme-font: #fff;
@color-blue-theme-font-label: lighten(@color-blue-theme, 35%);
@color-blue-theme_2: #fff;
@color-blue-theme_2-background_1: #fff;
@color-blue-theme_2-background_2: fadeout(@color-blue-theme_2-background_1, 2%);
@color-blue-theme_2-hover: fadeout(lighten(@color-blue-theme, 10%), 70%);
@color-blue-theme_2-active: fadeout(darken(@color-blue-theme, 5%), 70%);
@color-blue-theme_2-font: darken(@color-blue-theme_2, 70%);
@color-blue-theme_2-font-label: fadeout(@color-blue-theme_2-font, 50%);
@color-blue-theme_2-line: lighten(@color-blue-theme, 35%);
@color-blue-theme-sidebar: @color-blue-theme;
@color-blue-btn: fadeout(darken(@color-blue-theme, 5%), 15%);
@color-blue-btn-background: fadeout(lighten(@color-blue-theme, 35%), 70%);
@color-blue-pagination-background: fadeout(lighten(@color-blue-theme, 40%), 30%);
@color-blue-pagination-hover: fadeout(lighten(@color-blue-theme, 15%), 70%);
@color-blue-pagination-active: fadeout(darken(@color-blue-theme, 15%), 70%);
@color-blue-pagination-select: fadeout(lighten(@color-blue-theme, 15%), 50%);
@color-blue-search-form-background: fadeout(lighten(@color-blue-theme, 35%), 10%);
@color-blue-search-list-hover: fadeout(darken(@color-blue-theme, 10%), 70%);
@color-blue-scrollbar-track: fadeout(@color-blue-theme, 80%);
@color-blue-scrollbar-thumb: fadeout(@color-blue-theme, 60%);
@color-blue-scrollbar-thumb-hover: fadeout(@color-blue-theme, 40%);
@color-blue-player-pic-c1: fadeout(@color-blue-theme_2, 50%);
@color-blue-player-pic-c2: darken(@color-blue-theme_2, 30%);
@color-blue-player-progress: darken(@color-blue-theme_2, 6%);
@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-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-red-theme: #d64541;
@color-red-theme-bgimg: none;
@color-red-theme-bgposition: center center;
@color-red-theme-bgsize: auto auto;
@color-red-theme-hover: fadeout(lighten(@color-red-theme, 10%), 30%);
@color-red-theme-active: fadeout(darken(@color-red-theme, 20%), 60%);
@color-red-theme-font: #fff;
@color-red-theme-font-label: lighten(@color-red-theme, 35%);
@color-red-theme_2: #fff;
@color-red-theme_2-hover: fadeout(lighten(@color-red-theme, 10%), 70%);
@color-red-theme_2-active: fadeout(darken(@color-red-theme, 5%), 70%);
@color-red-theme_2-background_1: #fff;
@color-red-theme_2-background_2: fadeout(@color-red-theme_2-background_1, 2%);
@color-red-theme_2-font: darken(@color-red-theme_2, 70%);
@color-red-theme_2-font-label: fadeout(@color-red-theme_2-font, 50%);
@color-red-theme_2-line: lighten(@color-red-theme, 35%);
@color-red-theme-sidebar: @color-red-theme;
@color-red-btn: fadeout(darken(@color-red-theme, 5%), 15%);
@color-red-btn-background: fadeout(lighten(@color-red-theme, 35%), 70%);
@color-red-pagination-background: fadeout(lighten(@color-red-theme, 40%), 30%);
@color-red-pagination-hover: fadeout(lighten(@color-red-theme, 15%), 70%);
@color-red-pagination-active: fadeout(darken(@color-red-theme, 15%), 70%);
@color-red-pagination-select: fadeout(lighten(@color-red-theme, 15%), 50%);
@color-red-search-form-background: fadeout(lighten(@color-red-theme, 35%), 10%);
@color-red-search-list-hover: fadeout(darken(@color-red-theme, 10%), 70%);
@color-red-scrollbar-track: fadeout(@color-red-theme, 80%);
@color-red-scrollbar-thumb: fadeout(@color-red-theme, 60%);
@color-red-scrollbar-thumb-hover: fadeout(@color-red-theme, 40%);
@color-red-player-pic-c1: fadeout(@color-red-theme_2, 50%);
@color-red-player-pic-c2: darken(@color-red-theme_2, 30%);
@color-red-player-progress: darken(@color-red-theme_2, 6%);
@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-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-purple-theme: #9b59b6;
@color-purple-theme-bgimg: none;
@color-purple-theme-bgposition: center center;
@color-purple-theme-bgsize: auto auto;
@color-purple-theme-hover: fadeout(lighten(@color-purple-theme, 10%), 30%);
@color-purple-theme-active: fadeout(darken(@color-purple-theme, 20%), 60%);
@color-purple-theme-font: #fff;
@color-purple-theme-font-label: lighten(@color-purple-theme, 35%);
@color-purple-theme_2: #fff;
@color-purple-theme_2-background_1: #fff;
@color-purple-theme_2-background_2: fadeout(@color-purple-theme_2-background_1, 2%);
@color-purple-theme_2-hover: fadeout(lighten(@color-purple-theme, 10%), 70%);
@color-purple-theme_2-active: fadeout(darken(@color-purple-theme, 5%), 70%);
@color-purple-theme_2-font: darken(@color-purple-theme_2, 70%);
@color-purple-theme_2-font-label: fadeout(@color-purple-theme_2-font, 50%);
@color-purple-theme_2-line: lighten(@color-purple-theme, 35%);
@color-purple-theme-sidebar: @color-purple-theme;
@color-purple-btn: fadeout(darken(@color-purple-theme, 5%), 15%);
@color-purple-btn-background: fadeout(lighten(@color-purple-theme, 35%), 70%);
@color-purple-pagination-background: fadeout(lighten(@color-purple-theme, 40%), 30%);
@color-purple-pagination-hover: fadeout(lighten(@color-purple-theme, 15%), 70%);
@color-purple-pagination-active: fadeout(darken(@color-purple-theme, 15%), 70%);
@color-purple-pagination-select: fadeout(lighten(@color-purple-theme, 15%), 50%);
@color-purple-search-form-background: fadeout(lighten(@color-purple-theme, 35%), 10%);
@color-purple-search-list-hover: fadeout(darken(@color-purple-theme, 10%), 70%);
@color-purple-scrollbar-track: fadeout(@color-purple-theme, 80%);
@color-purple-scrollbar-thumb: fadeout(@color-purple-theme, 60%);
@color-purple-scrollbar-thumb-hover: fadeout(@color-purple-theme, 40%);
@color-purple-player-pic-c1: fadeout(@color-purple-theme_2, 50%);
@color-purple-player-pic-c2: darken(@color-purple-theme_2, 30%);
@color-purple-player-progress: darken(@color-purple-theme_2, 6%);
@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-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-grey-theme: #6c7a89;
@color-grey-theme-bgimg: none;
@color-grey-theme-bgposition: center center;
@color-grey-theme-bgsize: auto auto;
@color-grey-theme-hover: fadeout(lighten(@color-grey-theme, 10%), 30%);
@color-grey-theme-active: fadeout(darken(@color-grey-theme, 20%), 60%);
@color-grey-theme-font: #fff;
@color-grey-theme-font-label: lighten(@color-grey-theme, 35%);
@color-grey-theme_2: #fff;
@color-grey-theme_2-background_1: #fff;
@color-grey-theme_2-background_2: fadeout(@color-grey-theme_2-background_1, 2%);
@color-grey-theme_2-hover: fadeout(lighten(@color-grey-theme, 10%), 70%);
@color-grey-theme_2-active: fadeout(darken(@color-grey-theme, 5%), 70%);
@color-grey-theme_2-font: darken(@color-grey-theme_2, 70%);
@color-grey-theme_2-font-label: fadeout(@color-grey-theme_2-font, 50%);
@color-grey-theme_2-line: lighten(@color-grey-theme, 35%);
@color-grey-theme-sidebar: @color-grey-theme;
@color-grey-btn: fadeout(darken(@color-grey-theme, 5%), 15%);
@color-grey-btn-background: fadeout(lighten(@color-grey-theme, 35%), 70%);
@color-grey-pagination-background: fadeout(lighten(@color-grey-theme, 45%), 30%);
@color-grey-pagination-hover: fadeout(lighten(@color-grey-theme, 10%), 70%);
@color-grey-pagination-active: fadeout(darken(@color-grey-theme, 10%), 70%);
@color-grey-pagination-select: fadeout(lighten(@color-grey-theme, 10%), 50%);
@color-grey-search-form-background: fadeout(lighten(@color-grey-theme, 35%), 10%);
@color-grey-search-list-hover: fadeout(darken(@color-grey-theme, 10%), 70%);
@color-grey-scrollbar-track: fadeout(@color-grey-theme, 80%);
@color-grey-scrollbar-thumb: fadeout(@color-grey-theme, 60%);
@color-grey-scrollbar-thumb-hover: fadeout(@color-grey-theme, 40%);
@color-grey-player-pic-c1: fadeout(@color-grey-theme_2, 50%);
@color-grey-player-pic-c2: darken(@color-grey-theme_2, 30%);
@color-grey-player-progress: darken(@color-grey-theme_2, 6%);
@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-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-midAutumn-theme: rgba(74, 55, 82, 1);
@color-midAutumn-theme-bgimg: url(../images/jqbg.jpg);
@color-midAutumn-theme-bgposition: center center;
@color-midAutumn-theme-bgsize: auto 100%;
@color-midAutumn-theme-hover: fadeout(lighten(@color-midAutumn-theme, 10%), 30%);
@color-midAutumn-theme-active: fadeout(lighten(@color-midAutumn-theme, 15%), 60%);
@color-midAutumn-theme-font: rgba(246, 233, 255, 0.9);
@color-midAutumn-theme-font-label: fadeout(lighten(@color-midAutumn-theme, 20%), 20%);
@color-midAutumn-theme_2: rgba(255, 255, 255, .93);
@color-midAutumn-theme_2-background_1: #eeedef;
@color-midAutumn-theme_2-background_2: fadeout(@color-midAutumn-theme_2-background_1, 2%);
@color-midAutumn-theme_2-hover: fadeout(lighten(@color-midAutumn-theme, 10%), 65%);
@color-midAutumn-theme_2-active: fadeout(darken(@color-midAutumn-theme, 5%), 70%);
@color-midAutumn-theme_2-font: darken(@color-midAutumn-theme_2, 70%);
@color-midAutumn-theme_2-font-label: lighten(@color-midAutumn-theme, 40%);
@color-midAutumn-theme_2-line: lighten(@color-midAutumn-theme, 54%);
@color-midAutumn-theme-sidebar: rgba(255, 255, 255, 0);
@color-midAutumn-btn: fadeout(darken(@color-midAutumn-theme, 5%), 15%);
@color-midAutumn-btn-background: fadeout(lighten(@color-midAutumn-theme, 35%), 70%);
@color-midAutumn-pagination-background: fadeout(lighten(@color-midAutumn-theme, 45%), 50%);
@color-midAutumn-pagination-hover: fadeout(lighten(@color-midAutumn-theme, 10%), 70%);
@color-midAutumn-pagination-active: fadeout(darken(@color-midAutumn-theme, 10%), 70%);
@color-midAutumn-pagination-select: fadeout(lighten(@color-midAutumn-theme, 10%), 55%);
@color-midAutumn-search-form-background: fadeout(lighten(@color-midAutumn-theme, 50%), 10%);
@color-midAutumn-search-list-hover: fadeout(darken(@color-midAutumn-theme, 10%), 70%);
@color-midAutumn-scrollbar-track: fadeout(@color-midAutumn-theme, 80%);
@color-midAutumn-scrollbar-thumb: fadeout(@color-midAutumn-theme, 60%);
@color-midAutumn-scrollbar-thumb-hover: fadeout(@color-midAutumn-theme, 40%);
@color-midAutumn-player-pic-c1: fadeout(@color-midAutumn-theme_2, 50%);
@color-midAutumn-player-pic-c2: darken(@color-midAutumn-theme_2, 30%);
@color-midAutumn-player-progress: darken(@color-midAutumn-theme_2, 6%);
@color-midAutumn-player-progress-bar1: darken(@color-midAutumn-theme_2, 12%);
@color-midAutumn-player-progress-bar2: lighten(@color-midAutumn-theme, 12%);
@color-midAutumn-player-status-text: lighten(@color-midAutumn-theme_2-font, 10%);
@color-midAutumn-tab-btn-background: fadeout(lighten(@color-midAutumn-theme, 10%), 80%);
@color-midAutumn-tab-btn-background-hover: @color-midAutumn-theme_2-hover;
@color-midAutumn-tab-border-top: fadeout(lighten(@color-midAutumn-theme, 5%), 50%);
@color-midAutumn-tab-border-bottom: lighten(@color-midAutumn-theme, 5%);
@color-dhHyrz-theme: rgb(87, 144, 167);
@color-dhHyrz-theme-bgimg: url(../images/hzwbg.jpeg);
@color-dhHyrz-theme-bgposition: center center;
@color-dhHyrz-theme-bgsize: auto 100%;
@color-dhHyrz-theme-hover: fadeout(lighten(@color-dhHyrz-theme, 10%), 45%);
@color-dhHyrz-theme-active: fadeout(lighten(@color-dhHyrz-theme, 15%), 60%);
@color-dhHyrz-theme-font: rgba(67, 118, 148, 0.9);
@color-dhHyrz-theme-font-label: fadeout(darken(@color-dhHyrz-theme, 20%), 20%);
@color-dhHyrz-theme_2: rgba(255, 255, 255, 0.8);
@color-dhHyrz-theme_2-background_1: #e9faff;
@color-dhHyrz-theme_2-background_2: fadeout(@color-dhHyrz-theme_2-background_1, 2%);
@color-dhHyrz-theme_2-hover: fadeout(lighten(@color-dhHyrz-theme, 10%), 65%);
@color-dhHyrz-theme_2-active: fadeout(darken(@color-dhHyrz-theme, 5%), 70%);
@color-dhHyrz-theme_2-font: darken(@color-dhHyrz-theme_2, 80%);
@color-dhHyrz-theme_2-font-label: lighten(@color-dhHyrz-theme, 10%);
@color-dhHyrz-theme_2-line: lighten(@color-dhHyrz-theme, 36%);
@color-dhHyrz-theme-sidebar: rgba(255, 255, 255, .3);
@color-dhHyrz-btn: fadeout(darken(@color-dhHyrz-theme, 5%), 15%);
@color-dhHyrz-btn-background: fadeout(lighten(@color-dhHyrz-theme, 35%), 70%);
@color-dhHyrz-pagination-background: fadeout(lighten(@color-dhHyrz-theme, 45%), 50%);
@color-dhHyrz-pagination-hover: fadeout(lighten(@color-dhHyrz-theme, 10%), 70%);
@color-dhHyrz-pagination-active: fadeout(darken(@color-dhHyrz-theme, 10%), 70%);
@color-dhHyrz-pagination-select: fadeout(lighten(@color-dhHyrz-theme, 10%), 55%);
@color-dhHyrz-search-form-background: fadeout(lighten(@color-dhHyrz-theme, 30%), 20%);
@color-dhHyrz-search-list-hover: fadeout(darken(@color-dhHyrz-theme, 10%), 50%);
@color-dhHyrz-scrollbar-track: fadeout(@color-dhHyrz-theme, 80%);
@color-dhHyrz-scrollbar-thumb: fadeout(@color-dhHyrz-theme, 60%);
@color-dhHyrz-scrollbar-thumb-hover: fadeout(@color-dhHyrz-theme, 40%);
@color-dhHyrz-player-pic-c1: fadeout(@color-dhHyrz-theme_2, 50%);
@color-dhHyrz-player-pic-c2: darken(@color-dhHyrz-theme_2, 30%);
@color-dhHyrz-player-progress: darken(@color-dhHyrz-theme_2, 6%);
@color-dhHyrz-player-progress-bar1: darken(@color-dhHyrz-theme_2, 12%);
@color-dhHyrz-player-progress-bar2: lighten(@color-dhHyrz-theme, 12%);
@color-dhHyrz-player-status-text: lighten(@color-dhHyrz-theme_2-font, 10%);
@color-dhHyrz-tab-btn-background: fadeout(lighten(@color-dhHyrz-theme, 10%), 80%);
@color-dhHyrz-tab-btn-background-hover: @color-dhHyrz-theme_2-hover;
@color-dhHyrz-tab-border-top: fadeout(lighten(@color-dhHyrz-theme, 5%), 50%);
@color-dhHyrz-tab-border-bottom: lighten(@color-dhHyrz-theme, 5%);
// Width
@width-app-left: 180px;
// Height
@height-toolbar: 50px;
@height-player: 55px;
// Shadow
@shadow-app: 8px;
// Radius
@radius-progress-border: 5px;
@transition-theme: .4s ease;
+150
View File
@@ -0,0 +1,150 @@
<template lang="pug">
div(:class="$style.aside")
div(:class="$style.logo")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' height='100%' viewBox='0 0 127 61' space='preserve')
use(xlink:href='#icon-logo')
div(:class="$style.menu")
dl
dt 在线音乐
dd
router-link(:active-class="$style.active" to="search") 搜索
dd
router-link(:active-class="$style.active" to="songList") 歌单
dd
router-link(:active-class="$style.active" to="leaderboard") 排行榜
dl
dt 我的音乐
dd
router-link(:active-class="($route.query.id === defaultList.id || $route.query.id == '') ? $style.active : ''" :to="`list?id=${defaultList.id || ''}`") {{defaultList.name}}
router-link(:active-class="$route.query.id === loveList.id ? $style.active : ''" :to="`list?id=${loveList.id}`") {{loveList.name}}
router-link(:active-class="$route.query.id === item.id ? $style.active : ''" v-for="item in userList" :to="`list?id=${item._id}`" :key="item._id") {{item.name}}
dl
dt 其他
dd
router-link(:active-class="$style.active" to="download") 下载管理
dd
router-link(:active-class="$style.active" to="setting") 设置
</template>
<script>
import { mapGetters } from 'vuex'
export default {
props: {
list: {
type: Array,
default() {
return []
},
},
},
data() {
return {
active: 'search',
}
},
computed: {
...mapGetters('list', ['defaultList', 'loveList', 'userList']),
},
methods: {},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.aside {
// box-shadow: 0 0 5px rgba(0, 0, 0, .3);
transition: @transition-theme;
transition-property: background-color;
background-color: @color-theme-sidebar;
// background-color: @color-aside-background;
// border-right: 2px solid @color-theme;
-webkit-app-region: drag;
-webkit-user-select: none;
display: flex;
flex-flow: column nowrap;
}
.logo {
box-sizing: border-box;
padding: 20px;
height: 100px;
color: @color-theme-font;
flex: none;
}
.menu {
flex: auto;
padding: 10px;
dl {
-webkit-app-region: no-drag;
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
dt {
font-size: 11px;
transition: @transition-theme;
transition-property: color;
color: @color-theme-font-label;
}
dd a {
display: block;
box-sizing: border-box;
text-decoration: none;
position: relative;
padding: 10px;
margin: 5px 0;
// border-left: 5px solid transparent;
transition: @transition-theme;
transition-property: color;
color: @color-theme-font;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s ease;
border-radius: 4px;
&.active {
// border-left-color: @color-theme-active;
background-color: @color-theme-active;
}
&:hover:not(.active) {
background-color: @color-theme-hover;
}
}
}
}
each(@themes, {
:global(#container.@{value}) {
.aside {
background-color: ~'@{color-@{value}-theme-sidebar}';
}
.logo {
color: ~'@{color-@{value}-theme-font}';
}
.menu {
dl {
dt {
color: ~'@{color-@{value}-theme-font-label}';
}
dd a {
color: ~'@{color-@{value}-theme-font}';
&.active {
background-color: ~'@{color-@{value}-theme-active}';
}
&:hover:not(.active) {
background-color: ~'@{color-@{value}-theme-hover}';
}
}
}
}
}
})
</style>
File diff suppressed because one or more lines are too long
+787
View File
@@ -0,0 +1,787 @@
<template lang="pug">
div(:class="$style.player")
div(:class="$style.left")
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.column1")
div(:class="$style.container")
div(:class="$style.title" @click="handleCopy(title)" :title="title + '(点击复制)'") {{title}}
div(:class="$style.volumeContent")
div(:class="$style.volume")
div(:class="$style.volumeBar" :style="{ transform: `scaleX(${volume || 0})` }")
div(:class="$style.volumeMask" @mousedown="handleVolumeMsDown" ref="dom_volumeMask" :title="`当前音量:${parseInt(volume * 100)}%`")
//- div(:class="$style.playBtn" @click='handleNext' title="音量")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 291.063 291.064' space='preserve')
use(xlink:href='#icon-sound')
div(:class="$style.playBtn" @click='handleNext' title="下一首")
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 ? '暂停' : '播放'" @click='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.column2")
div(:class="$style.progress")
//- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ transform: `scaleX(${progress || 0})` }")
div(:class="[$style.progressBar, $style.progressBar2]" :style="{ transform: `scaleX(${progress || 0})` }")
div(:class="$style.progressMask" @click='setProgess' ref="dom_progress")
div(:class="$style.column3")
span {{nowPlayTimeStr}}
span(:class="$style.statusText") {{status}}
span {{maxPlayTimeStr}}
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")
path(d='M29,0C12.984,0,0,12.984,0,29c0,16.016,12.984,29,29,29s29-12.984,29-29C58,12.984,45.016,0,29,0zM29,36.08c-3.91,0-7.08-3.17-7.08-7.08c0-3.91,3.17-7.08,7.08-7.08s7.08,3.17,7.08,7.08C36.08,32.91,32.91,36.08,29,36.08z')
path(:class="$style.c1" d='M6.487,22.932c-0.077,0-0.156-0.009-0.234-0.027c-0.537-0.13-0.868-0.67-0.739-1.206c0.946-3.935,2.955-7.522,5.809-10.376s6.441-4.862,10.376-5.809c0.536-0.127,1.077,0.202,1.206,0.739c0.129,0.536-0.202,1.076-0.739,1.206c-3.575,0.859-6.836,2.685-9.429,5.277s-4.418,5.854-5.277,9.429C7.349,22.624,6.938,22.932,6.487,22.932z')
path(:class="$style.c1" d='M36.066,52.514c-0.451,0-0.861-0.308-0.972-0.767c-0.129-0.536,0.202-1.076,0.739-1.206c3.576-0.859,6.837-2.685,9.43-5.277s4.418-5.854,5.277-9.429c0.129-0.538,0.668-0.868,1.206-0.739c0.537,0.13,0.868,0.67,0.739,1.206c-0.946,3.935-2.955,7.522-5.809,10.376s-6.441,4.862-10.377,5.809C36.223,52.505,36.144,52.514,36.066,52.514z')
path(:class="$style.c1" d='M11.313,24.226c-0.075,0-0.151-0.008-0.228-0.026c-0.538-0.125-0.873-0.663-0.747-1.2c0.72-3.09,2.282-5.904,4.52-8.141c2.236-2.237,5.051-3.8,8.141-4.52c0.535-0.131,1.075,0.209,1.2,0.747c0.126,0.537-0.209,1.075-0.747,1.2c-2.725,0.635-5.207,2.014-7.18,3.986s-3.352,4.455-3.986,7.18C12.179,23.914,11.768,24.226,11.313,24.226z')
path(:class="$style.c1" d='M34.773,47.688c-0.454,0-0.865-0.312-0.973-0.773c-0.126-0.537,0.209-1.075,0.747-1.2c2.725-0.635,5.207-2.014,7.18-3.986s3.352-4.455,3.986-7.18c0.125-0.538,0.662-0.88,1.2-0.747c0.538,0.125,0.873,0.663,0.747,1.2c-0.72,3.09-2.282,5.904-4.52,8.141c-2.236,2.237-5.051,3.8-8.141,4.52C34.925,47.68,34.849,47.688,34.773,47.688z')
path(:class="$style.c1" d='M16.14,25.519c-0.071,0-0.143-0.008-0.215-0.023c-0.539-0.118-0.881-0.651-0.763-1.19c0.997-4.557,4.586-8.146,9.143-9.143c0.537-0.116,1.071,0.222,1.19,0.763c0.118,0.539-0.224,1.072-0.763,1.19c-3.796,0.831-6.786,3.821-7.617,7.617C17.013,25.2,16.6,25.519,16.14,25.519z')
path(:class="$style.c1" d='M33.48,42.861c-0.46,0-0.873-0.318-0.976-0.786c-0.118-0.539,0.224-1.072,0.763-1.19c3.796-0.831,6.786-3.821,7.617-7.617c0.118-0.541,0.65-0.881,1.19-0.763c0.539,0.118,0.881,0.651,0.763,1.19c-0.997,4.557-4.586,8.146-9.143,9.143C33.623,42.854,33.552,42.861,33.48,42.861z')
path(:class="$style.c2" d='M29,38.08c-5.007,0-9.08-4.073-9.08-9.08s4.073-9.08,9.08-9.08s9.08,4.073,9.08,9.08S34.007,38.08,29,38.08z M29,23.92c-2.801,0-5.08,2.279-5.08,5.08s2.279,5.08,5.08,5.08s5.08-2.279,5.08-5.08S31.801,23.92,29,23.92z')
</template>
<script>
import Lyric from 'lrc-file-parser'
import { rendererSend } from '../../../common/icp'
import { formatPlayTime2, getRandom, checkPath, setTitle, clipboardWriteText, debounce } from '../../utils'
import { mapGetters, mapActions, mapMutations } from 'vuex'
import { requestMsg } from '../../utils/message'
import { isMac } from '../../../common/utils'
export default {
data() {
return {
show: true,
audio: null,
volume: 0,
nowPlayTime: 0,
maxPlayTime: 0,
isPlay: false,
status: '^-^',
musicInfo: {
songmid: null,
img: null,
lrc: null,
url: null,
name: '^',
singer: '^',
},
targetSong: null,
pregessWidth: 0,
lyric: {
lrc: null,
text: '',
line: 0,
},
delayNextTimeout: null,
audioErrorTime: 0,
retryNum: 0,
isMac,
volumeEvent: {
isMsDown: false,
msDownX: 0,
msDownVolume: 0,
},
handleVolumeMsMoveFn: null,
handleVolumeMsUpFn: null,
}
},
computed: {
...mapGetters(['setting']),
...mapGetters('player', ['list', 'playIndex', 'changePlay', 'listId']),
// 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'
},
maxPlayTimeStr() {
return this.maxPlayTime ? formatPlayTime2(this.maxPlayTime) : '00:00'
},
progress() {
return this.nowPlayTime / this.maxPlayTime || 0
},
isAPITemp() {
return this.setting.apiSource == 'temp'
},
},
mounted() {
this.init()
this.$nextTick(() => {
this.setProgessWidth()
})
this.handleSaveVolume = debounce(volume => {
this.setVolume(volume)
}, 300)
document.addEventListener('mousemove', this.handleVolumeMsMoveFn = this.handleVolumeMsMove.bind(this))
document.addEventListener('mouseup', this.handleVolumeMsUpFn = this.handleVolumeMsUp.bind(this))
},
beforeDestroy() {
document.removeEventListener('mousemove', this.handleVolumeMsMoveFn)
document.removeEventListener('mouseup', this.handleVolumeMsUpFn)
},
watch: {
changePlay(n) {
if (!n) return
// console.log('changePlay')
this.handleRemoveMusic()
this.resetChangePlay()
if (this.playIndex < 0) return
this.stopPlay()
this.play()
},
'setting.player.togglePlayMethod'(n) {
this.audio.loop = n === 'singleLoop'
},
list(n, o) {
if (n === o) {
let index = this.listId == 'download'
? n.findIndex(s => s.musicInfo.songmid === this.musicInfo.songmid)
: n.findIndex(s => s.songmid === this.musicInfo.songmid)
if (index < 0) {
// console.log(this.playIndex)
if (n.length) {
this.fixPlayIndex(this.playIndex - 1)
this.handleNext()
} else {
this.setPlayIndex(-1)
}
} else {
this.fixPlayIndex(index)
}
// console.log(this.playIndex)
}
},
progress(n, o) {
if (n.toFixed(2) === o.toFixed(2)) return
this.sendProgressEvent(n, 'normal')
},
volume(n) {
this.handleSaveVolume(n)
},
},
methods: {
...mapActions('player', ['getUrl', 'getPic', 'getLrc']),
...mapMutations('player', [
'setPlayIndex',
'fixPlayIndex',
'resetChangePlay',
]),
...mapMutations(['setVolume']),
...mapMutations('list', ['updateMusicInfo']),
init() {
this.audio = document.createElement('audio')
this.volume = this.audio.volume = this.setting.player.volume
this.audio.controls = false
this.audio.autoplay = true
this.audio.preload = 'auto'
this.audio.loop = this.setting.player.togglePlayMethod === 'singleLoop'
this.audio.addEventListener('playing', () => {
console.log('开始播放')
this.status = '播放中...'
this.startPlay()
})
this.audio.addEventListener('pause', () => {
console.log('暂停播放')
this.lyric.lrc.pause()
this.stopPlay()
this.status = '暂停播放'
})
this.audio.addEventListener('ended', () => {
console.log('播放完毕')
this.stopPlay()
this.status = '播放完毕'
this.handleNext()
})
this.audio.addEventListener('error', () => {
// console.log('code', this.audio.error.code)
if (!this.musicInfo.songmid) return
console.log('出错')
this.stopPlay()
if (this.listId != 'download' && this.audio.error.code !== 1 && this.retryNum < 3) { // 若音频URL无效则尝试刷新3次URL
// console.log(this.retryNum)
this.audioErrorTime = this.audio.currentTime // 记录出错的播放时间
this.retryNum++
this.setUrl(this.list[this.playIndex], true)
this.status = 'URL过期,正在刷新URL...'
return
}
this.sendProgressEvent(this.progress, 'error')
this.status = '音频加载出错,5 秒后切换下一首'
this.addDelayNextTimeout()
})
this.audio.addEventListener('loadeddata', () => {
this.maxPlayTime = this.audio.duration
if (this.audioErrorTime) {
this.audio.currentTime = this.audioErrorTime
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.audio.addEventListener('loadstart', () => {
this.status = '音乐加载中...'
})
this.audio.addEventListener('canplay', () => {
console.log('加载完成开始播放')
// if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000)
this.status = '音乐加载中...'
})
// this.audio.addEventListener('canplaythrough', () => {
// console.log('音乐加载完毕')
// // if (this.musicInfo.lyric.orgLrc) this.musicInfo.lyric.lrc.play(this.audio.currentTime * 1000)
// this.status = '播放中...'
// })
// this.audio.addEventListener('emptied', () => {
// console.log('媒介资源元素突然为空,网络错误 or 切换歌曲?')
// this.status = '媒介资源元素突然为空,网络错误?'
// })
this.audio.addEventListener('timeupdate', () => {
this.nowPlayTime = this.audio.currentTime
})
this.audio.addEventListener('waiting', () => {
// this.musicInfo.lyric.lrc.pause()
this.stopPlay()
this.status = '缓冲中...'
})
this.lyric.lrc = new Lyric({
onPlay: (line, text) => {
this.lyric.text = text
this.lyric.line = line
this.status = text
// console.log(line, text)
},
offset: 150,
})
},
play() {
console.log('play', this.playIndex)
this.checkDelayNextTimeout()
let targetSong = this.targetSong = this.list[this.playIndex]
this.retryNum = 0
this.audioErrorTime = 0
if (this.listId == 'download') {
console.log(targetSong.filePath)
if (!checkPath(targetSong.filePath) || !targetSong.isComplate || /\.ape$/.test(targetSong.filePath)) {
return this.list.length == 1 ? null : this.handleNext()
}
this.musicInfo.songmid = targetSong.musicInfo.songmid
this.musicInfo.singer = targetSong.musicInfo.singer
this.musicInfo.name = targetSong.musicInfo.name
this.audio.src = targetSong.filePath
// console.log(targetSong.filePath)
this.setImg(targetSong.musicInfo)
this.setLrc(targetSong.musicInfo)
} else {
this.musicInfo.songmid = targetSong.songmid
this.musicInfo.singer = targetSong.singer
this.musicInfo.name = targetSong.name
this.setUrl(targetSong)
this.setImg(targetSong)
this.setLrc(targetSong)
}
},
checkDelayNextTimeout() {
// console.log(this.delayNextTimeout)
if (this.delayNextTimeout) {
clearTimeout(this.delayNextTimeout)
this.delayNextTimeout = null
}
},
addDelayNextTimeout() {
this.checkDelayNextTimeout()
this.delayNextTimeout = setTimeout(() => {
this.delayNextTimeout = null
this.handleNext()
}, 5000)
},
handleNext() {
// if (this.list.listName === null) return
let list
if (this.listId == 'download') {
list = this.list.filter(s => !(!checkPath(s.filePath) || !s.isComplate || /\.ape$/.test(s.filePath)))
} else if (this.isAPITemp) {
list = this.list.filter(s => s.source == 'kw')
} else {
list = this.list
}
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 'list':
index = this.hanldeListNext(list, playIndex)
break
default:
return
}
if (index < 0) return
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
},
hanldeListRandom(list, index) {
return getRandom(0, list.length)
},
startPlay() {
this.isPlay = true
if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000)
this.setAppTitle()
this.sendProgressEvent(this.progress, 'normal')
},
stopPlay() {
this.isPlay = false
this.lyric.lrc.pause()
this.sendProgressEvent(this.progress, 'paused')
this.clearAppTitle()
},
setProgess(e) {
if (!this.audio.src) return
this.audio.currentTime =
(e.offsetX / this.pregessWidth) * this.maxPlayTime
if (!this.isPlay) this.audio.play()
},
setProgessWidth() {
this.pregessWidth = parseInt(
window.getComputedStyle(this.$refs.dom_progress, null).width
)
},
togglePlay() {
if (!this.audio.src) return
this.isPlay ? this.audio.pause() : this.audio.play()
},
imgError(e) {
// e.target.src = 'https://y.gtimg.cn/music/photo_new/T002R500x500M000002BMEC42fM8S3.jpg'
this.musicInfo.img = null
},
getPlayType(highQuality, songInfo) {
switch (songInfo.source) {
case 'wy':
case 'tx':
// case 'kg':
return '128k'
}
let type = '128k'
if (highQuality && songInfo._types['320k']) type = '320k'
return type
},
setUrl(targetSong, isRefresh) {
let type = this.getPlayType(this.setting.player.highQuality, targetSong)
this.musicInfo.url = targetSong.typeUrl[type]
this.status = '歌曲链接获取中...'
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
this.status = err.message
this.addDelayNextTimeout()
return Promise.reject(err)
})
},
setImg(targetSong) {
this.musicInfo.img = targetSong.img
if (!this.musicInfo.img) {
this.getPic(targetSong).then(() => {
this.musicInfo.img = targetSong.img
})
}
},
setLrc(targetSong) {
this.musicInfo.lrc = targetSong.lrc
let lrcP = this.musicInfo.lrc
? Promise.resolve()
: this.getLrc(targetSong).then(() => {
this.musicInfo.lrc = targetSong.lrc
})
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)
})
.catch(() => {
this.status = '歌词获取失败'
})
},
handleRemoveMusic() {
this.stopPlay()
this.audio.src = null
this.audio.removeAttribute('src')
this.status = '^-^'
this.musicInfo.img = null
this.musicInfo.name = this.musicInfo.singer = '^'
this.musicInfo.songmid = null
this.musicInfo.lrc = null
this.musicInfo.url = null
this.nowPlayTime = 0
this.maxPlayTime = 0
},
sendProgressEvent(status, mode) {
// console.log(status)
this.setting.player.isShowTaskProgess && rendererSend('progress', {
status: status < 0.01 ? 0.01 : status,
mode: mode || 'normal',
})
},
setAppTitle() {
setTitle(`${this.musicInfo.name} - ${this.musicInfo.singer}`)
},
clearAppTitle() {
setTitle()
},
handleVolumeMsDown(e) {
this.volumeEvent.isMsDown = true
this.volumeEvent.isMsMoved = false
this.volumeEvent.msDownX = e.clientX
let val = e.offsetX / 70
if (val < 0) val = 0
if (val > 1) val = 1
this.volume = val
this.volumeEvent.msDownVolume = val
// console.log(val)
if (this.audio) this.audio.volume = this.volume
},
handleVolumeMsUp(e) {
this.volumeEvent.isMsDown = false
},
handleVolumeMsMove(e) {
if (!this.volumeEvent.isMsDown) return
let val = this.volumeEvent.msDownVolume + (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)
},
handleCopy(text) {
clipboardWriteText(text)
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.player {
height: @height-player;
// background-color: rgb(245, 245, 245);
transition: @transition-theme;
transition-property: border-color;
border-top: 2px solid @color-theme;
box-sizing: border-box;
display: flex;
z-index: 1;
* {
box-sizing: border-box;
}
}
.left {
width: @height-player;
color: @color-theme;
transition: @transition-theme;
transition-property: color;
flex: none;
padding: 2px;
svg {
fill: currentColor;
}
img {
max-width: 100%;
max-height: 100%;
transition: @transition-theme;
transition-property: border-color;
// border: 2px solid @color-theme_2-background_1;
}
}
.right {
flex: auto;
// margin-left: 10px;
padding: 5px 10px;
display: flex;
flex-flow: column nowrap;
}
.column1 {
flex: auto;
position: relative;
font-size: 16px;
.container {
position: absolute;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
}
}
.title {
flex: 1 1 0;
width: 0;
padding-right: 5px;
font-size: 14px;
line-height: 18px;
.mixin-ellipsis-1;
}
.volume-content {
position: relative;
width: 100px;
display: flex;
align-items: center;
padding: 0 15px;
opacity: .5;
transition: opacity @transition-theme;
&:hover {
opacity: 1;
}
}
.volume {
// cursor: pointer;
width: 100%;
height: 0.25em;
border-radius: 10px;
// overflow: hidden;
transition: @transition-theme;
transition-property: background-color;
background-color: @color-player-progress;
// background-color: #f5f5f5;
position: relative;
border-radius: @radius-progress-border;
}
.volume-bar {
position: absolute;
left: 0;
top: 0;
transform: scaleX(0);
transform-origin: 0;
transition-property: transform;
transition-timing-function: ease;
width: 100%;
height: 100%;
border-radius: @radius-progress-border;
transition-duration: 0.2s;
background-color: @color-theme;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
}
.volume-mask {
position: absolute;
left: 15px;
right: 15px;
top: 0;
width: 70%;
height: 100%;
cursor: pointer;
}
.play-btn {
+ .play-btn {
margin-left: 10px;
}
flex: none;
height: 95%;
width: 20px;
align-self: center;
// margin-top: -2px;
transition: @transition-theme;
transition-property: color;
color: @color-theme;
transition: opacity 0.1s ease;
opacity: 1;
cursor: pointer;
svg {
fill: currentColor;
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.2));
}
&:active {
opacity: 0.7;
}
}
.column2 {
flex: none;
padding: 3px 0;
position: relative;
}
.progress {
width: 100%;
height: 0.15rem;
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;
transition-property: transform;
transition-timing-function: ease-out;
border-radius: @radius-progress-border;
}
.progress-bar1 {
transition-duration: 0.6s;
background-color: @color-player-progress-bar1;
}
.progress-bar2 {
transition-duration: 0.2s;
background-color: @color-player-progress-bar2;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
}
.column3 {
transition: @transition-theme;
transition-property: color;
color: @color-theme_2-font;
flex: none;
font-size: 12px;
display: flex;
padding-top: 2px;
// justify-content: space-between;
align-items: center;
}
.status-text {
font-size: 0.98em;
transition: @transition-theme;
transition-property: color;
color: @color-player-status-text;
.mixin-ellipsis-1;
padding: 0 5px;
flex: 1 1 0;
text-align: center;
line-height: 1.2;
width: 0;
}
#icon-pic {
color: @color-theme;
.c1 {
transition: @transition-theme;
transition-property: fill;
fill: @color-player-pic-c1;
}
.c2 {
transition: @transition-theme;
transition-property: fill;
fill: @color-player-pic-c2;
}
}
each(@themes, {
:global(#container.@{value}) {
.player {
border-top-color: ~'@{color-@{value}-theme}';
}
.left {
color: ~'@{color-@{value}-theme}';
// img {
// border-color: ~'@{color-@{value}-theme_2-background_1}';
// }
}
.play-btn {
color: ~'@{color-@{value}-theme}';
svg {
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.3));
}
}
.volume {
background-color: ~'@{color-@{value}-player-progress}';
}
.volume-bar {
background-color: ~'@{color-@{value}-theme}';
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
}
.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}';
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
}
.column3 {
color: ~'@{color-@{value}-theme_2-font}';
}
.status-text {
color: ~'@{color-@{value}-player-status-text}';
}
#icon-pic {
color: ~'@{color-@{value}-theme}';
.c1 {
fill: ~'@{color-@{value}-player-pic-c1}';
}
.c2 {
fill: ~'@{color-@{value}-player-pic-c2}';
}
}
}
})
</style>
+231
View File
@@ -0,0 +1,231 @@
<template lang="pug">
div(:class="$style.toolbar")
//- img(v-if="icon")
//- h1 {{title}}
material-search-input(:class="$style.input"
@event="handleEvent" :list="tipList" :visibleList="visibleList"
v-model="searchText")
div(:class="$style.control")
button(type="button" :class="$style.min" title="最小化" @click="min")
//- button(type="button" :class="$style.max" @click="max")
button(type="button" :class="$style.close" title="关闭" @click="close")
</template>
<script>
import { rendererSend } from 'common/icp'
import { mapGetters } from 'vuex'
import music from '../../utils/music'
import { debounce } from '../../utils'
export default {
data() {
return {
searchText: '',
visibleList: false,
tipList: [],
tipSearch: null,
}
},
computed: {
...mapGetters(['route', 'setting']),
...mapGetters('search', {
storeSearchText: 'searchText',
}),
source() {
return this.setting.search.tempSearchSource
},
isAutoClearInput() {
return this.setting.odc.isAutoClearSearchInput
},
},
watch: {
route(n) {
if (this.isAutoClearInput && n.name != 'search' && this.searchText) this.searchText = ''
},
'storeSearchText'(n) {
if (n !== this.searchText) this.searchText = n
},
searchText(n) {
this.handleTipSearch()
},
},
mounted() {
this.tipSearch = debounce(() => {
if (this.searchText === '') {
this.tipList.splice(0, this.tipList.length)
music[this.source].tempSearch.cancelTempSearch()
return
}
music[this.source].tempSearch.search(this.searchText).then(list => {
this.tipList = list
}).catch(() => {})
}, 50)
},
methods: {
handleEvent({ action, data }) {
switch (action) {
case 'focus':
if (!this.visibleList) this.visibleList = true
if (this.searchText) this.handleTipSearch()
break
case 'blur':
setTimeout(() => {
if (this.visibleList) this.visibleList = false
}, 50)
break
case 'submit':
this.handleSearch()
break
case 'listClick':
this.searchText = this.tipList[data]
this.$nextTick(() => {
this.handleSearch()
})
}
},
handleTipSearch() {
if (!this.visibleList) this.visibleList = true
this.tipSearch()
},
handleSearch() {
if (this.visibleList) this.visibleList = false
setTimeout(() => {
this.$router.push({
path: 'search',
query: {
text: this.searchText,
},
}).catch(_ => _)
}, 200)
},
min() {
rendererSend('min')
},
max() {
rendererSend('max')
},
close() {
rendererSend('close')
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.toolbar {
display: flex;
height: @height-toolbar;
justify-content: flex-end;
align-items: center;
padding-left: 15px;
-webkit-app-region: drag;
z-index: 1;
position: relative;
}
.input {
-webkit-app-region: no-drag;
position: absolute;
left: 15px;
top: 13px;
}
// img {
// flex: none;
// height: 100%;
// }
// h1 {
// text-align: center;
// padding: 8px;
// flex: auto;
// -webkit-app-region: drag;
// -webkit-user-select: none;
// }
.control {
display: flex;
height: 100%;
-webkit-app-region: no-drag;
&:hover {
button:before {
opacity: 1;
}
}
button {
position: relative;
width: @height-toolbar;
background: none;
border: none;
display: flex;
justify-content: center;
align-items: center;
outline: none;
padding: 0;
cursor: pointer;
&: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;
}
&.min:after {
background-color: @color-minBtn;
}
&.max:after {
background-color: @color-maxBtn;
}
&.close:after {
background-color: @color-closeBtn;
}
&.min:hover:after {
background-color: lighten(@color-minBtn, 10%);
opacity: 1;
}
&.max:hover:after {
background-color: lighten(@color-maxBtn, 10%);
}
&.close:hover:after {
background-color: lighten(@color-closeBtn, 10%);
}
}
}
.min {
&:before {
content: ' ';
width: 8px;
height: 2px;
left: @height-toolbar / 2 - 4;
top: @height-toolbar / 2 - 1;
background-color: #fff;
}
}
.close {
&:before {
content: '×';
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 14px;
line-height: 1;
color: #fff;
}
}
</style>
+24
View File
@@ -0,0 +1,24 @@
<template lang="pug">
div(:class="$style.view")
transition(enter-active-class="animated-fast fadeIn"
leave-active-class="animated-fast fadeOut")
router-view
//- core-title-bar
//- router-view
</template>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.view {
position: relative;
> * {
position: absolute;
width: 100%;
}
// background: #fff;
// overflow: hidden;
}
</style>
+17
View File
@@ -0,0 +1,17 @@
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
'./', true, /\.vue$/
)
requireComponent.keys().forEach(fileName => {
const componentConfig = requireComponent(fileName)
const componentName = upperFirst(
camelCase(fileName.replace(/^\.\//, '').replace(/\.\w+$/, ''))
)
Vue.component(componentName, componentConfig.default || componentConfig)
})
+66
View File
@@ -0,0 +1,66 @@
<template lang="pug">
button(:class="[$style.btn, min ? $style.min : '']" :disabled="disabled" @click="$emit('click', $event)")
slot
</template>
<script>
export default {
props: {
min: {
type: Boolean,
},
disabled: {
type: Boolean,
default: false,
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.btn {
display: inline-block;
border: none;
border-radius: 3px;
cursor: pointer;
padding: 8px 15px;
color: @color-btn;
outline: none;
transition: background-color 0.2s ease;
background-color: @color-btn-background;
&[disabled] {
opacity: .4;
}
&:hover {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
.min {
padding: 3px 8px;
font-size: 12px;
}
each(@themes, {
:global(#container.@{value}) {
.btn {
color: ~'@{color-@{value}-btn}';
background-color: ~'@{color-@{value}-btn-background}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
}
})
</style>
@@ -0,0 +1,205 @@
<template lang="pug">
div(:class="$style.checkbox")
input(:type="need ? 'radio' : 'checkbox'" :class="$style.input" :id="id" :disabled="disabled" :value="value" :name="name" @change="change" v-model="bool")
label(:for="id" :class="$style.content")
div(v-if="indeterminate" :class="$style.container")
svg(v-show="indeterminate" :class="$style.icon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' width="100%" viewBox='0 32 448 448' space='preserve')
use(xlink:href='#icon-check-indeterminate')
svg(v-show="!indeterminate" :class="$style.icon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' width="100%" viewBox='0 0 448 512' space='preserve')
use(xlink:href='#icon-check-true')
div(v-else :class="$style.container")
svg(version='1.1' :class="$style.icon" xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' width="100%" viewBox='0 32 448 448' space='preserve')
use(xlink:href='#icon-check-true')
span(v-if="label != null" v-html="label" :class="$style.label")
</template>
<script>
export default {
model: {
prop: 'checked',
event: 'input',
},
props: {
checked: {},
value: {},
id: {
type: String,
required: true,
},
name: {
type: String,
},
need: {
type: Boolean,
default: false,
},
label: {},
disabled: {
type: Boolean,
default: false,
},
indeterminate: {
type: Boolean,
default: false,
},
},
data() {
return {
bool: false,
}
},
watch: {
checked(n) {
this.setValue(n)
},
},
mounted() {
this.setValue(this.checked)
},
methods: {
change() {
let checked
if (Array.isArray(this.checked)) {
checked = [...this.checked]
const index = checked.indexOf(this.value)
if (index < 0) {
checked.push(this.value)
} else {
checked.splice(index, 1)
}
} else if (typeof this.checked == 'string') {
checked = this.bool ? this.value : ''
} else if (typeof this.checked == 'boolean') {
let bool = this.bool
if (this.indeterminate) {
bool = true
// this.$nextTick(() => {
this.bool = true
// })
}
checked = bool
}
this.$emit('input', checked)
this.$emit('change', checked)
},
setValue(value) {
let bool
if (Array.isArray(value)) {
bool = value.includes(this.value)
} else {
switch (typeof value) {
case 'string':
bool = value === this.value
break
case 'boolean':
bool = value
break
default:
return
}
}
this.bool = this.need ? bool && this.value : bool
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.checkbox {
display: inline-block;
// font-size: 56px;
}
.input {
display: none;
&[disabled] {
+ .content {
opacity: .5;
}
}
&:checked {
+ .content {
.container {
&:after {
border-color: @color-theme;
}
}
.icon {
transform: scale(1);
// opacity: 1;
}
}
}
}
.content {
display: flex;
justify-content: center;
}
.container {
flex: none;
position: relative;
display: inline-block;
width: 1em;
height: 1em;
cursor: pointer;
display: flex;
color: @color-theme;
// border: 1px solid #ccc;
&:after {
position: absolute;
content: ' ';
top: 0;
bottom: 0;
left: 0;
right: 0;
border: 1px solid @color-theme_2-font-label;
transition: border-color 0.2s ease;
border-radius: 15%;
}
}
.icon {
transition: 0.3s ease;
transition-property: transform;
transform: scale(0);
// opacity: 0;
}
.label {
flex: auto;
line-height: 1;
margin-left: 5px;
cursor: pointer;
}
each(@themes, {
:global(#container.@{value}) {
.checkbox {
> input {
&:checked {
+ .content {
.container {
&:after {
border-color: ~'@{color-@{value}-theme}';
}
}
}
}
}
}
.content {
.container {
color: ~'@{color-@{value}-theme}';
// border: 1px solid #ccc;
&:after {
border-color: ~'@{color-@{value}-theme_2-font-label}';
}
}
}
}
})
</style>
@@ -0,0 +1,104 @@
<template lang="pug">
material-modal(:show="show" :bg-close="bgClose" @close="handleClose")
main(:class="$style.main")
h2
| {{ info.name }}
br
| {{ info.singer }}
material-btn(:class="$style.btn" :title="!checkSource(type.type) && '腾讯、网易音源仅支持下载128k音质'" :disabled="!checkSource(type.type)" :key="type.type" @click="handleClick(type.type)" v-for="type in info.types") {{getTypeName(type.type)}} {{ type.type.toUpperCase() }}{{ type.size && ` - ${type.size.toUpperCase()}` }}
</template>
<script>
export default {
props: {
show: {
type: Boolean,
default: false,
},
musicInfo: {
type: Object,
},
bgClose: {
type: Boolean,
default: true,
},
},
computed: {
info() {
return this.musicInfo || {}
},
},
methods: {
handleClick(type) {
this.$emit('select', type)
},
handleClose() {
this.$emit('close')
},
getTypeName(type) {
switch (type) {
case 'flac':
case 'ape':
return '无损音质'
case '320k':
return '高品音质'
case '192k':
case '128k':
return '普通音质'
}
},
checkSource(type) {
switch (this.musicInfo.source) {
case 'wy':
case 'tx':
return type == '128k'
default:
return true
}
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.main {
padding: 15px;
max-width: 300px;
min-width: 200px;
display: flex;
flex-flow: column nowrap;
justify-content: center;
h2 {
font-size: 13px;
color: @color-theme_2-font;
line-height: 1.3;
text-align: center;
margin-bottom: 15px;
}
}
.btn {
display: block;
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
each(@themes, {
:global(#container.@{value}) {
.main {
h2 {
color: ~'@{color-@{value}-theme_2-font}';
}
}
}
})
</style>
@@ -0,0 +1,82 @@
<template lang="pug">
material-modal(:show="show" :bg-close="bgClose" @close="handleClose")
main(:class="$style.main")
h2
| 已选择 {{list.length}} 首歌曲
br
| 请选择要优先下载的音质
material-btn(:class="$style.btn" @click="handleClick('128k')") 普通音质 - 128K
material-btn(:class="$style.btn" @click="handleClick('320k')") 高品音质 - 320K
//- material-btn(:class="$style.btn" @click="handleClick('ape')") 无损音质 - APE
material-btn(:class="$style.btn" @click="handleClick('flac')") 无损音质 - FLAC
</template>
<script>
export default {
props: {
show: {
type: Boolean,
default: false,
},
bgClose: {
type: Boolean,
default: true,
},
list: {
type: Array,
default() {
return []
},
},
},
methods: {
handleClick(type) {
this.$emit('select', type)
},
handleClose() {
this.$emit('close')
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.main {
padding: 15px;
max-width: 300px;
min-width: 200px;
display: flex;
flex-flow: column nowrap;
justify-content: center;
h2 {
font-size: 13px;
color: @color-theme_2-font;
line-height: 1.3;
text-align: center;
margin-bottom: 15px;
}
}
.btn {
display: block;
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
each(@themes, {
:global(#container.@{value}) {
.main {
h2 {
color: ~'@{color-@{value}-theme_2-font}';
}
}
}
})
</style>
@@ -0,0 +1,122 @@
<template lang="pug">
transition(enter-active-class="animated-fast zoomIn" leave-active-class="animated zoomOut")
div(:class="$style.btns" v-show="show")
button(type="button" v-if="playBtn" title="播放" @click.stop="handleClick('play')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 287.386 287.386' space='preserve')
use(xlink:href='#icon-testPlay')
button(type="button" v-if="addBtn" title="添加" @click.stop="handleClick('add')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 42 42' space='preserve')
use(xlink:href='#icon-addTo')
button(type="button" v-if="downloadBtn" title="下载" @click.stop="handleClick('download')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 475.078 475.077' space='preserve')
use(xlink:href='#icon-download')
button(type="button" v-if="startBtn" title="开始" @click.stop="handleClick('start')")
svg(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')
button(type="button" v-if="pauseBtn" title="暂停" @click.stop="handleClick('pause')")
svg(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')
button(type="button" v-if="removeBtn" title="移除" @click.stop="handleClick('remove')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 212.982 212.982' space='preserve')
use(xlink:href='#icon-delete')
</template>
<script>
export default {
props: {
removeBtn: {
type: Boolean,
default: true,
},
startBtn: {
type: Boolean,
default: false,
},
pauseBtn: {
type: Boolean,
default: false,
},
downloadBtn: {
type: Boolean,
default: true,
},
addBtn: {
type: Boolean,
default: true,
},
playBtn: {
type: Boolean,
default: true,
},
show: {
type: Boolean,
default: false,
},
},
methods: {
handleClick(action) {
this.$emit('btn-click', action)
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.btns {
position: fixed;
bottom: 80px;
right: 30px;
background-color: @color-search-form-background;
border-radius: 5px;
// padding: 3px 5px;
box-shadow: 0 1px 5px 0 rgba(0,0,0,.2);
button {
background-color: transparent;
border: none;
border-radius: 3px;
margin-right: 5px;
cursor: pointer;
padding: 4px 7px;
color: @color-btn;
outline: none;
transition: background-color 0.2s ease;
line-height: 0;
&:last-child {
margin-right: 0;
}
svg {
height: 1.2em;
}
&:hover {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
}
each(@themes, {
:global(#container.@{value}) {
.btns {
background-color: ~'@{color-@{value}-search-form-background}';
button {
color: ~'@{color-@{value}-btn}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
}
}
})
</style>
@@ -0,0 +1,90 @@
<template lang="pug">
input(:class="$style.input" :type="type" :placeholder="placeholder" v-model.trim="text" :disabled="disabled"
@focus="$emit('focus', $event)" @blur="$emit('blur', $event)" @input="$emit('input', text)" @change="$emit('change', text)"
@keyup.enter="submit")
</template>
<script>
export default {
props: {
placeholder: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
value: {
type: String,
default: '',
},
type: {
type: String,
default: 'text',
},
},
data() {
return {
text: '',
}
},
watch: {
value(n) {
this.text = n
},
},
methods: {
handleInput() {
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.input {
display: inline-block;
border: none;
border-radius: 3px;
padding: 7px 8px;
color: @color-btn;
outline: none;
transition: background-color 0.2s ease;
background-color: @color-btn-background;
&[disabled] {
opacity: .4;
}
&:hover, &:focus {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
.min {
padding: 3px 8px;
font-size: 12px;
}
each(@themes, {
:global(#container.@{value}) {
.input {
color: ~'@{color-@{value}-btn}';
background-color: ~'@{color-@{value}-btn-background}';
&:hover, &:focus {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
}
})
</style>
@@ -0,0 +1,80 @@
<template lang="pug">
input(type="range" :class="[$style.range, min ? $style.min : '']" :disabled="disabled" v-model="val" input="handleInput" @change="handleChange")
</template>
<script>
export default {
props: {
min: {
type: Boolean,
},
disabled: {
type: Boolean,
default: false,
},
value: {
type: Number,
default: 0,
},
},
data() {
return {
val: 0,
}
},
mounted() {
this.val = this.value
},
methods: {
handleChange(e) {
this.$emit('input', this.val)
this.$emit('change', e)
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.range {
display: inline-block;
border-radius: .25em;
cursor: pointer;
color: @color-btn;
outline: none;
background: transparent;
// background-color: @color-btn-background;
&[disabled] {
opacity: .4;
}
&:hover {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
.min {
}
// each(@themes, {
// :global(#container.@{value}) {
// .btn {
// color: ~'@{color-@{value}-btn}';
// background-color: ~'@{color-@{value}-btn-background}';
// &:hover {
// background-color: ~'@{color-@{value}-theme_2-hover}';
// }
// &:active {
// background-color: ~'@{color-@{value}-theme_2-active}';
// }
// }
// }
// })
</style>
@@ -0,0 +1,134 @@
<template lang="pug">
div(:class="$style.btns")
button(type="button" v-if="playBtn" title="播放" @click.stop="handleClick('play')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 287.386 287.386' space='preserve')
use(xlink:href='#icon-testPlay')
button(type="button" v-if="listAddBtn" title="添加到..." @click.stop="handleClick('listAdd')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 42 42' space='preserve')
use(xlink:href='#icon-addTo')
button(type="button" v-if="downloadBtn" title="下载" @click.stop="handleClick('download')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 475.078 475.077' space='preserve')
use(xlink:href='#icon-download')
button(type="button" title="添加" v-if="userInfo" @click.stop="handleClick('add')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 42 42' space='preserve')
use(xlink:href='#icon-addTo')
button(type="button" v-if="startBtn" title="开始" @click.stop="handleClick('start')")
svg(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')
button(type="button" v-if="pauseBtn" title="暂停" @click.stop="handleClick('pause')")
svg(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')
button(type="button" v-if="fileBtn" title="定位文件" @click.stop="handleClick('file')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='-61 0 512 512' space='preserve')
use(xlink:href='#icon-musicFile')
button(type="button" v-if="removeBtn" title="移除" @click.stop="handleClick('remove')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 212.982 212.982' space='preserve')
use(xlink:href='#icon-delete')
button(type="button" v-if="searchBtn" title="搜索" @click.stop="handleClick('search')")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 30.239 30.239' space='preserve')
use(xlink:href='#icon-search')
</template>
<script>
import { mapGetters } from 'vuex'
export default {
props: {
index: {
type: Number,
required: true,
},
startBtn: {
type: Boolean,
default: false,
},
pauseBtn: {
type: Boolean,
default: false,
},
removeBtn: {
type: Boolean,
default: true,
},
downloadBtn: {
type: Boolean,
default: true,
},
playBtn: {
type: Boolean,
default: true,
},
listAddBtn: {
type: Boolean,
default: true,
},
searchBtn: {
type: Boolean,
default: false,
},
fileBtn: {
type: Boolean,
default: false,
},
},
computed: {
...mapGetters(['userInfo']),
},
methods: {
handleClick(action) {
this.$emit('btn-click', { action, index: this.index })
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.btns {
button {
background-color: transparent;
border: none;
border-radius: 3px;
margin-right: 5px;
cursor: pointer;
padding: 4px 7px;
color: @color-btn;
outline: none;
transition: background-color 0.2s ease;
line-height: 0;
&:last-child {
margin-right: 0;
}
svg {
height: 1.2em;
}
&:hover {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
}
each(@themes, {
:global(#container.@{value}) {
.btns {
button {
color: ~'@{color-@{value}-btn}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
}
}
})
</style>
+199
View File
@@ -0,0 +1,199 @@
<template lang="pug">
transition(enter-active-class="animated fadeIn"
leave-active-class="animated fadeOut")
div(:class="$style.modal" v-show="show" @click="bgClose && close()")
transition(:enter-active-class="inClass"
:leave-active-class="outClass")
div(:class="$style.content" v-show="show" @click.stop)
header(:class="$style.header")
button(type="button" @click="close")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 212.982 212.982' space='preserve')
use(xlink:href='#icon-delete')
slot
</template>
<script>
import { getRandom } from '../../utils'
import { mapGetters } from 'vuex'
export default {
props: {
show: {
type: Boolean,
default: false,
},
bgClose: {
type: Boolean,
default: false,
},
},
data() {
return {
animateIn: [
'flipInX',
'flipInY',
'fadeIn',
'bounceIn',
'lightSpeedIn',
'rotateInDownLeft',
'rotateInDownRight',
'rotateInUpLeft',
'rotateInUpRight',
'rollIn',
'zoomIn',
'zoomInDown',
'zoomInLeft',
'zoomInRight',
'zoomInUp',
'slideInDown',
'slideInLeft',
'slideInRight',
'slideInUp',
],
animateOut: [
'flipOutX',
'flipOutY',
'fadeOut',
'bounceOut',
'lightSpeedOut',
'rotateOutDownLeft',
'rotateOutDownRight',
'rotateOutUpLeft',
'rotateOutUpRight',
'rollOut',
'zoomOut',
'zoomOutDown',
'zoomOutLeft',
'zoomOutRight',
'zoomOutUp',
'slideOutDown',
'slideOutLeft',
'slideOutRight',
'slideOutUp',
'hinge',
],
inClass: 'animated flipInX',
outClass: 'animated flipOutX',
unwatchFn: null,
}
},
computed: {
...mapGetters(['setting']),
},
watch: {
'setting.randomAnimate': {
handler(n) {
if (n) {
this.unwatchFn = this.$watch('show', function(n) {
if (n) {
this.inClass = 'animated ' + this.animateIn[getRandom(0, this.animateIn.length)]
this.outClass = 'animated ' + this.animateOut[getRandom(0, this.animateOut.length)]
}
})
} else {
this.unwatchFn && this.unwatchFn()
}
},
immediate: true,
},
},
mounted() {
},
methods: {
close() {
this.$emit('close')
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.modal {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .3);
display: grid;
align-items: center;
justify-items: center;
z-index: 99;
}
.content {
border-radius: 5px;
box-shadow: 0 0 3px rgba(0, 0, 0, .3);
overflow: hidden;
max-height: 70%;
max-width: 70%;
position: relative;
display: flex;
flex-flow: column nowrap;
> * {
background-color: @color-theme_2-background_2;
}
}
.header {
flex: none;
background-color: @color-theme;
display: flex;
justify-content: flex-end;
button {
border: none;
cursor: pointer;
padding: 4px 7px;
background-color: transparent;
color: @color-theme-font-label;
outline: none;
transition: background-color 0.2s ease;
line-height: 0;
svg {
height: .7em;
}
&:hover {
background-color: @color-theme-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
}
each(@themes, {
:global(#container.@{value}) {
.modal {
background-color: rgba(0, 0, 0, .3);
}
.content {
box-shadow: 0 0 3px rgba(0, 0, 0, .3);
> * {
background-color: ~'@{color-@{value}-theme_2-background_2}';
}
}
.header {
background-color: ~'@{color-@{value}-theme}';
button {
color: ~'@{color-@{value}-theme-font-label}';
&:hover {
background-color: ~'@{color-@{value}-theme-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
}
}
})
</style>
@@ -0,0 +1,227 @@
<template lang="pug">
div(:class="$style.pagination" v-if="allPage > 1")
ul
li(v-if="page===1" :class="$style.disabled")
span
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve')
use(xlink:href='#icon-left')
li(v-else)
button(type="button" @click="handleClick(page - 1)" title="上一页")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve')
use(xlink:href='#icon-left')
li(v-if="allPage > btnLength && page > pageEvg+1" :class="$style.first")
button(type="button" @click="handleClick(1)" title="第 1 页")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve')
use(xlink:href='#icon-first')
li(v-for="(p, index) in pages" :key="index" :class="{[$style.active] : p == page}")
span(v-if="p === page" v-text="page")
button(v-else type="button" @click="handleClick(p)" v-text="p" :title="`第 ${p} 页`")
li(v-if="allPage > btnLength && allPage - page > pageEvg" :class="$style.last")
button(type="button" @click="handleClick(allPage)" :title="`第 ${allPage} 页`")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve')
use(xlink:href='#icon-last')
li(v-if="page===allPage" :class="$style.disabled")
span
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve')
use(xlink:href='#icon-right')
li(v-else)
button(type="button" @click="handleClick(page + 1)" title="下一页")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve')
use(xlink:href='#icon-right')
</template>
<script>
import { mapGetters } from 'vuex'
export default {
props: {
count: {
type: Number,
default: 0,
},
limit: {
type: Number,
default: 10,
},
page: {
type: Number,
default: 1,
},
btnLength: {
type: Number,
default: 7,
},
},
data() {
return {
pageArr: [],
}
},
computed: {
...mapGetters(['userInfo']),
allPage() {
return Math.ceil(this.count / this.limit) || 1
},
pageEvg() {
return Math.floor(this.btnLength / 2)
},
pages() {
if (this.allPage <= this.btnLength) return this.pageArr
let start =
this.page - this.pageEvg > 1
? this.allPage - this.page < this.pageEvg + 1
? this.allPage - (this.btnLength - 1)
: this.page - this.pageEvg
: 1
let end =
this.page + this.pageEvg <= this.btnLength
? this.btnLength
: this.page + this.pageEvg <= this.allPage
? this.page + this.pageEvg
: this.allPage
// console.log(start-1);
// console.log(end);
// console.log(this.pageArr.slice(start-1, end-1));
return this.pageArr.slice(start - 1, end)
},
},
watch: {
allPage() {
this.initPageArr()
},
},
methods: {
initPageArr() {
this.pageArr = []
for (let i = 1; i <= this.allPage; i++) this.pageArr.push(i)
},
handleClick(page) {
this.$emit('btn-click', page)
},
},
mounted() {
this.initPageArr()
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.pagination {
display: inline-block;
background-color: @color-pagination-background;
// border-top-left-radius: 8px;
border-radius: 4px;
ul {
display: flex;
flex-flow: row nowrap;
// border: .0625rem solid @theme_color2;
// border-radius: .3125rem;
li {
// margin-right: @padding;
color: @color-theme;
// border: .0625rem solid @theme_line;
// border-radius: .3125rem;
transition: 0.4s ease;
transition-property: all;
line-height: 1;
display: flex;
// border-right: none;
svg {
height: 1em;
}
span,
button {
display: block;
padding: 7px 12px;
line-height: 1;
color: @color-theme;
font-size: 13px;
}
&.active {
span {
background-color: @color-pagination-select;
}
}
button {
background-color: transparent;
border: none;
cursor: pointer;
outline: none;
transition: background-color .3s ease;
&:hover {
background-color: @color-pagination-hover;
}
&:active {
background-color: @color-pagination-active;
}
}
&.disabled {
span {
opacity: .3;
}
}
&:first-child {
span, button {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
// border-right: .0625rem solid @theme_line;
}
&:last-child {
span, button {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
// border-right: .0625rem solid @theme_line;
}
&:first-child, &:last-child, &.first, &.last {
span,
button {
line-height: 0;
}
}
}
}
}
each(@themes, {
:global(#container.@{value}) {
.pagination {
background-color: ~'@{color-@{value}-pagination-background}';
ul {
li {
color: ~'@{color-@{value}-theme}';
span,
button {
color: ~'@{color-@{value}-theme}';
}
&.active {
span {
background-color: ~'@{color-@{value}-pagination-select}';
}
}
button {
&:hover {
background-color: ~'@{color-@{value}-pagination-hover}';
}
&:active {
background-color: ~'@{color-@{value}-pagination-active}';
}
}
}
}
}
}
})
</style>
@@ -0,0 +1,251 @@
<template lang="pug">
div(:class="[$style.search, focus ? $style.active : '', big ? $style.big : '', small ? $style.small : '']")
div(:class="$style.form")
input(:placeholder="placeholder" v-model.trim="text"
@focus="handleFocus" @blur="handleBlur" @input="$emit('input', text)"
@change="sendEvent('change')"
@keyup.enter="handleSearch")
button(type="button" @click="handleSearch")
slot
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 30.239 30.239' space='preserve')
use(xlink:href='#icon-search')
//- transition(name="custom-classes-transition"
//- enter-active-class="animated flipInX"
//- leave-active-class="animated flipOutX")
div(v-if="list" :class="$style.list" :style="listStyle")
ul(ref="dom_list")
li(v-for="(item, index) in list" :key="item" @click="handleTemplistClick(index)")
span {{item}}
</template>
<script>
export default {
props: {
placeholder: {
type: String,
default: 'Search for something...',
},
list: {
type: Array,
},
visibleList: {
type: Boolean,
default: false,
},
value: {
type: String,
default: '',
},
big: {
type: Boolean,
default: false,
},
small: {
type: Boolean,
default: false,
},
},
data() {
return {
isShow: false,
text: '',
index: null,
focus: false,
listStyle: {
height: 0,
},
}
},
watch: {
list(n) {
if (!this.visibleList) return
this.$nextTick(() => {
this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
})
},
value(n) {
this.text = n
},
visibleList(n) {
n ? this.showList() : this.hideList()
},
},
methods: {
handleTemplistClick(index) {
this.sendEvent('listClick', index)
},
handleFocus() {
this.focus = true
this.sendEvent('focus')
},
handleBlur() {
this.focus = false
this.sendEvent('blur')
},
handleSearch() {
this.hideList()
this.sendEvent('submit')
},
showList() {
this.isShow = true
this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
},
hideList() {
this.isShow = false
this.listStyle.height = 0
},
sendEvent(action, data) {
this.$emit('event', {
action,
data,
})
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.search {
border-radius: 3px;
transition: box-shadow .4s ease, background-color @transition-theme;
display: flex;
flex-flow: column nowrap;
width: 240px;
background-color: @color-search-form-background;
&.active {
box-shadow: 0 1px 5px 0 rgba(0,0,0,.2);
.form {
input {
border-bottom-left-radius: 0;
}
button {
border-bottom-right-radius: 0;
}
}
}
.form {
display: flex;
height: @height-toolbar / 2;
position: relative;
input {
flex: auto;
// border: 1px solid;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
background-color: transparent;
// border-bottom: 2px solid @color-theme;
// border-color: @color-theme;
border: none;
outline: none;
// height: @height-toolbar * .7;
padding: 0 5px;
overflow: hidden;
&::placeholder {
color: @color-btn;
}
}
button {
flex: none;
border: none;
// background-color: @color-search-form-background;
background-color: transparent;
outline: none;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
cursor: pointer;
height: 100%;
padding: 5px 7px;
color: @color-btn;
transition: background-color .2s ease;
&:hover {
background-color: @color-theme-hover;
}
&:active {
background-color: @color-theme-active;
}
}
}
.list {
// background-color: @color-search-form-background;
font-size: 13px;
transition: .3s ease;
height: 0;
transition-property: height;
overflow: hidden;
li {
cursor: pointer;
padding: 8px 5px;
transition: background-color .2s ease;
line-height: 1.3;
span {
.mixin-ellipsis-2;
}
&:hover {
background-color: @color-search-list-hover;
}
&:last-child {
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
}
}
}
}
.big {
width: 500px;
// input {
// line-height: 30px;
// }
.form {
height: 30px;
button {
padding: 6px 10px;
}
}
}
each(@themes, {
:global(#container.@{value}) {
.search {
background-color: ~'@{color-@{value}-search-form-background}';
&.active {
box-shadow: 0 1px 5px 0 rgba(0,0,0,.2);
}
.form {
input {
&::placeholder {
color: ~'@{color-@{value}-btn}';
}
}
button {
color: ~'@{color-@{value}-btn}';
&:hover {
background-color: ~'@{color-@{value}-theme-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme-active}';
}
}
}
.list {
li {
&:hover {
background-color: ~'@{color-@{value}-search-list-hover}';
}
}
}
}
}
})
</style>
+162
View File
@@ -0,0 +1,162 @@
<template lang="pug">
div(:class="[$style.select, show ? $style.active : '']")
div(:class="$style.label" ref="dom_btn" @click="handleShow") {{value ? itemName ? list.find(l => l.id == value).name : value : ''}}
ul(:class="$style.list")
li(v-for="item in list" @click="handleClick(itemKey ? item[itemKey] : item)") {{itemName ? item[itemName] : item}}
</template>
<script>
// import { isChildren } from '../../utils'
export default {
props: {
list: {
type: Array,
default() {
return []
},
},
value: {
type: [String, Number],
},
itemName: {
type: String,
},
itemKey: {
type: String,
},
},
data() {
return {
show: false,
}
},
mounted() {
document.addEventListener('click', this.handleHide)
},
beforeDestroy() {
document.removeEventListener('click', this.handleHide)
},
methods: {
handleHide(e) {
// if (e && e.target.parentNode != this.$refs.dom_list && this.show) return this.show = false
if (e && e.target == this.$refs.dom_btn) return
setTimeout(() => {
this.show = false
}, 50)
},
handleClick(item) {
if (item === this.value) return
this.$emit('input', item)
this.$emit('change', item)
},
handleShow() {
this.show = !this.show
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.select {
font-size: 12px;
position: relative;
&.active {
.list {
transform: scaleY(1);
opacity: 1;
}
}
}
.label {
padding: 8px 15px;
// background-color: @color-btn-background;
transition: background-color @transition-theme;
border-top: 2px solid @color-tab-border-bottom;
border-left: 2px solid @color-tab-border-bottom;
box-sizing: border-box;
text-align: center;
border-top-left-radius: 4px;
color: @color-btn;
cursor: pointer;
&:hover {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
.list {
position: absolute;
top: 100%;
left: 0;
border-bottom: 2px solid @color-tab-border-bottom;
border-left: 2px solid @color-tab-border-bottom;
border-bottom-left-radius: 4px;
background-color: @color-theme_2-background_2;
opacity: 0;
transform: scaleY(0);
transform-origin: 0 0 0;
transition: .25s ease;
transition-property: transform, opacity;
z-index: 10;
li {
cursor: pointer;
padding: 8px 15px;
// color: @color-btn;
text-align: center;
outline: none;
transition: background-color @transition-theme;
background-color: @color-btn-background;
box-sizing: border-box;
&:hover {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
}
each(@themes, {
:global(#container.@{value}) {
.label {
border-top-color: ~'@{color-@{value}-tab-border-bottom}';
border-left-color: ~'@{color-@{value}-tab-border-bottom}';
color: ~'@{color-@{value}-btn}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
.list {
border-bottom-color: ~'@{color-@{value}-tab-border-bottom}';
border-left-color: ~'@{color-@{value}-tab-border-bottom}';
background-color: ~'@{color-@{value}-theme_2-background_2}';
li {
// color: ~'@{color-@{value}-btn}';
background-color: ~'@{color-@{value}-btn-background}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
}
}
})
</style>
@@ -0,0 +1,239 @@
<template lang="pug">
div(:class="$style.songList")
transition(enter-active-class="animated fadeIn" leave-active-class="animated fadeOut")
div(v-if="list.length" :class="$style.list")
div(:class="$style.thead")
table
thead
tr
th.nobreak.center(style="width: 37px;")
material-checkbox(id="search_select_all" v-model="isSelectAll" @change="handleSelectAllData"
:indeterminate="isIndeterminate" :title="isSelectAll && !isIndeterminate ? '全不选' : '全选'")
th.nobreak(style="width: 25%;") 歌曲名
th.nobreak(style="width: 20%;") 歌手
th.nobreak(style="width: 20%;") 专辑
th.nobreak(style="width: 20%;") 操作
th.nobreak(style="width: 10%;") 时长
div.scroll(:class="$style.tbody" ref="dom_scrollContent")
table
tbody
tr(v-for='(item, index) in list' :key='item.songmid' @click="handleDoubleClick(index)")
td.nobreak.center(style="width: 37px;" @click.stop)
material-checkbox(:id="index.toString()" v-model="selectdList" @change="handleChangeSelect" :value="item")
td.break(style="width: 25%;")
| {{item.name}}
span.badge.badge-info(v-if="item._types['320k']") 高品质
span.badge.badge-success(v-if="item._types.ape || item._types.flac") 无损
td.break(style="width: 20%;") {{item.singer}}
td.break(style="width: 20%;") {{item.albumName}}
td(style="width: 20%; padding-left: 0; padding-right: 0;")
material-list-buttons(:index="index" :search-btn="true"
:remove-btn="false" @btn-click="handleListBtnClick"
:listAdd-btn="item.source == 'kw' || (!isAPITemp)"
:play-btn="item.source == 'kw' || (!isAPITemp)"
:download-btn="item.source == 'kw' || (!isAPITemp)")
//- button.btn-info(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k'] || item._types.flac" @click.stop='openDownloadModal(index)') 下载
//- button.btn-secondary(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k']" @click.stop='testPlay(index)') 试听
//- button.btn-success(type='button' v-if="(item._types['128k'] || item._types['192k'] || item._types['320k']) && userInfo" @click.stop='showListModal(index)') +
td(style="width: 10%;") {{item.interval || '--/--'}}
div(:class="$style.pagination")
material-pagination(:count="total" :limit="limit" :page="page" @btn-click="handleTogglePage")
div(v-else :class="$style.noitem")
p(v-html="noItem")
material-flow-btn(:show="isShowEditBtn && (source == 'kw' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick")
</template>
<script>
import { mapGetters } from 'vuex'
import { scrollTo } from '../../utils'
export default {
name: 'MaterialSongList',
model: {
prop: 'selectdData',
event: 'input',
},
props: {
list: {
type: Array,
default() {
return []
},
},
page: {
type: Number,
required: true,
},
limit: {
type: Number,
required: true,
},
total: {
type: Number,
required: true,
},
selectdData: {
type: Array,
required: true,
},
source: {
type: String,
},
noItem: {
type: String,
default: '列表加载中...',
},
},
computed: {
...mapGetters(['setting']),
isAPITemp() {
return this.setting.apiSource == 'temp'
},
},
watch: {
selectdList(n) {
const len = n.length
if (len) {
this.isSelectAll = true
this.isIndeterminate = len !== this.list.length
this.isShowEditBtn = true
} else {
this.isSelectAll = false
this.isShowEditBtn = false
}
},
selectdData(n) {
const len = n.length
if (len) {
this.isSelectAll = true
this.isIndeterminate = len !== this.list.length
this.isShowEditBtn = true
this.selectdList = [...n]
} else {
this.isSelectAll = false
this.isShowEditBtn = false
this.resetSelect()
}
},
list(n) {
this.resetSelect()
if (!this.list.length) return
this.$nextTick(() => scrollTo(this.$refs.dom_scrollContent, 0))
},
},
data() {
return {
clickTime: 0,
clickIndex: -1,
isSelectAll: false,
isIndeterminate: false,
isShowEditBtn: false,
selectdList: [],
}
},
methods: {
handleDoubleClick(index) {
if (
window.performance.now() - this.clickTime > 400 ||
this.clickIndex !== index
) {
this.clickTime = window.performance.now()
this.clickIndex = index
return
}
this.emitEvent((this.source == 'kw' || !this.isAPITemp) ? 'testPlay' : 'search', index)
this.clickTime = 0
this.clickIndex = -1
},
handleListBtnClick(info) {
this.emitEvent('listBtnClick', info)
},
handleSelectAllData(isSelect) {
this.selectdList = isSelect ? [...this.list] : []
this.$emit('input', [...this.selectdList])
},
resetSelect() {
this.selectdList = false
this.selectdList = []
},
handleTogglePage(page) {
this.emitEvent('togglePage', page)
},
handleFlowBtnClick(action) {
this.emitEvent('flowBtnClick', action)
},
emitEvent(action, data) {
this.$emit('action', { action, data })
},
handleChangeSelect() {
this.$emit('input', [...this.selectdList])
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.song-list {
overflow: hidden;
height: 100%;
display: flex;
flex-flow: column nowrap;
}
.list {
position: relative;
font-size: 14px;
overflow: hidden;
display: flex;
flex-flow: column nowrap;
}
.thead {
flex: none;
}
.tbody {
flex: auto;
overflow-y: auto;
td {
font-size: 12px;
:global(.badge) {
margin-right: 3px;
&:first-child {
margin-left: 3px;
}
&:last-child {
margin-right: 0;
}
}
}
}
.pagination {
text-align: center;
padding: 15px 0;
// left: 50%;
// transform: translateX(-50%);
}
.noitem {
position: relative;
height: 100%;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
p {
font-size: 24px;
color: @color-theme_2-font-label;
}
}
each(@themes, {
:global(#container.@{value}) {
.noitem {
p {
color: ~'@{color-@{value}-theme_2-font-label}';
}
}
}
})
</style>
+221
View File
@@ -0,0 +1,221 @@
<template lang="pug">
div.scroll(:class="$style.tab")
//- div(:class="$style.content")
ul
li(v-for="item in list" :key="itemKey ? item[itemKey] : item" :class="value === (itemKey ? item[itemKey] : item) ? $style.active : ''")
button(type="button"
@click="handleClick(itemKey ? item[itemKey] : item)") {{ itemName ? item[itemName] : item }}
//- div(:class="$style.control")
div(:class="$style.left")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve')
use(xlink:href='#icon-left')
div(:class="$style.right")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve')
use(xlink:href='#icon-right')
</template>
<script>
export default {
props: {
list: {
type: Array,
default() {
return []
},
},
value: {
type: [String, Number],
},
itemName: {
type: String,
},
itemKey: {
type: String,
},
},
methods: {
handleClick(item) {
if (item === this.value) return
this.$emit('input', item)
this.$emit('change', item)
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.tab {
// overflow: hidden;
// display: flex;
// flex-flow: row nowrap;
overflow-x: auto;
ul {
display: flex;
flex: none;
border-bottom: 2px solid @color-tab-border-bottom;
transition: border-bottom-color @transition-theme;
li {
position: relative;
flex: none;
margin-bottom: -2px;
border-top: 2px solid @color-tab-border-top;
border-left: 2px solid @color-tab-btn-background;
border-right: 2px solid @color-tab-btn-background;
// box-sizing: border-box;
transition: border-color @transition-theme;
margin-left: -2px;
&::after {
content: ' ';
display: block;
width: 50%;
height: 2px;
position: absolute;
bottom: 0;
left: 0;
background-color: @color-tab-border-bottom;
transition: width @transition-theme;
}
&::before {
content: ' ';
display: block;
width: 50%;
height: 2px;
position: absolute;
bottom: 0;
right: 0;
background-color: @color-tab-border-bottom;
transition: width @transition-theme;
}
&:first-child {
border-left: none;
margin-left: 0;
button {
border-top-left-radius: 3px;
// border-bottom-left-radius: 4px;
}
}
&:last-child {
border-right: 2px solid @color-tab-border-top;
border-top-right-radius: 4px;
button {
border-top-right-radius: 3px;
// border-bottom-right-radius: 4px;
}
}
button {
display: inline-block;
border: none;
cursor: pointer;
padding: 5px 10px 7px;
font-size: 12px;
// color: @color-btn;
outline: none;
transition: background-color @transition-theme;
background-color: @color-tab-btn-background;
}
&:hover {
// border-left-color: @color-theme_2-hover;
// border-right-color: @color-theme_2-hover;
button {
background-color: @color-tab-btn-background-hover;
}
}
&:active {
// border-left-color: @color-theme_2-active;
// border-right-color: @color-theme_2-active;
button {
background-color: @color-theme_2-active;
}
}
&.active {
border-bottom-color: @color-theme_2-background_1;
border-top-color: @color-tab-border-bottom;
border-left-color: @color-tab-border-bottom;
border-right-color: @color-tab-border-bottom;
&::after {
width: 0;
}
&::before {
width: 0;
}
button {
background-color: @color-theme_2-background_1;
}
}
}
}
}
// .control {
// flex: none;
// border-bottom: 2px solid @color-tab-border-bottom;
// width: 1em;
// svg {
// width: .85em;
// }
// }
// .left, .right {
// line-height: 0;
// }
// .content {
// flex: auto;
// overflow: hidden;
// }
each(@themes, {
:global(#container.@{value}) {
.tab {
ul {
border-bottom-color: ~'@{color-@{value}-tab-border-bottom}';
li {
border-top-color: ~'@{color-@{value}-tab-border-top}';
border-left-color: ~'@{color-@{value}-tab-btn-background}';
border-right-color: ~'@{color-@{value}-tab-btn-background}';
&::after {
background-color: ~'@{color-@{value}-tab-border-bottom}';
}
&::before {
background-color: ~'@{color-@{value}-tab-border-bottom}';
}
&:last-child {
border-right-color: ~'@{color-@{value}-tab-border-top}';
}
button {
// color: ~'@{color-@{value}-btn}';
background-color: ~'@{color-@{value}-tab-btn-background}';
}
&:hover {
// border-left-color: ~'@{color-@{value}-theme_2-hover}';
// border-right-color: ~'@{color-@{value}-theme_2-hover}';
button {
background-color: ~'@{color-@{value}-tab-btn-background-hover}';
}
}
&:active {
// border-left-color: ~'@{color-@{value}-theme_2-active}';
// border-right-color: ~'@{color-@{value}-theme_2-active}';
button {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
&.active {
border-bottom-color: ~'@{color-@{value}-theme_2}';
border-top-color: ~'@{color-@{value}-tab-border-bottom}';
border-left-color: ~'@{color-@{value}-tab-border-bottom}';
border-right-color: ~'@{color-@{value}-tab-border-bottom}';
button {
background-color: ~'@{color-@{value}-theme_2-background_1}';
}
}
}
}
}
}
})
</style>
@@ -0,0 +1,231 @@
<template lang="pug">
div(:class="[$style.tagList, show ? $style.active : '']")
div(:class="$style.label" ref="dom_btn" @click="handleShow")
span {{value.name}}
div(:class="$style.icon")
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.847 451.847' space='preserve')
use(xlink:href='#icon-down')
div.scroll(:class="$style.list" @click.stop ref="dom_list")
div(:class="$style.tag" @click="handleClick(null)") 默认
dl(v-for="type in list")
dt(:class="$style.type") {{type.name}}
dd(:class="$style.tag" v-for="tag in type.list" @click="handleClick(tag)") {{tag.name}}
</template>
<script>
import { isChildren } from '../../utils'
export default {
props: {
list: {
type: Array,
default() {
return []
},
},
value: {
type: Object,
},
},
data() {
return {
show: false,
}
},
mounted() {
document.addEventListener('click', this.handleHide)
},
beforeDestroy() {
document.removeEventListener('click', this.handleHide)
},
methods: {
handleHide(e) {
// if (e && e.target.parentNode != this.$refs.dom_list && this.show) return this.show = false
if (e && (e.target == this.$refs.dom_btn || isChildren(this.$refs.dom_btn, e.target))) return
setTimeout(() => {
this.show = false
}, 50)
},
handleClick(item) {
if (!item) {
item = {
name: '默认',
id: null,
}
}
if (item.id === this.value.id) return this.handleShow()
this.$emit('input', item)
this.$emit('change', item)
this.handleShow()
},
handleShow() {
this.show = !this.show
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.tag-list {
font-size: 12px;
position: relative;
&.active {
.label {
.icon {
svg{
transform: rotate(180deg);
}
}
}
.list {
opacity: 1;
transform: scaleY(1);
}
}
}
.label {
padding: 8px 15px;
// background-color: @color-btn-background;
transition: background-color @transition-theme;
border-top: 2px solid @color-tab-border-bottom;
// border-left: 2px solid @color-tab-border-bottom;
box-sizing: border-box;
text-align: center;
// border-top-left-radius: 3px;
color: @color-btn;
cursor: pointer;
display: flex;
span {
flex: auto;
}
.icon {
flex: none;
margin-left: 7px;
line-height: 0;
svg {
width: 1em;
transition: transform .2s ease;
transform: rotate(0);
}
}
&:hover {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
.list {
position: absolute;
top: 100%;
width: 646px;
left: 0;
border-bottom: 2px solid @color-tab-border-bottom;
border-right: 2px solid @color-tab-border-bottom;
border-bottom-right-radius: 5px;
background-color: @color-theme_2-background_2;
opacity: 0;
transform: scaleY(0);
overflow-y: auto;
transform-origin: 0 0 0;
max-height: 250px;
transition: .25s ease;
transition-property: transform, opacity;
z-index: 10;
padding: 10px;
box-sizing: border-box;
li {
cursor: pointer;
padding: 8px 15px;
// color: @color-btn;
text-align: center;
outline: none;
transition: background-color @transition-theme;
background-color: @color-btn-background;
box-sizing: border-box;
&:hover {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
}
.type {
padding-top: 10px;
padding-bottom: 3px;
color: #999;
}
.tag {
display: inline-block;
margin: 5px;
background-color: @color-btn-background;
padding: 8px 10px;
border-radius: @radius-progress-border;
transition: background-color @transition-theme;
cursor: pointer;
&:hover {
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
}
each(@themes, {
:global(#container.@{value}) {
.label {
border-top-color: ~'@{color-@{value}-tab-border-bottom}';
// border-left-color: ~'@{color-@{value}-tab-border-bottom}';
color: ~'@{color-@{value}-btn}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
.list {
border-bottom-color: ~'@{color-@{value}-tab-border-bottom}';
border-right-color: ~'@{color-@{value}-tab-border-bottom}';
background-color: ~'@{color-@{value}-theme_2-background_2}';
// border-left-color: ~'@{color-@{value}-tab-border-bottom}';
li {
// color: ~'@{color-@{value}-btn}';
background-color: ~'@{color-@{value}-btn-background}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
}
.tag {
background-color: ~'@{color-@{value}-btn-background}';
&:hover {
background-color: ~'@{color-@{value}-theme_2-hover}';
}
&:active {
background-color: ~'@{color-@{value}-theme_2-active}';
}
}
}
})
</style>
@@ -0,0 +1,210 @@
<template lang="pug">
material-modal(:show="version.showModal" @close="handleClose")
main(:class="$style.main" v-if="version.newVersion")
h2 {{ version.isError ? (isUnknow ? '❓ 版本信息获取失败 ❓' : '🌟发现新版本🌟') : '🚀程序更新🚀'}}
div.scroll(:class="$style.info")
div(:class="$style.current")
h3 最新版本:{{version.newVersion.version}}
h3 当前版本:{{version.version}}
h3 版本变化:
p(:class="$style.desc" v-html="version.newVersion.desc")
div(:class="[$style.history, $style.desc]" v-if="history.length")
h3 历史版本:
div(:class="$style.item" v-for="ver in history")
h4 v{{ver.version}}
p(v-html="ver.desc")
div(:class="$style.footer" v-if="version.isError")
div(:class="$style.desc" v-if="!isUnknow")
p 发现有新版本啦,但是自动更新功能出问题了,
p
| 你现在可以选择继续使用当前版本或
strong 去发布页下载新版本
| ,
p
| 国内Windows/MAC用户推荐到
strong.hover.underline(@click="handleOpenUrl('https://www.lanzous.com/b906260/')") 网盘(点击打开)
| 下载,密码:
strong.hover(@click="handleCopy('glqw')" title="点击复制") glqw
div(:class="$style.btns")
material-btn(:class="$style.btn" @click.onec="handleIgnoreClick") 忽略该版本
material-btn(:class="$style.btn" @click.onec="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop#readme')") 去软件发布页
div(:class="$style.footer" v-else)
div(:class="$style.desc")
p 新版本已下载完毕,
p
| 你可以选择
strong 立即重启更新
| 或稍后
strong 关闭程序时
| 自动更新~
material-btn(:class="$style.btn" @click.onec="handleRestartClick") 立即重启更新
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
import { rendererSend } from '../../../common/icp'
import { checkVersion, openUrl, clipboardWriteText } from '../../utils'
export default {
computed: {
...mapGetters(['version', 'setting']),
history() {
if (!this.version.newVersion) return []
let arr = []
let currentVer = this.version.version
this.version.newVersion.history.forEach(ver => {
if (checkVersion(currentVer, ver.version)) arr.push(ver)
})
return arr
},
isUnknow() {
return this.version.newVersion.version == '0.0.0'
},
},
methods: {
...mapMutations(['setVersionModalVisible', 'setSetting']),
handleClose() {
this.setVersionModalVisible({
isShow: false,
})
},
handleIgnoreClick(event) {
this.handleClose()
// event.target.disabled = true
this.setSetting(Object.assign({}, this.setting, { ignoreVersion: this.version.newVersion.version }))
},
handleOpenUrl(url) {
openUrl(url)
},
handleRestartClick(event) {
this.handleClose()
event.target.disabled = true
rendererSend('quit-update')
},
handleCopy(text) {
clipboardWriteText(text)
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.main {
position: relative;
padding: 15px;
max-width: 450px;
min-width: 300px;
display: flex;
flex-flow: column nowrap;
justify-content: center;
overflow: hidden;
// overflow-y: auto;
* {
box-sizing: border-box;
}
h2 {
flex: 0 0 none;
font-size: 16px;
color: @color-theme_2-font;
line-height: 1.3;
text-align: center;
margin-bottom: 15px;
}
h3 {
font-size: 14px;
line-height: 1.3;
}
}
.info {
flex: 1 1 auto;
font-size: 13px;
line-height: 1.5;
overflow-y: auto;
height: 100%;
padding-right: 5px;
}
.current {
> p {
padding-left: 15px;
}
}
.desc {
h3, h4 {
font-weight: bold;
}
h3 {
padding: 5px 0 3px;
}
ul {
list-style: initial;
padding-inline-start: 30px;
}
}
.history {
h3 {
padding-top: 15px;
}
.item {
h3 {
padding: 5px 0 3px;
}
padding: 0 15px;
+ .item {
padding-top: 15px;
}
h4 {
font-weight: 700;
}
> p {
padding-left: 15px;
}
}
}
.footer {
flex: 0 0 none;
.desc {
font-size: 12px;
padding-top: 10px;
color: @color-theme;
line-height: 1.2;
}
}
.btn {
display: block;
width: 100%;
}
.btns {
display: grid;
padding-top: 10px;
grid-template-columns: 1fr 1fr;
grid-gap: 0 10px;
}
each(@themes, {
:global(#container.@{value}) {
.main {
h2 {
color: ~'@{color-@{value}-theme_2-font}';
}
}
.footer {
.desc {
color: ~'@{color-@{value}-theme}';
}
}
}
})
</style>
@@ -0,0 +1,103 @@
<template lang="pug">
material-modal(:show="show" :bg-close="bgClose" @close="handleClose")
main(:class="$style.main")
h2
| 添加&nbsp;
span(:class="$style.name") {{this.musicInfo && `${musicInfo.name}`}}
| &nbsp;到...
material-btn(:class="$style.btn" :title="`把该歌曲添加到 ${item.name}`" :key="item.id" @click="handleClick(index)" v-for="(item, index) in lists") {{item.name}}
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
props: {
show: {
type: Boolean,
default: false,
},
musicInfo: {
type: Object,
},
bgClose: {
type: Boolean,
default: true,
},
excludeListId: {
type: Array,
default() {
return []
},
},
},
computed: {
...mapGetters('list', ['defaultList', 'loveList', 'userList']),
lists() {
return [
this.defaultList,
this.loveList,
...this.userList,
].filter(l => !this.excludeListId.includes(l.id))
},
},
methods: {
...mapMutations('list', ['listAdd']),
handleClick(index) {
this.listAdd({ id: this.lists[index].id, musicInfo: this.musicInfo })
this.$nextTick(() => {
this.handleClose()
})
},
handleClose() {
this.$emit('close')
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.main {
padding: 15px;
max-width: 300px;
min-width: 200px;
display: flex;
flex-flow: column nowrap;
justify-content: center;
h2 {
font-size: 13px;
color: @color-theme_2-font;
line-height: 1.3;
text-align: center;
margin-bottom: 15px;
}
}
.name {
color: @color-theme;
}
.btn {
display: block;
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
each(@themes, {
:global(#container.@{value}) {
.main {
h2 {
color: ~'@{color-@{value}-theme_2-font}';
}
}
.name {
color: ~'@{color-@{value}-theme}';
}
}
})
</style>
@@ -0,0 +1,97 @@
<template lang="pug">
material-modal(:show="show" :bg-close="bgClose" @close="handleClose")
main(:class="$style.main")
h2 添加已选的 {{musicList.length}} 首歌曲到...
material-btn(:class="$style.btn" :title="`把该歌曲添加到 ${item.name}`" :key="item.id" @click="handleClick(index)" v-for="(item, index) in lists") {{item.name}}
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
props: {
show: {
type: Boolean,
default: false,
},
musicList: {
type: Array,
default() {
return []
},
},
bgClose: {
type: Boolean,
default: true,
},
excludeListId: {
type: Array,
default() {
return []
},
},
},
computed: {
...mapGetters('list', ['defaultList', 'loveList', 'userList']),
lists() {
return [
this.defaultList,
this.loveList,
...this.userList,
].filter(l => !this.excludeListId.includes(l.id))
},
},
methods: {
...mapMutations('list', ['listAddMultiple']),
handleClick(index) {
this.listAddMultiple({ id: this.lists[index].id, list: this.musicList })
this.$nextTick(() => {
this.handleClose(true)
})
},
handleClose(isSelect = false) {
this.$emit('close', isSelect)
},
},
}
</script>
<style lang="less" module>
@import '../../assets/styles/layout.less';
.main {
padding: 15px;
max-width: 300px;
min-width: 200px;
display: flex;
flex-flow: column nowrap;
justify-content: center;
h2 {
font-size: 13px;
color: @color-theme_2-font;
line-height: 1.3;
text-align: center;
margin-bottom: 15px;
}
}
.btn {
display: block;
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
each(@themes, {
:global(#container.@{value}) {
.main {
h2 {
color: ~'@{color-@{value}-theme_2-font}';
}
}
}
})
</style>
+11
View File
@@ -0,0 +1,11 @@
module.exports = {
development: {
},
production: {
},
ajax: {
timeout: 15000, // ajax请求超时时间
},
}
+27
View File
@@ -0,0 +1,27 @@
import Vue from 'vue'
import { sync } from 'vuex-router-sync'
// Components
import './components'
// Plugins
import './plugins'
import App from './App'
import router from './route'
import store from './store'
sync(store, router)
if (!process.env.IS_WEB) {
}
Vue.config.productionTip = false
new Vue({
router,
store,
el: '#root',
render: h => h(App),
})
+1
View File
@@ -0,0 +1 @@
// import './axios'

Some files were not shown because too many files have changed in this diff Show More