parent
4dc34a48f4
commit
14f7bd10a3
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -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"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
# 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/
|
||||
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
|
|
@ -0,0 +1,11 @@
|
|||
# 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.1.0] - 2019-8-16
|
||||
|
||||
* 0.1.0版本发布
|
44
README.md
44
README.md
|
@ -1,2 +1,42 @@
|
|||
# lx-music-desktop
|
||||
洛雪音乐助手桌面版
|
||||
# 洛雪音乐助手桌面版
|
||||
|
||||
[![GitHub release][1]][2]
|
||||
[![Build status][3]][4]
|
||||
[![GitHub All Releases Download][5]][6]
|
||||
[![dev branch][7]][8]
|
||||
|
||||
[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/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
|
||||
|
||||
## 说明
|
||||
|
||||
一个基于 Electron + Vue 开发的 Windows 版音乐软件。
|
||||
|
||||
所用技术栈:
|
||||
|
||||
- Electron 7.x
|
||||
- Vue 2.x
|
||||
|
||||
其他说明:TODO
|
||||
|
||||
感谢 <https://github.com/messoer> 提供的部分音乐API!
|
||||
|
||||
## 使用方法
|
||||
|
||||
```bash
|
||||
# 开发模式
|
||||
npm run dev
|
||||
|
||||
# 构建免安装版
|
||||
npm run pack:dir
|
||||
|
||||
# 构建安装包
|
||||
npm run pack
|
||||
|
||||
```
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
platform:
|
||||
- x64
|
||||
|
||||
cache:
|
||||
- node_modules
|
||||
- '%APPDATA%\npm-cache'
|
||||
# - '%USERPROFILE%\.electron'
|
||||
|
||||
install:
|
||||
- ps: Install-Product node 12 x64
|
||||
- npm install
|
||||
|
||||
build_script:
|
||||
- npm run pub:gh
|
||||
|
||||
test: off
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
|
@ -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',
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
target: 'electron-main',
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
libraryTarget: 'commonjs2',
|
||||
path: path.join(__dirname, '../../dist/electron'),
|
||||
},
|
||||
externals: [
|
||||
// suppress electron-debug warning
|
||||
// see https://github.com/SimulatedGREG/electron-vue/issues/498
|
||||
{ 'electron-debug': 'electron-debug' },
|
||||
],
|
||||
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,
|
||||
},
|
||||
}
|
|
@ -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(),
|
||||
],
|
||||
})
|
|
@ -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"',
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
|
@ -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: '#cheap-module-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,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -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()
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
module.exports = {
|
||||
// preserveWhitepace: true,
|
||||
compilerOptions: {
|
||||
whitespace: 'preserve',
|
||||
},
|
||||
extractCSS: !isDev,
|
||||
// cssModules: {
|
||||
// localIndetName: '',
|
||||
// },
|
||||
}
|
|
@ -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',
|
||||
}),
|
||||
],
|
||||
}
|
|
@ -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: '#cheap-module-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,
|
||||
},
|
||||
})
|
||||
|
|
@ -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',
|
||||
},
|
||||
})
|
|
@ -0,0 +1,5 @@
|
|||
本程序仅用于学习交流使用!
|
||||
请勿用于商业用途!!
|
||||
使用本软件造成的一切后果由使用者承担!
|
||||
|
||||
By: 落雪无痕
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,141 @@
|
|||
{
|
||||
"name": "lx-music-desktop",
|
||||
"version": "0.1.0",
|
||||
"description": "一个免费的音乐下载助手",
|
||||
"main": "./dist/electron/main.js",
|
||||
"scripts": {
|
||||
"publish": "node publish",
|
||||
"pub:gh": "node build-config/pack.js && electron-builder --win -p always",
|
||||
"pack": "node build-config/pack.js && electron-builder",
|
||||
"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/**/*"
|
||||
],
|
||||
"win": {
|
||||
"icon": "src/static/icons/lunch.ico",
|
||||
"legalTrademarks": "lyswhut"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"language": "2052",
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"differentialPackage": true,
|
||||
"license": "./license.txt"
|
||||
},
|
||||
"publish": [
|
||||
{
|
||||
"provider": "github",
|
||||
"owner": "lyswhut",
|
||||
"repo": "lx-music-desktop"
|
||||
}
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lyswhut/lx-music-desktop.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "lyswhut",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/lyswhut/lx-music-desktop/issues"
|
||||
},
|
||||
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/polyfill": "^7.4.4",
|
||||
"@babel/preset-env": "^7.5.5",
|
||||
"autoprefixer": "^9.6.1",
|
||||
"babel-eslint": "^10.0.2",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-minify-webpack-plugin": "^0.3.1",
|
||||
"babel-preset-minify": "^0.5.0",
|
||||
"cfonts": "^2.4.4",
|
||||
"chalk": "^2.4.2",
|
||||
"copy-webpack-plugin": "^5.0.4",
|
||||
"core-js": "^3.2.1",
|
||||
"cos-nodejs-sdk-v5": "^2.5.11",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^3.2.0",
|
||||
"del": "^3.0.0",
|
||||
"electron": "^6.0.2",
|
||||
"electron-builder": "^21.2.0",
|
||||
"electron-debug": "^3.0.1",
|
||||
"electron-devtools-installer": "^2.2.4",
|
||||
"eslint": "^6.1.0",
|
||||
"eslint-config-standard": "^13.0.1",
|
||||
"eslint-formatter-friendly": "^7.0.0",
|
||||
"eslint-loader": "^2.2.1",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-node": "^9.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"file-loader": "^4.2.0",
|
||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"less": "^3.9.0",
|
||||
"less-loader": "^5.0.0",
|
||||
"markdown-it": "^9.1.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.5",
|
||||
"stylus-loader": "^3.0.2",
|
||||
"terser-webpack-plugin": "^1.4.1",
|
||||
"url-loader": "^2.1.0",
|
||||
"vue-loader": "^15.7.1",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"webpack": "^4.39.2",
|
||||
"webpack-cli": "^3.3.6",
|
||||
"webpack-dev-server": "^3.8.0",
|
||||
"webpack-hot-middleware": "^2.25.0",
|
||||
"webpack-merge": "^4.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.0",
|
||||
"electron-log": "^3.0.7",
|
||||
"electron-store": "^4.0.0",
|
||||
"electron-updater": "^4.1.2",
|
||||
"js-htmlencode": "^0.3.0",
|
||||
"lrc-file-parser": "^0.1.12",
|
||||
"node-downloader-helper": "^1.0.10",
|
||||
"request": "^2.88.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-router": "^3.1.2",
|
||||
"vuex": "^3.1.1",
|
||||
"vuex-electron": "^1.0.3",
|
||||
"vuex-router-sync": "^5.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
const autoprefixer = require('autoprefixer')
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
autoprefixer(),
|
||||
],
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
* 0.1.0版本发布
|
|
@ -0,0 +1,49 @@
|
|||
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)
|
||||
|
||||
try {
|
||||
console.log(chalk.blue('Clearing assets...'))
|
||||
await clearAssets()
|
||||
console.log(chalk.green('Assets clear complated...'))
|
||||
|
||||
// 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()
|
|
@ -0,0 +1,8 @@
|
|||
const del = require('del')
|
||||
// const copyFile = require('./copyFile')
|
||||
|
||||
module.exports = () => {
|
||||
del.sync(['publish/assets/*'])
|
||||
// return copyFile(false)
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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('没有需要上传的文件'))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
module.exports = {
|
||||
secretId: '',
|
||||
secretKey: '',
|
||||
|
||||
bucket: '', // 存储桶
|
||||
region: '', // 区域
|
||||
prefix: '', // 路径
|
||||
}
|
|
@ -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
|
||||
})
|
||||
})
|
||||
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"version": "0.1.0",
|
||||
"desc": "0.1.0版本发布",
|
||||
"history": []
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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= require('../package.json').name
|
||||
|
||||
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, '/static').replace(/\\/g, '\\\\')
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
require('./request')
|
||||
require('./appName')
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
const { mainOn } = require('../../common/icp')
|
||||
|
||||
module.exports = win => {
|
||||
mainOn('progress', (event, params) => {
|
||||
// console.log(params)
|
||||
win.setProgressBar(params.status, {
|
||||
mode: params.mode || 'normal',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -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 = fatchData(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 fatchData = (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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
const { mainOn } = require('../../common/icp')
|
||||
|
||||
module.exports = win => {
|
||||
mainOn('min', event => {
|
||||
if (win) {
|
||||
win.minimize()
|
||||
}
|
||||
})
|
||||
// mainOn('max', event => {
|
||||
// if (win) {
|
||||
// win.maximize()
|
||||
// }
|
||||
// })
|
||||
mainOn('close', event => {
|
||||
if (win) {
|
||||
win.close()
|
||||
}
|
||||
})
|
||||
}
|
|
@ -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')
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
const { app, BrowserWindow } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
require('./events')
|
||||
const progressBar = require('./events/progressBar')
|
||||
const trafficLight = require('./events/trafficLight')
|
||||
const autoUpdate = require('./utils/autoUpdate')
|
||||
|
||||
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
|
||||
|
||||
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 = new BrowserWindow({
|
||||
height: 590,
|
||||
useContentSize: true,
|
||||
width: 920,
|
||||
frame: false,
|
||||
transparent: true,
|
||||
icon: path.join(global.__static, 'icons/lunch.ico'),
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
fullscreenable: false,
|
||||
webPreferences: {
|
||||
// contextIsolation: true,
|
||||
webSecurity: !isDev,
|
||||
nodeIntegration: true,
|
||||
},
|
||||
})
|
||||
|
||||
mainWindow.loadURL(winURL)
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
|
||||
// mainWindow.webContents.openDevTools()
|
||||
|
||||
trafficLight(mainWindow)
|
||||
progressBar(mainWindow)
|
||||
if (!isDev) autoUpdate(mainWindow)
|
||||
}
|
||||
|
||||
app.once('ready', createWindow)
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (mainWindow === null) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
const log = require('electron-log')
|
||||
const { autoUpdater } = require('electron-updater')
|
||||
const { mainOn } = require('../../common/icp')
|
||||
|
||||
autoUpdater.logger = log
|
||||
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)
|
||||
// win.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)
|
||||
|
||||
// })
|
||||
|
||||
|
||||
module.exports = win => {
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
sendStatusToWindow('Checking for update...')
|
||||
})
|
||||
autoUpdater.on('update-available', (ev, info) => {
|
||||
sendStatusToWindow('Update available.')
|
||||
})
|
||||
autoUpdater.on('update-not-available', (ev, info) => {
|
||||
sendStatusToWindow('Update not available.')
|
||||
})
|
||||
autoUpdater.on('error', (ev, err) => {
|
||||
sendStatusToWindow('Error in auto-updater.')
|
||||
})
|
||||
autoUpdater.on('download-progress', (ev, progressObj) => {
|
||||
sendStatusToWindow('Download progress...')
|
||||
})
|
||||
autoUpdater.on('update-downloaded', (ev, info) => {
|
||||
sendStatusToWindow('Update downloaded.')
|
||||
win.webContents.send('update-downloaded')
|
||||
})
|
||||
mainOn('quit-update', () => {
|
||||
setTimeout(() => {
|
||||
autoUpdater.quitAndInstall(true, true)
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
autoUpdater.checkForUpdates()
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
<template lang="pug">
|
||||
#container(v-if="isProd" :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 { mapMutations, mapGetters, mapActions } from 'vuex'
|
||||
import { rendererOn } from '../common/icp'
|
||||
window.ELECTRON_DISABLE_SECURITY_WARNINGS = process.env.ELECTRON_DISABLE_SECURITY_WARNINGS
|
||||
const win = require('electron').remote.getCurrentWindow()
|
||||
const body = document.body
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isProd: process.env.NODE_ENV === 'production',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['electronStore', 'setting', 'theme', 'version']),
|
||||
...mapGetters('list', ['defaultList']),
|
||||
...mapGetters('download', {
|
||||
downloadList: 'list',
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
watch: {
|
||||
setting: {
|
||||
handler(n) {
|
||||
this.electronStore.set('setting', n)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
defaultList: {
|
||||
handler(n) {
|
||||
this.electronStore.set('list.defaultList', n)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
downloadList: {
|
||||
handler(n) {
|
||||
this.electronStore.set('download.list', n)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['getVersionInfo']),
|
||||
...mapMutations(['setNewVersion', 'setVersionVisible']),
|
||||
...mapMutations('list', ['initDefaultList']),
|
||||
...mapMutations('download', ['updateDownloadList']),
|
||||
init() {
|
||||
if (this.isProd) {
|
||||
body.addEventListener('mouseenter', this.dieableIgnoreMouseEvents)
|
||||
body.addEventListener('mouseleave', this.enableIgnoreMouseEvents)
|
||||
}
|
||||
rendererOn('update-downloaded', () => {
|
||||
this.getVersionInfo().then(body => {
|
||||
this.setNewVersion(body)
|
||||
this.$nextTick(() => {
|
||||
this.setVersionVisible(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
this.initData()
|
||||
},
|
||||
enableIgnoreMouseEvents() {
|
||||
win.setIgnoreMouseEvents(false)
|
||||
// console.log('content enable')
|
||||
},
|
||||
dieableIgnoreMouseEvents() {
|
||||
// console.log('content disable')
|
||||
win.setIgnoreMouseEvents(true, { forward: true })
|
||||
},
|
||||
|
||||
initData() { // 初始化数据
|
||||
this.initPlayList() // 初始化播放列表
|
||||
this.initDownloadList() // 初始化下载列表
|
||||
},
|
||||
initPlayList() {
|
||||
let defaultList = this.electronStore.get('list.defaultList')
|
||||
if (defaultList) {
|
||||
defaultList.list.forEach(m => {
|
||||
m.typeUrl = {}
|
||||
})
|
||||
this.initDefaultList(defaultList)
|
||||
}
|
||||
},
|
||||
initDownloadList() {
|
||||
let downloadList = this.electronStore.get('download.list')
|
||||
if (downloadList) {
|
||||
this.updateDownloadList(downloadList)
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
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 {
|
||||
// background-color: #fff;
|
||||
padding: @shadow-app;
|
||||
user-select: none;
|
||||
height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 @shadow-app rgba(0, 0, 0, 0.5);
|
||||
// background-color: #fff;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#left {
|
||||
flex: none;
|
||||
width: @width-app-left;
|
||||
}
|
||||
#right {
|
||||
flex: auto;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
#toolbar, #player {
|
||||
flex: none;
|
||||
}
|
||||
#view {
|
||||
flex: auto;
|
||||
height: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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";
|
|
@ -0,0 +1,165 @@
|
|||
@import './reset.less';
|
||||
@import './animate.less';
|
||||
*, *::after, *::before {
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
|
||||
.nobreak {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.auto-hidden {
|
||||
.mixin-ellipsis-1;
|
||||
}
|
||||
|
||||
.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 #e0e0e0;
|
||||
// border-top: 1px solid rgba(0, 0, 0, 0.12);
|
||||
transition: background-color 0.2s ease;
|
||||
&:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
&: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;
|
||||
}
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
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}';
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
@import './colors.less';
|
||||
|
||||
@themes: green, yellow, blue, red, purple, orange, grey;
|
||||
|
||||
|
||||
// Colors
|
||||
// @color-theme: #03a678;
|
||||
@color-theme: #4daf7c;
|
||||
@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: #fff;
|
||||
@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: lighten(@color-theme, 40%);
|
||||
@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-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-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-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: lighten(@color-green-theme, 40%);
|
||||
@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-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-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-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: lighten(@color-yellow-theme, 40%);
|
||||
@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-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-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-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: lighten(@color-orange-theme, 40%);
|
||||
@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-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-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-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: lighten(@color-blue-theme, 40%);
|
||||
@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-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-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-font: darken(@color-red-theme_2, 70%);
|
||||
@color-red-theme_2-font-label: lighten(@color-red-theme, 40%);
|
||||
@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-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-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-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: lighten(@color-purple-theme, 40%);
|
||||
@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-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-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-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: lighten(@color-grey-theme, 40%);
|
||||
@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-border-top: fadeout(lighten(@color-grey-theme, 5%), 50%);
|
||||
@color-grey-tab-border-bottom: lighten(@color-grey-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;
|
|
@ -0,0 +1,149 @@
|
|||
<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="leaderboard") 排行榜
|
||||
//- dd
|
||||
router-link(:active-class="$style.active" to="recommend") 歌单
|
||||
dl
|
||||
dt 我的音乐
|
||||
dd
|
||||
router-link(:active-class="$style.active" to="list") {{defaultList.name}}
|
||||
router-link(:active-class="$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', '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;
|
||||
// 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}';
|
||||
}
|
||||
.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
|
@ -0,0 +1,606 @@
|
|||
<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") {{title}}
|
||||
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" @click='setProgess' ref="dom_progress")
|
||||
div(:class="$style.progress")
|
||||
//- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ width: progress + '%' }")
|
||||
div(:class="[$style.progressBar, $style.progressBar2]" :style="{ width: (progress * 100 || 0) + '%' }")
|
||||
|
||||
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 } from '../../utils'
|
||||
import { mapGetters, mapActions, mapMutations } from 'vuex'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: true,
|
||||
audio: null,
|
||||
nowPlayTime: 0,
|
||||
maxPlayTime: 0,
|
||||
isPlay: false,
|
||||
status: '^-^',
|
||||
musicInfo: {
|
||||
songmid: null,
|
||||
img: null,
|
||||
lrc: null,
|
||||
url: null,
|
||||
name: '^',
|
||||
singer: '^',
|
||||
},
|
||||
pregessWidth: 0,
|
||||
lyric: {
|
||||
lrc: null,
|
||||
text: '',
|
||||
line: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
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 50
|
||||
return this.nowPlayTime / this.maxPlayTime || 0
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setProgessWidth()
|
||||
this.init()
|
||||
},
|
||||
watch: {
|
||||
changePlay(n) {
|
||||
if (!n) return
|
||||
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) {
|
||||
this.handleRemoveMusic()
|
||||
if (n.length) this.handleNext()
|
||||
} else {
|
||||
this.fixPlayIndex(index)
|
||||
}
|
||||
// console.log(this.playIndex)
|
||||
}
|
||||
},
|
||||
progress(n, o) {
|
||||
if (n.toFixed(2) === o.toFixed(2)) return
|
||||
this.sendProgressEvent(n, 'normal')
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('player', ['getUrl', 'getPic', 'getLrc']),
|
||||
...mapMutations('player', [
|
||||
'setPlayIndex',
|
||||
'fixPlayIndex',
|
||||
'resetChangePlay',
|
||||
]),
|
||||
init() {
|
||||
this.audio = document.createElement('audio')
|
||||
this.audio.controls = false
|
||||
this.audio.autoplay = true
|
||||
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', () => {
|
||||
if (!this.musicInfo.songmid) return
|
||||
console.log('出错')
|
||||
this.stopPlay()
|
||||
this.status = '加载出错'
|
||||
this.sendProgressEvent(this.progress, 'error')
|
||||
|
||||
// let urls = this.player_info.targetSong.urls
|
||||
// if (urls && urls.some((url, index) => {
|
||||
// if (this.musicInfo.musicUrl.includes(url)) {
|
||||
// let newUrl = urls[index + 1]
|
||||
// if (!newUrl) return false
|
||||
// this.musicInfo.musicUrl = this.musicInfo.musicUrl.replace(url, newUrl)
|
||||
// // this.musicInfo.musicUrl = newUrl ? this.musicInfo.musicUrl.replace(url, newUrl) : this.setFormTag(this.musicInfo.musicUrl.replace(url, urls[0]))
|
||||
// return true
|
||||
// }
|
||||
// })) {
|
||||
// this.audio.src = this.musicInfo.musicUrl
|
||||
// // console.log(this.musicInfo.musicUrl)
|
||||
// } else {
|
||||
// this.handleNext()
|
||||
// }
|
||||
this.handleNext()
|
||||
})
|
||||
this.audio.addEventListener('loadeddata', () => {
|
||||
this.maxPlayTime = this.audio.duration
|
||||
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)
|
||||
let targetSong = this.list[this.playIndex]
|
||||
|
||||
if (this.listId == 'download') {
|
||||
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)
|
||||
}
|
||||
},
|
||||
handleNext() {
|
||||
// if (this.list.listName === null) return
|
||||
if (!this.list.length) return
|
||||
let index
|
||||
switch (this.setting.player.togglePlayMethod) {
|
||||
case 'listLoop':
|
||||
index = this.hanldeListLoop()
|
||||
break
|
||||
case 'random':
|
||||
index = this.hanldeListRandom()
|
||||
break
|
||||
case 'list':
|
||||
index = this.hanldeListNext()
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
if (index < 0) return
|
||||
this.setPlayIndex(index)
|
||||
},
|
||||
hanldeListLoop() {
|
||||
return this.playIndex === this.list.length - 1 ? 0 : this.playIndex + 1
|
||||
},
|
||||
hanldeListNext() {
|
||||
return this.playIndex === this.list.length - 1 ? -1 : this.playIndex + 1
|
||||
},
|
||||
hanldeListRandom() {
|
||||
return getRandom(0, this.list.length)
|
||||
},
|
||||
startPlay() {
|
||||
this.isPlay = true
|
||||
if (this.musicInfo.lrc) this.lyric.lrc.play(this.audio.currentTime * 1000)
|
||||
this.setAppName()
|
||||
this.sendProgressEvent(this.progress, 'normal')
|
||||
},
|
||||
stopPlay() {
|
||||
this.isPlay = false
|
||||
this.lyric.lrc.pause()
|
||||
this.sendProgressEvent(this.progress, 'paused')
|
||||
this.clearAppName()
|
||||
},
|
||||
setProgess(e) {
|
||||
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) {
|
||||
let type = songInfo._types['192k'] ? '192k' : '128k'
|
||||
if (highQuality && songInfo._types['320k']) type = '320k'
|
||||
return type
|
||||
},
|
||||
setUrl(targetSong) {
|
||||
let type = this.getPlayType(this.setting.player.highQuality, targetSong)
|
||||
this.musicInfo.url = targetSong.typeUrl[type]
|
||||
|
||||
let urlP = this.musicInfo.url
|
||||
? Promise.resolve()
|
||||
: this.getUrl({ musicInfo: targetSong, type }).then(() => {
|
||||
this.musicInfo.url = targetSong.typeUrl[type]
|
||||
})
|
||||
|
||||
urlP.then(() => {
|
||||
this.audio.src = this.musicInfo.url
|
||||
})
|
||||
},
|
||||
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.lyric
|
||||
|
||||
let lrcP = this.musicInfo.lrc
|
||||
? Promise.resolve()
|
||||
: this.getLrc(targetSong).then(() => {
|
||||
this.musicInfo.lrc = targetSong.lyric
|
||||
})
|
||||
|
||||
lrcP
|
||||
.then(() => {
|
||||
this.lyric.lrc.setLyric(this.musicInfo.lrc)
|
||||
if (this.isPlay) this.lyric.lrc.play(this.audio.currentTime * 1000)
|
||||
})
|
||||
.catch(err => {
|
||||
this.status = err.message
|
||||
})
|
||||
},
|
||||
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
|
||||
this.fixPlayIndex(this.playIndex - 1)
|
||||
},
|
||||
sendProgressEvent(status, mode) {
|
||||
// console.log(status)
|
||||
rendererSend('progress', {
|
||||
status: status < 0.01 ? 0.01 : status,
|
||||
mode: mode || 'normal',
|
||||
})
|
||||
},
|
||||
setAppName() {
|
||||
// rendererSend('appName', {
|
||||
// name: `${this.musicInfo.name} - ${this.musicInfo.singer}`,
|
||||
// })
|
||||
},
|
||||
clearAppName() {
|
||||
// rendererSend('appName')
|
||||
},
|
||||
},
|
||||
}
|
||||
</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: background-color, border-color;
|
||||
background-color: @color-theme_2;
|
||||
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;
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
transition: @transition-theme;
|
||||
transition-property: border-color;
|
||||
border: 2px solid @color-theme_2;
|
||||
}
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
||||
.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.2s 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;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.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-bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
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 {
|
||||
background-color: ~'@{color-@{value}-theme_2}';
|
||||
border-top-color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
.left {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
img {
|
||||
border-color: ~'@{color-@{value}-theme_2}';
|
||||
}
|
||||
}
|
||||
.play-btn {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
svg {
|
||||
filter: drop-shadow(0 0 1px 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>
|
|
@ -0,0 +1,128 @@
|
|||
<template lang="pug">
|
||||
div(:class="$style.toolbar")
|
||||
//- img(v-if="icon")
|
||||
//- h1 {{title}}
|
||||
material-search-input(:class="$style.input")
|
||||
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'
|
||||
export default {
|
||||
// props: {
|
||||
// color: {
|
||||
// type: String,
|
||||
// default: color,
|
||||
// },
|
||||
// icon: {
|
||||
// type: Boolean,
|
||||
// default: false,
|
||||
// },
|
||||
// },
|
||||
computed: {
|
||||
// ...mapGetters(['theme']),
|
||||
},
|
||||
methods: {
|
||||
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;
|
||||
background-color: @color-theme_2;
|
||||
align-items: center;
|
||||
padding-left: 15px;
|
||||
-webkit-app-region: drag;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
.input {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.toolbar {
|
||||
background-color: ~'@{color-@{value}-theme_2}';
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 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;
|
||||
|
||||
button {
|
||||
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: 13px;
|
||||
height: 13px;
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
&.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%);
|
||||
}
|
||||
&.max:hover:after {
|
||||
background-color: lighten(@color-maxBtn, 10%);
|
||||
}
|
||||
&.close:hover:after {
|
||||
background-color: lighten(@color-closeBtn, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,33 @@
|
|||
<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;
|
||||
background-color: @color-theme_2;
|
||||
> * {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
// background: #fff;
|
||||
// overflow: hidden;
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.view {
|
||||
background-color: ~'@{color-@{value}-theme_2}';
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
|
@ -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)
|
||||
})
|
|
@ -0,0 +1,59 @@
|
|||
<template lang="pug">
|
||||
button(:class="[$style.btn, min ? $style.min : '']" @click="$emit('click', $event)")
|
||||
slot
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
min: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
}
|
||||
</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;
|
||||
|
||||
&: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,160 @@
|
|||
<template lang="pug">
|
||||
div(:class="$style.checkbox")
|
||||
input(:type="need ? 'radio' : 'checkbox'" :id="id" :value="target" :name="name" @change="change" v-model="val")
|
||||
label(:for="id" :class="$style.content")
|
||||
div
|
||||
svg(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')
|
||||
span
|
||||
slot
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
target: {},
|
||||
value: {},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
need: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
val: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(n) {
|
||||
if (this.target && n !== this.target) {
|
||||
this.val = this.need
|
||||
? n === this.target
|
||||
? this.target
|
||||
: false
|
||||
: n === this.target
|
||||
} else if (n !== this.val) {
|
||||
this.val = n
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.target) {
|
||||
this.val = this.need
|
||||
? this.value === this.target
|
||||
? this.target
|
||||
: false
|
||||
: this.value === this.target
|
||||
} else {
|
||||
this.val = this.value
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
change() {
|
||||
let val = this.target == null ? this.val : this.val ? this.target : null
|
||||
this.$emit('input', val)
|
||||
this.$emit('change', val)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
|
||||
.checkbox {
|
||||
display: inline-block;
|
||||
// font-size: 56px;
|
||||
|
||||
> input {
|
||||
display: none;
|
||||
&:checked {
|
||||
+ .content {
|
||||
> div {
|
||||
&:after {
|
||||
border-color: @color-theme;
|
||||
}
|
||||
svg {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
> div {
|
||||
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 #ccc;
|
||||
transition: border-color 0.2s ease;
|
||||
border-radius: 15%;
|
||||
}
|
||||
svg {
|
||||
transition: 0.2s ease;
|
||||
transition-property: transform, opacity;
|
||||
transform: scale(0.5);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> span {
|
||||
flex: auto;
|
||||
line-height: 1;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.checkbox {
|
||||
> input {
|
||||
&:checked {
|
||||
+ .content {
|
||||
> div {
|
||||
&:after {
|
||||
border-color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
> div {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
// border: 1px solid #ccc;
|
||||
&:after {
|
||||
border-color: #ccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</style>
|
|
@ -0,0 +1,94 @@
|
|||
<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" :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 '普通音质'
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</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,106 @@
|
|||
<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="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="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,
|
||||
},
|
||||
removeBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
downloadBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
playBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
searchBtn: {
|
||||
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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
.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}';
|
||||
}
|
||||
}
|
||||
|
||||
.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,232 @@
|
|||
<template lang="pug">
|
||||
div(:class="[$style.search, focus ? $style.active : '']")
|
||||
div(:class="$style.form")
|
||||
input(placeholder="Search for something..." v-model.trim="text"
|
||||
@focus="handleFocus" @blur="handleBlur" @input="handleInput"
|
||||
@keyup.enter="handleSearch")
|
||||
button(type="button" @click="handleSearch")
|
||||
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(: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>
|
||||
import { mapGetters } from 'vuex'
|
||||
import music from '../../utils/music'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isShow: false,
|
||||
text: '',
|
||||
list: [],
|
||||
index: null,
|
||||
focus: false,
|
||||
listStyle: {
|
||||
height: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['source']),
|
||||
...mapGetters('search', ['info']),
|
||||
},
|
||||
watch: {
|
||||
list(n) {
|
||||
this.$nextTick(() => {
|
||||
this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
|
||||
})
|
||||
},
|
||||
'info.text'(n) {
|
||||
if (n !== this.text) this.text = n
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleTemplistClick(index) {
|
||||
this.text = this.list[index]
|
||||
this.handleSearch()
|
||||
},
|
||||
handleFocus() {
|
||||
this.focus = true
|
||||
if (this.text) this.handleInput()
|
||||
this.showList()
|
||||
},
|
||||
handleBlur() {
|
||||
setTimeout(() => {
|
||||
this.focus = false
|
||||
this.hideList()
|
||||
}, 200)
|
||||
},
|
||||
handleSearch() {
|
||||
this.hideList()
|
||||
this.$router.push({
|
||||
path: 'search',
|
||||
query: {
|
||||
text: this.text,
|
||||
},
|
||||
})
|
||||
},
|
||||
handleInput() {
|
||||
if (this.text === '') {
|
||||
this.list.splice(0, this.list.length)
|
||||
music[this.source.id].tempSearch.cancelTempSearch()
|
||||
return
|
||||
}
|
||||
if (!this.isShow) this.showList()
|
||||
music[this.source.id].tempSearch.search(this.text).then(list => {
|
||||
this.list = list
|
||||
}).catch(() => {})
|
||||
},
|
||||
showList() {
|
||||
this.isShow = true
|
||||
this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
|
||||
},
|
||||
hideList() {
|
||||
this.isShow = false
|
||||
this.listStyle.height = 0
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
|
||||
.search {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: 13px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
|
@ -0,0 +1,170 @@
|
|||
<template lang="pug">
|
||||
div(:class="$style.select")
|
||||
div(:class="$style.label" ref="dom_btn" @click="handleShow") {{value ? itemName ? list.find(l => l.id == value).name : value : ''}}
|
||||
ul(:class="$style.list" ref="dom_list" :style="listStyle")
|
||||
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,
|
||||
listStyle: {
|
||||
height: 0,
|
||||
opacity: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(n) {
|
||||
this.$nextTick(() => {
|
||||
if (n) {
|
||||
this.listStyle.height = this.$refs.dom_list.scrollHeight + 'px'
|
||||
this.listStyle.opacity = 1
|
||||
} else {
|
||||
this.listStyle.height = 0
|
||||
this.listStyle.opacity = 0
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
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;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
||||
&: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: 3px;
|
||||
background-color: @color-theme_2;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transition: .25s ease;
|
||||
transition-property: height, 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}';
|
||||
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,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: 3px;
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
border-right: 2px solid @color-tab-border-top;
|
||||
border-top-right-radius: 3px;
|
||||
button {
|
||||
border-top-right-radius: 3px;
|
||||
// border-bottom-right-radius: 3px;
|
||||
}
|
||||
}
|
||||
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-theme_2-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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// .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}-theme_2-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}';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
|
@ -0,0 +1,156 @@
|
|||
<template lang="pug">
|
||||
material-modal(:show="version.showModal" @close="handleClose")
|
||||
main(:class="$style.main" v-if="version.newVersion")
|
||||
h2 🚀程序更新🚀
|
||||
|
||||
div.scroll(:class="$style.info")
|
||||
div(:class="$style.current")
|
||||
h3 最新版本:{{version.newVersion.version}}
|
||||
h3 当前版本:{{version.version}}
|
||||
h3 版本变化:
|
||||
p(v-html="version.newVersion.desc")
|
||||
div(:class="$style.history" 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")
|
||||
div(:class="$style.desc")
|
||||
p 新版本已下载完毕,
|
||||
p
|
||||
| 你可以选择
|
||||
strong 立即重启更新
|
||||
| 或稍后
|
||||
strong 关闭程序时
|
||||
| 自动更新~
|
||||
material-btn(:class="$style.btn" @click.onec="handleClick") 立即重启更新
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import { rendererSend } from '../../../common/icp'
|
||||
import { checkVersion } from '../../utils'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(['version']),
|
||||
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
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setVersionVisible']),
|
||||
handleClose() {
|
||||
this.setVersionVisible(false)
|
||||
},
|
||||
handleClick(event) {
|
||||
this.handleClose()
|
||||
event.target.disabled = true
|
||||
rendererSend('quit-update')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
|
||||
.main {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
max-width: 500px;
|
||||
min-width: 300px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
// 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%;
|
||||
}
|
||||
.current {
|
||||
> p {
|
||||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
.history {
|
||||
h3 {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.item {
|
||||
+ .item {
|
||||
padding-top: 15px;
|
||||
}
|
||||
h4 {
|
||||
font-weight: 700;
|
||||
}
|
||||
> p {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.footer {
|
||||
flex: 0 0 none;
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
padding: 10px 0;
|
||||
color: @color-theme;
|
||||
line-height: 1.2;
|
||||
strong {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.main {
|
||||
h2 {
|
||||
color: ~'@{color-@{value}-theme_2-font}';
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
.desc {
|
||||
color: ~'@{color-@{value}-theme}';
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
|
@ -0,0 +1,11 @@
|
|||
module.exports = {
|
||||
development: {
|
||||
|
||||
},
|
||||
production: {
|
||||
|
||||
},
|
||||
ajax: {
|
||||
timeout: 15000, // ajax请求超时时间
|
||||
},
|
||||
}
|
|
@ -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),
|
||||
})
|
|
@ -0,0 +1 @@
|
|||
// import './axios'
|
|
@ -0,0 +1,56 @@
|
|||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
|
||||
import paths from './paths'
|
||||
|
||||
|
||||
function route(path, view, name, meta) {
|
||||
return {
|
||||
name: name || view,
|
||||
path,
|
||||
meta,
|
||||
component: (resovle) => import(
|
||||
`../views/${view}.vue`
|
||||
).then(resovle),
|
||||
}
|
||||
}
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
const router = new Router({
|
||||
mode: 'hash',
|
||||
routes: paths.map(path => route(path.path, path.view, path.name, path.meta)).concat([
|
||||
{ path: '*', redirect: '/search' },
|
||||
]),
|
||||
linkActiveClass: 'active-link',
|
||||
linkExactActiveClass: 'exact-active-link',
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
if (savedPosition) {
|
||||
resolve(savedPosition)
|
||||
} else {
|
||||
const position = {}
|
||||
// new navigation.
|
||||
// scroll to anchor by returning the selector
|
||||
if (to.hash) {
|
||||
position.selector = to.hash
|
||||
}
|
||||
// check if any matched route config has meta that requires scrolling to top
|
||||
if (to.matched.some(m => m.meta.scrollToTop)) {
|
||||
// cords will be used if no selector is provided,
|
||||
// or if the selector didn't match any element.
|
||||
position.x = 0
|
||||
position.y = 0
|
||||
}
|
||||
// if the returned position is falsy or an empty object,
|
||||
// will retain current scroll position.
|
||||
resolve(position)
|
||||
}
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
export default router
|
|
@ -0,0 +1,40 @@
|
|||
export default [
|
||||
// {
|
||||
// path: '/',
|
||||
// // redirect: '/app',
|
||||
// // props: true,
|
||||
// component: () => import('../views/Dashboard.vue'),
|
||||
// name: 'Dashboard',
|
||||
// alias: '/dashboard'
|
||||
// }
|
||||
{
|
||||
path: '/search',
|
||||
name: 'search',
|
||||
view: 'Search',
|
||||
},
|
||||
{
|
||||
path: '/leaderboard',
|
||||
name: 'leaderboard',
|
||||
view: 'Leaderboard',
|
||||
},
|
||||
{
|
||||
path: '/recommend',
|
||||
name: 'recommend',
|
||||
view: 'Recommend',
|
||||
},
|
||||
{
|
||||
path: '/list',
|
||||
name: 'list',
|
||||
view: 'List',
|
||||
},
|
||||
{
|
||||
path: '/download',
|
||||
name: 'download',
|
||||
view: 'Download',
|
||||
},
|
||||
{
|
||||
path: '/setting',
|
||||
name: 'setting',
|
||||
view: 'Setting',
|
||||
},
|
||||
]
|
|
@ -0,0 +1,14 @@
|
|||
// import api from 'api/connom'
|
||||
import { httpGet } from '../utils/request'
|
||||
import { author, name } from '../../../package.json'
|
||||
|
||||
export default {
|
||||
getVersionInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
httpGet(`https://raw.githubusercontent.com/${author}/${name}/master/publish/version.json`, (err, resp, body) => {
|
||||
if (err) return reject(err)
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import music from '../utils/music'
|
||||
|
||||
export default {
|
||||
theme(state) {
|
||||
return (state.themes[state.setting.themeId] && state.themes[state.setting.themeId].class) || ''
|
||||
},
|
||||
themes(state) {
|
||||
return {
|
||||
active: state.setting.themeId,
|
||||
list: state.themes,
|
||||
}
|
||||
},
|
||||
source(state) {
|
||||
return music.sources.find(s => s.id === state.setting.sourceId) || music.sources[0]
|
||||
},
|
||||
sources(state) {
|
||||
return {
|
||||
active: state.setting.sourceId,
|
||||
list: music.sources,
|
||||
}
|
||||
},
|
||||
userInfo(state) {
|
||||
return state.userInfo
|
||||
},
|
||||
setting(state) {
|
||||
return state.setting
|
||||
},
|
||||
electronStore(state) {
|
||||
return state.electronStore
|
||||
},
|
||||
version(state) {
|
||||
return state.version
|
||||
},
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
// import { createPersistedState, createSharedMutations } from 'vuex-electron'
|
||||
|
||||
import defaultState from './state'
|
||||
import mutations from './mutations'
|
||||
import modules from './modules'
|
||||
import getters from './getters'
|
||||
import actions from './actions'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
const store = new Vuex.Store({
|
||||
strict: isDev,
|
||||
state: defaultState,
|
||||
modules,
|
||||
mutations,
|
||||
getters,
|
||||
actions,
|
||||
// plugins: [createPersistedState(), createSharedMutations()],
|
||||
})
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept([
|
||||
'./state',
|
||||
'./mutations',
|
||||
'./actions',
|
||||
'./getters',
|
||||
], () => {
|
||||
const newState = require('./state').default
|
||||
const newMutations = require('./mutations').default
|
||||
const newActions = require('./actions').default
|
||||
const newGetters = require('./getters').default
|
||||
|
||||
store.hotUpdate({
|
||||
state: newState,
|
||||
mutations: newMutations,
|
||||
getters: newGetters,
|
||||
actions: newActions,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default store
|
|
@ -0,0 +1,274 @@
|
|||
import download from '../../utils/download'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import music from '../../utils/music'
|
||||
|
||||
// state
|
||||
const state = {
|
||||
list: [],
|
||||
}
|
||||
|
||||
const dls = {}
|
||||
|
||||
// getters
|
||||
const getters = {
|
||||
list: state => state.list || [],
|
||||
dls: () => dls || {},
|
||||
}
|
||||
|
||||
const checkPath = path => {
|
||||
try {
|
||||
if (!fs.existsSync(path)) fs.mkdirSync(path, { recursive: true })
|
||||
} catch (error) {
|
||||
return error.message
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const getExt = type => {
|
||||
switch (type) {
|
||||
case '128k':
|
||||
case '192k':
|
||||
case '320k':
|
||||
return 'mp3'
|
||||
case 'ape':
|
||||
return 'ape'
|
||||
case 'flac':
|
||||
return 'flac'
|
||||
}
|
||||
}
|
||||
|
||||
const checkList = (list, musicInfo, type) => list.some(s => s.musicInfo.songmid === musicInfo.songmid && s.type === type)
|
||||
|
||||
const refreshUrl = downloadInfo => {
|
||||
return music[downloadInfo.musicInfo.source].getMusicUrl(downloadInfo.musicInfo, downloadInfo.type)
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
createDownload({ state, rootState }, { musicInfo, type }) {
|
||||
if (checkList(state.list, musicInfo, type)) return
|
||||
let ext = getExt(type)
|
||||
const downloadInfo = {
|
||||
isComplate: false,
|
||||
isDownloading: false,
|
||||
statusText: '任务初始化中',
|
||||
url: null,
|
||||
fileName: `${rootState.setting.download.fileName
|
||||
.replace('歌名', musicInfo.name)
|
||||
.replace('歌手', musicInfo.singer)}.${ext}`,
|
||||
progress: {
|
||||
downloaded: 0,
|
||||
total: 0,
|
||||
progress: 0,
|
||||
},
|
||||
type,
|
||||
ext,
|
||||
musicInfo,
|
||||
key: `${musicInfo.songmid}${ext}`,
|
||||
}
|
||||
downloadInfo.filePath = path.join(rootState.setting.download.savePath, downloadInfo.fileName)
|
||||
if (dls[downloadInfo.key]) {
|
||||
dls[downloadInfo.key].stop().finally(() => {
|
||||
this.dispatch('download/addTask', downloadInfo)
|
||||
})
|
||||
} else {
|
||||
// console.log(downloadInfo)
|
||||
this.dispatch('download/addTask', downloadInfo)
|
||||
}
|
||||
},
|
||||
addTask({ commit, rootState }, downloadInfo) {
|
||||
commit('addTask', downloadInfo)
|
||||
let msg = checkPath(rootState.setting.download.savePath)
|
||||
if (msg) return commit('setStatusText', '检查下载目录出错: ' + msg)
|
||||
const options = {
|
||||
url: downloadInfo.url,
|
||||
path: rootState.setting.download.savePath,
|
||||
fileName: downloadInfo.fileName,
|
||||
method: 'get',
|
||||
override: true,
|
||||
onEnd() {
|
||||
commit('onEnd', downloadInfo)
|
||||
console.log('on complate')
|
||||
},
|
||||
onError(err) {
|
||||
console.log(err)
|
||||
if (err.message.includes('Response status was')) {
|
||||
const code = err.message.replace(/Response status was (\d+)$/, '$1')
|
||||
switch (code) {
|
||||
case '401':
|
||||
case '403':
|
||||
case '410':
|
||||
commit('setStatusText', { downloadInfo, text: '链接失效,正在刷新链接' })
|
||||
refreshUrl(downloadInfo).then(result => {
|
||||
commit('updateUrl', { downloadInfo, url: result.url })
|
||||
commit('setStatusText', { downloadInfo, text: '链接刷新成功' })
|
||||
dls[downloadInfo.key].url = dls[downloadInfo.key].requestURL = result.url
|
||||
dls[downloadInfo.key].__initProtocol(result.url)
|
||||
dls[downloadInfo.key].resume()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
return
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
commit('onError', downloadInfo)
|
||||
},
|
||||
onStateChanged(state) {
|
||||
console.log(state)
|
||||
},
|
||||
onDownload() {
|
||||
commit('onDownload', downloadInfo)
|
||||
console.log('on download')
|
||||
},
|
||||
onProgress(status) {
|
||||
commit('onProgress', { downloadInfo, status })
|
||||
console.log(status)
|
||||
},
|
||||
onPause() {
|
||||
commit('pauseTask', downloadInfo)
|
||||
},
|
||||
onResume() {
|
||||
commit('resumeTask', downloadInfo)
|
||||
},
|
||||
}
|
||||
let p = options.url ? Promise.resolve() : refreshUrl(downloadInfo).then(result => {
|
||||
commit('updateUrl', { downloadInfo, url: result.url })
|
||||
options.url = result.url
|
||||
})
|
||||
p.then(() => {
|
||||
dls[downloadInfo.key] = download(options)
|
||||
})
|
||||
},
|
||||
removeTask({ commit, state }, index) {
|
||||
let info = state.list[index]
|
||||
if (state.list[index].isDownloading) {
|
||||
dls[info.key].stop().finally(() => {
|
||||
delete dls[info.key]
|
||||
})
|
||||
}
|
||||
commit('removeTask', index)
|
||||
if (dls[info.key]) delete dls[info.key]
|
||||
},
|
||||
resumeTask({ commit, rootState }, downloadInfo) {
|
||||
let msg = checkPath(rootState.setting.download.savePath)
|
||||
if (msg) return commit('setStatusText', '检查下载目录出错: ' + msg)
|
||||
const options = {
|
||||
url: downloadInfo.url,
|
||||
path: rootState.setting.download.savePath,
|
||||
fileName: downloadInfo.fileName,
|
||||
method: 'get',
|
||||
override: true,
|
||||
onEnd() {
|
||||
commit('onEnd', downloadInfo)
|
||||
console.log('on complate')
|
||||
},
|
||||
onError(err) {
|
||||
commit('onError', downloadInfo)
|
||||
commit('setStatusText', { downloadInfo, text: '链接失效,正在刷新链接' })
|
||||
refreshUrl(downloadInfo).then(result => {
|
||||
commit('updateUrl', { downloadInfo, url: result.url })
|
||||
commit('setStatusText', { downloadInfo, text: '链接刷新成功' })
|
||||
dls[downloadInfo.key].url = dls[downloadInfo.key].requestURL = result.url
|
||||
dls[downloadInfo.key].__initProtocol(result.url)
|
||||
dls[downloadInfo.key].resume()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
console.log(err)
|
||||
},
|
||||
onStateChanged(state) {
|
||||
console.log(state)
|
||||
},
|
||||
onDownload() {
|
||||
commit('onDownload', downloadInfo)
|
||||
console.log('on download')
|
||||
},
|
||||
onProgress(status) {
|
||||
commit('onProgress', { downloadInfo, status })
|
||||
console.log(status)
|
||||
},
|
||||
onPause() {
|
||||
commit('pauseTask', downloadInfo)
|
||||
},
|
||||
onResume() {
|
||||
commit('resumeTask', downloadInfo)
|
||||
},
|
||||
}
|
||||
|
||||
let p = options.url ? Promise.resolve() : refreshUrl(downloadInfo).then(result => {
|
||||
commit('updateUrl', { downloadInfo, url: result.url })
|
||||
options.url = result.url
|
||||
})
|
||||
|
||||
if (fs.existsSync(downloadInfo.filePath)) {
|
||||
options.resumeInfo = {
|
||||
totalFileSize: downloadInfo.progress.total,
|
||||
}
|
||||
}
|
||||
p.then(() => {
|
||||
dls[downloadInfo.key] = download(options)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// mitations
|
||||
const mutations = {
|
||||
addTask(state, downloadInfo) {
|
||||
state.list.push(downloadInfo)
|
||||
},
|
||||
removeTask(state, index) {
|
||||
state.list.splice(index, 1)
|
||||
},
|
||||
pauseTask(state, downloadInfo) {
|
||||
downloadInfo.isDownloading = false
|
||||
},
|
||||
resumeTask(state, downloadInfo) {
|
||||
downloadInfo.statusText = '恢复下载'
|
||||
},
|
||||
setStatusText(state, { downloadInfo, index, text }) {
|
||||
if (downloadInfo) {
|
||||
downloadInfo.statusText = text
|
||||
} else {
|
||||
state.list[index].statusText = text
|
||||
}
|
||||
},
|
||||
onEnd(state, downloadInfo) {
|
||||
downloadInfo.isComplate = true
|
||||
downloadInfo.isDownloading = false
|
||||
downloadInfo.statusText = '下载完成'
|
||||
},
|
||||
onError(state, downloadInfo) {
|
||||
downloadInfo.isDownloading = false
|
||||
downloadInfo.statusText = '任务出错'
|
||||
},
|
||||
onDownload(state, downloadInfo) {
|
||||
downloadInfo.isDownloading = true
|
||||
downloadInfo.statusText = '正在下载'
|
||||
},
|
||||
onProgress(state, { downloadInfo, status }) {
|
||||
downloadInfo.progress.progress = status.progress
|
||||
downloadInfo.progress.downloaded = status.downloaded
|
||||
downloadInfo.progress.total = status.total
|
||||
},
|
||||
setTotal(state, { order, downloadInfo }) {
|
||||
downloadInfo.order = order
|
||||
},
|
||||
updateDownloadList(state, list) {
|
||||
state.list = list
|
||||
},
|
||||
updateUrl(state, { downloadInfo, url }) {
|
||||
downloadInfo.url = url
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// https://vuex.vuejs.org/en/modules.html
|
||||
|
||||
const requireModule = require.context('./', true, /\.js$/)
|
||||
const modules = {}
|
||||
|
||||
requireModule.keys().forEach(fileName => {
|
||||
if (fileName === './index.js') return
|
||||
const path = fileName.replace(/(\.\/|\.js)/g, '')
|
||||
|
||||
if (/\//.test(path)) {
|
||||
// Replace ./ and .js
|
||||
const [moduleName, imported] = path.split('/')
|
||||
|
||||
if (!modules[moduleName]) {
|
||||
modules[moduleName] = {
|
||||
namespaced: true,
|
||||
}
|
||||
}
|
||||
|
||||
modules[moduleName][imported] = requireModule(fileName).default
|
||||
} else {
|
||||
modules[path] = requireModule(fileName).default
|
||||
}
|
||||
})
|
||||
|
||||
export default modules
|
|
@ -0,0 +1,63 @@
|
|||
import music from '../../utils/music'
|
||||
const sourceList = {}
|
||||
const sources = []
|
||||
for (const source of music.sources) {
|
||||
const leaderboard = music[source.id].leaderboard
|
||||
if (!leaderboard) continue
|
||||
sourceList[source.id] = leaderboard.list
|
||||
sources.push(source)
|
||||
}
|
||||
|
||||
// state
|
||||
const state = {
|
||||
list: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
limit: 30,
|
||||
key: null,
|
||||
}
|
||||
|
||||
// getters
|
||||
const getters = {
|
||||
sourceInfo: () => ({ sources, sourceList }),
|
||||
list(state) {
|
||||
return state.list
|
||||
},
|
||||
info(state) {
|
||||
return {
|
||||
total: state.total,
|
||||
limit: state.limit,
|
||||
page: state.page,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
getList({ state, rootState, commit }, page) {
|
||||
let source = rootState.setting.leaderboard.source
|
||||
let tabId = rootState.setting.leaderboard.tabId
|
||||
let key = `${source}${tabId}${page}}`
|
||||
if (state.list.length && state.key == key) return true
|
||||
return music[source].leaderboard.getList(tabId, page).then(result => commit('setList', { result, key }))
|
||||
},
|
||||
}
|
||||
|
||||
// mitations
|
||||
const mutations = {
|
||||
setList(state, { result, key }) {
|
||||
state.list = result.list
|
||||
state.total = result.total
|
||||
state.limit = result.limit
|
||||
state.page = result.page
|
||||
state.key = key
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// state
|
||||
const state = {
|
||||
defaultList: {
|
||||
name: '试听列表',
|
||||
list: [],
|
||||
},
|
||||
userList: [],
|
||||
}
|
||||
|
||||
// getters
|
||||
const getters = {
|
||||
defaultList: state => state.defaultList || {},
|
||||
userList: state => state.userList,
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
|
||||
}
|
||||
|
||||
// mitations
|
||||
const mutations = {
|
||||
initDefaultList(state, data) {
|
||||
state.defaultList = data
|
||||
},
|
||||
setDefaultList(state, list) {
|
||||
state.defaultList.list = list
|
||||
},
|
||||
defaultListAdd(state, musicInfo) {
|
||||
if (state.defaultList.list.some(s => s.songmid === musicInfo.songmid)) return
|
||||
state.defaultList.list.push(musicInfo)
|
||||
},
|
||||
defaultListRemove(state, index) {
|
||||
state.defaultList.list.splice(index, 1)
|
||||
},
|
||||
defaultListClear(state) {
|
||||
state.defaultList.list.length = 0
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import music from '../../utils/music'
|
||||
|
||||
// state
|
||||
const state = {
|
||||
list: [],
|
||||
listId: null,
|
||||
playIndex: -1,
|
||||
changePlay: false,
|
||||
}
|
||||
|
||||
// getters
|
||||
const getters = {
|
||||
list: state => state.list || [],
|
||||
listId: state => state.listId,
|
||||
changePlay: satte => satte.changePlay,
|
||||
playIndex: state => state.playIndex,
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
getUrl({ commit, state }, { musicInfo, type }) {
|
||||
return music[musicInfo.source].getMusicUrl(musicInfo, type).then(result => commit('setUrl', { musicInfo, url: result.url, type }))
|
||||
},
|
||||
getPic({ commit, state }, musicInfo) {
|
||||
return music[musicInfo.source].getPic(musicInfo).then(url => commit('getPic', { musicInfo, url }))
|
||||
},
|
||||
getLrc({ commit, state }, musicInfo) {
|
||||
return music[musicInfo.source].getLyric(musicInfo).then(lrc => commit('setLrc', { musicInfo, lrc }))
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// mitations
|
||||
const mutations = {
|
||||
setUrl(state, datas) {
|
||||
datas.musicInfo.typeUrl[datas.type] = datas.url
|
||||
},
|
||||
getPic(state, datas) {
|
||||
datas.musicInfo.img = datas.url
|
||||
},
|
||||
setLrc(state, datas) {
|
||||
datas.musicInfo.lyric = datas.lrc
|
||||
},
|
||||
setList(state, { list, listId, index }) {
|
||||
state.list = list
|
||||
state.listId = listId
|
||||
state.playIndex = index
|
||||
state.changePlay = true
|
||||
},
|
||||
setPlayIndex(state, index) {
|
||||
state.playIndex = index
|
||||
state.changePlay = true
|
||||
},
|
||||
fixPlayIndex(state, index) {
|
||||
state.playIndex = index
|
||||
},
|
||||
resetChangePlay(state) {
|
||||
state.changePlay = false
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import music from '../../utils/music'
|
||||
|
||||
// state
|
||||
const state = {
|
||||
list: [],
|
||||
text: '',
|
||||
page: 1,
|
||||
limit: 30,
|
||||
allPage: 1,
|
||||
total: 0,
|
||||
}
|
||||
|
||||
// getters
|
||||
const getters = {
|
||||
list: state => state.list || [],
|
||||
limit: state => state.limit,
|
||||
info: state => ({ page: state.page, text: state.text }),
|
||||
listInfo: state => ({ allPage: state.allPage, total: state.total }),
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
search({ commit, rootState }, { text, page, limit }) {
|
||||
return music[rootState.setting.sourceId].musicSearch.search(text, page, limit)
|
||||
.then(data => commit('setList', { list: data.list, allPage: data.allPage, total: data.total, text, page }))
|
||||
},
|
||||
}
|
||||
|
||||
// mitations
|
||||
const mutations = {
|
||||
setList(state, datas) {
|
||||
state.list = datas.list
|
||||
state.total = datas.total
|
||||
state.allPage = datas.allPage
|
||||
state.page = datas.page
|
||||
state.text = datas.text
|
||||
},
|
||||
clearList(state) {
|
||||
state.list.length = 0
|
||||
state.page = 0
|
||||
state.allPage = 0
|
||||
state.total = 0
|
||||
state.text = ''
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
export default {
|
||||
setTheme(state, val) {
|
||||
state.setting.themeId = val
|
||||
},
|
||||
setSource(state, val) {
|
||||
state.setting.sourceId = val
|
||||
},
|
||||
setSetting(state, val) {
|
||||
state.setting = val
|
||||
},
|
||||
setLeaderboard(state, { tabId, source }) {
|
||||
if (tabId != null) state.setting.leaderboard.tabId = tabId
|
||||
if (source != null) state.setting.leaderboard.source = source
|
||||
},
|
||||
setNewVersion(state, val) {
|
||||
val.history.forEach(ver => {
|
||||
ver.desc = ver.desc.replace(/\n/g, '<br>')
|
||||
})
|
||||
val.desc = val.desc.replace(/\n/g, '<br>')
|
||||
state.version.newVersion = val
|
||||
},
|
||||
setVersionVisible(state, val) {
|
||||
state.version.showModal = val
|
||||
},
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
// const isDev = process.env.NODE_ENV === 'development'
|
||||
import Store from 'electron-store'
|
||||
import { updateSetting } from '../utils'
|
||||
import { version } from '../../../package.json'
|
||||
let electronStore = new Store()
|
||||
const setting = updateSetting(electronStore.get('setting'))
|
||||
electronStore.set('setting', setting)
|
||||
|
||||
export default {
|
||||
themes: [
|
||||
{
|
||||
id: 0,
|
||||
name: '绿意盎然',
|
||||
class: 'green',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: '蓝田生玉',
|
||||
class: 'blue',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '信口雌黄',
|
||||
class: 'yellow',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '橙黄橘绿',
|
||||
class: 'orange',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '热情似火',
|
||||
class: 'red',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '重斤球紫',
|
||||
class: 'purple',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '灰常美丽',
|
||||
class: 'grey',
|
||||
},
|
||||
],
|
||||
version: {
|
||||
version,
|
||||
newVersion: null,
|
||||
showModal: false,
|
||||
},
|
||||
userInfo: null,
|
||||
setting,
|
||||
electronStore,
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
import { DownloaderHelper } from 'node-downloader-helper'
|
||||
// import { pauseResumeTimer } from './util'
|
||||
import { sizeFormate } from '../index'
|
||||
import { debugDownload } from '../env'
|
||||
|
||||
// these are the default options
|
||||
// const options = {
|
||||
// method: 'GET', // Request Method Verb
|
||||
// // Custom HTTP Header ex: Authorization, User-Agent
|
||||
// headers: {},
|
||||
// fileName: '', // Custom filename when saved
|
||||
// override: false, // if true it will override the file, otherwise will append '(number)' to the end of file
|
||||
// forceResume: false, // If the server does not return the "accept-ranges" header, can be force if it does support it
|
||||
// // httpRequestOptions: {}, // Override the http request options
|
||||
// // httpsRequestOptions: {}, // Override the https request options, ex: to add SSL Certs
|
||||
// }
|
||||
|
||||
export default ({
|
||||
url,
|
||||
path,
|
||||
fileName,
|
||||
method = 'get',
|
||||
headers,
|
||||
override,
|
||||
forceResume,
|
||||
// resumeTime = 5000,
|
||||
onEnd = () => {},
|
||||
onError = () => {},
|
||||
onStateChanged = () => {},
|
||||
onDownload = () => {},
|
||||
onPause = () => {},
|
||||
onResume = () => {},
|
||||
onProgress = () => {},
|
||||
resumeInfo,
|
||||
} = {}) => {
|
||||
const dl = new DownloaderHelper(url, path, {
|
||||
fileName,
|
||||
method,
|
||||
headers,
|
||||
override,
|
||||
forceResume,
|
||||
})
|
||||
|
||||
dl.on('end', () => {
|
||||
onEnd()
|
||||
debugDownload && console.log('Download Completed')
|
||||
}).on('error', err => {
|
||||
onError(err)
|
||||
dl.resume()
|
||||
console.log('Download failed, Attempting Retry')
|
||||
debugDownload && console.error('Something happend', err)
|
||||
}).on('stateChanged', state => {
|
||||
onStateChanged(state)
|
||||
debugDownload && console.log('State: ', state)
|
||||
}).on('download', () => {
|
||||
onDownload()
|
||||
// pauseResumeTimer(dl, resumeTime)
|
||||
}).on('progress', stats => {
|
||||
const progress = stats.progress.toFixed(2)
|
||||
const speed = sizeFormate(stats.speed)
|
||||
onProgress({
|
||||
progress,
|
||||
speed,
|
||||
downloaded: stats.downloaded,
|
||||
total: stats.total,
|
||||
})
|
||||
if (debugDownload) {
|
||||
const downloaded = sizeFormate(stats.downloaded)
|
||||
const total = sizeFormate(stats.total)
|
||||
console.log(`${speed}/s - ${progress}% [${downloaded}/${total}]`)
|
||||
}
|
||||
}).on('pause', () => {
|
||||
onPause()
|
||||
debugDownload && console.log('paused')
|
||||
}).on('resume', () => {
|
||||
onResume()
|
||||
debugDownload && console.log('resume')
|
||||
})
|
||||
|
||||
debugDownload && console.log('Downloading: ', url)
|
||||
|
||||
if (resumeInfo) {
|
||||
dl.__total = resumeInfo.totalFileSize // <--- Workaround
|
||||
// dl.__filePath = resumeInfo.filePath // <--- Workaround
|
||||
dl.__isResumable = true // <--- Workaround
|
||||
dl.resume()
|
||||
} else {
|
||||
dl.start()
|
||||
}
|
||||
|
||||
return dl
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { DH_STATES } from 'node-downloader-helper'
|
||||
|
||||
|
||||
export const pauseResumeTimer = (_dl, wait) => {
|
||||
setTimeout(() => {
|
||||
if (_dl.state === DH_STATES.FINISHED || _dl.state === DH_STATES.FAILED) {
|
||||
return
|
||||
}
|
||||
|
||||
_dl
|
||||
.pause()
|
||||
.then(() => console.log(`Paused for ${wait / 1000} seconds`))
|
||||
.then(() =>
|
||||
setTimeout(() => {
|
||||
if (!_dl.isResumable()) {
|
||||
console.warn(
|
||||
"This URL doesn't support resume, it will start from the beginning"
|
||||
)
|
||||
}
|
||||
return _dl.resume()
|
||||
}, wait)
|
||||
)
|
||||
}, wait)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
export const debug = isDev && true
|
||||
export const debugRequest = isDev && false
|
||||
export const debugDownload = isDev && false
|
|
@ -0,0 +1,196 @@
|
|||
import fs from 'fs'
|
||||
import { shell, remote } from 'electron'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
|
||||
/**
|
||||
* 获取两个数之间的随机整数,大于等于min,小于max
|
||||
* @param {*} min
|
||||
* @param {*} max
|
||||
*/
|
||||
export const getRandom = (min, max) => Math.floor(Math.random() * (max - min)) + min
|
||||
|
||||
|
||||
export const 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]}`
|
||||
}
|
||||
|
||||
export const formatPlayTime = time => {
|
||||
let m = parseInt(time / 60)
|
||||
let s = parseInt(time % 60)
|
||||
return m === 0 && s === 0 ? '--/--' : (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
|
||||
}
|
||||
|
||||
export const formatPlayTime2 = time => {
|
||||
let m = parseInt(time / 60)
|
||||
let s = parseInt(time % 60)
|
||||
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
|
||||
}
|
||||
|
||||
export const b64DecodeUnicode = str => {
|
||||
// Going backwards: from bytestream, to percent-encoding, to original string.
|
||||
return decodeURIComponent(window.atob(str).split('').map(function(c) {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
|
||||
}).join(''))
|
||||
}
|
||||
|
||||
export const decodeName = str => str.replace(/'/g, '\'')
|
||||
|
||||
export const scrollTo = (element, to, duration = 300, fn = function() {}) => {
|
||||
const start = element.scrollTop || element.scrollY
|
||||
const change = to - start
|
||||
const increment = 10
|
||||
if (!change) {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
let currentTime = 0; let val
|
||||
const easeInOutQuad = (t, b, c, d) => {
|
||||
t /= d / 2
|
||||
if (t < 1) return (c / 2) * t * t + b
|
||||
t--
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b
|
||||
}
|
||||
const animateScroll = () => {
|
||||
currentTime += increment
|
||||
val = parseInt(easeInOutQuad(currentTime, start, change, duration))
|
||||
if (element.scrollTo) {
|
||||
element.scrollTo(0, val)
|
||||
} else {
|
||||
element.scrollTop = val
|
||||
}
|
||||
if (currentTime < duration) {
|
||||
setTimeout(animateScroll, increment)
|
||||
} else {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
animateScroll()
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查路径是否存在
|
||||
* @param {*} path
|
||||
*/
|
||||
export const checkPath = path => fs.existsSync(path)
|
||||
|
||||
|
||||
/**
|
||||
* 在资源管理器中打开目录
|
||||
* @param {*} 选项
|
||||
*/
|
||||
export const openSelectDir = options => remote.dialog.showOpenDialog(remote.getCurrentWindow(), options)
|
||||
|
||||
/**
|
||||
* 在资源管理器中打开目录
|
||||
* @param {*} 选项
|
||||
*/
|
||||
export const openSaveDir = options => remote.dialog.showSaveDialog(remote.getCurrentWindow(), options)
|
||||
|
||||
/**
|
||||
* 在资源管理器中打开目录
|
||||
* @param {*} dir
|
||||
*/
|
||||
export const openDirInExplorer = dir => {
|
||||
shell.showItemInFolder(dir)
|
||||
}
|
||||
|
||||
export const checkVersion = (currentVer, targetVer) => {
|
||||
// console.log(currentVer)
|
||||
// console.log(targetVer)
|
||||
currentVer = currentVer.split('.')
|
||||
targetVer = targetVer.split('.')
|
||||
let maxLen = Math.max(currentVer.length, targetVer.length)
|
||||
if (currentVer.length < maxLen) {
|
||||
for (let index = 0, len = maxLen - currentVer.length; index < len; index++) {
|
||||
currentVer.push(0)
|
||||
}
|
||||
}
|
||||
if (targetVer.length < maxLen) {
|
||||
for (let index = 0, len = maxLen - targetVer.length; index < len; index++) {
|
||||
targetVer.push(0)
|
||||
}
|
||||
}
|
||||
for (let index = 0; index < currentVer.length; index++) {
|
||||
if (parseInt(currentVer[index]) < parseInt(targetVer[index])) return true
|
||||
if (parseInt(currentVer[index]) > parseInt(targetVer[index])) return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const isObject = item => item && typeof item === 'object' && !Array.isArray(item)
|
||||
|
||||
/**
|
||||
* 对象深度合并
|
||||
* 注意:循环引用的对象会出现死循环
|
||||
* @param {} target 要合并源对象
|
||||
* @param {} source 要合并目标对象
|
||||
*/
|
||||
export const objectDeepMerge = (target, source) => {
|
||||
let base = {}
|
||||
Object.keys(source).forEach(item => {
|
||||
if (Array.isArray(source[item])) {
|
||||
let arr = Array.isArray(target[item]) ? target[item] : []
|
||||
target[item] = arr.concat(source[item])
|
||||
return
|
||||
} else if (isObject(source[item])) {
|
||||
if (!isObject(target[item])) target[item] = {}
|
||||
objectDeepMerge(target[item], source[item])
|
||||
return
|
||||
}
|
||||
base[item] = source[item]
|
||||
})
|
||||
Object.assign(target, base)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否父子元素
|
||||
* @param {*} parent
|
||||
* @param {*} children
|
||||
*/
|
||||
export const isChildren = (parent, children) => {
|
||||
return children.parentNode ? children.parentNode === parent ? true : isChildren(parent, children.parentNode) : false
|
||||
}
|
||||
|
||||
export const updateSetting = setting => {
|
||||
const defaultVersion = '1.0.1'
|
||||
const defaultSetting = {
|
||||
version: defaultVersion,
|
||||
player: {
|
||||
togglePlayMethod: 'listLoop',
|
||||
highQuality: false,
|
||||
},
|
||||
list: {
|
||||
isShowAlbumName: true,
|
||||
},
|
||||
download: {
|
||||
savePath: path.join(os.homedir(), 'Desktop'),
|
||||
fileName: '歌名 - 歌手',
|
||||
},
|
||||
leaderboard: {
|
||||
source: 'kw',
|
||||
tabId: 'kwbiaosb',
|
||||
},
|
||||
themeId: 0,
|
||||
sourceId: 'kw',
|
||||
randomAnimate: true,
|
||||
}
|
||||
const overwriteSetting = {
|
||||
version: defaultVersion,
|
||||
sourceId: 'kw',
|
||||
}
|
||||
|
||||
|
||||
if (!setting) {
|
||||
setting = defaultSetting
|
||||
} else if (checkVersion(setting.version, defaultSetting.version)) {
|
||||
objectDeepMerge(defaultSetting, setting)
|
||||
objectDeepMerge(defaultSetting, overwriteSetting)
|
||||
setting = defaultSetting
|
||||
}
|
||||
return setting
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import kw from './kw'
|
||||
import kg from './kg'
|
||||
import tx from './tx'
|
||||
import wy from './wy'
|
||||
export default {
|
||||
sources: [
|
||||
{
|
||||
name: '酷我音乐',
|
||||
id: 'kw',
|
||||
},
|
||||
{
|
||||
name: '酷狗音乐',
|
||||
id: 'kg',
|
||||
},
|
||||
{
|
||||
name: 'QQ音乐',
|
||||
id: 'tx',
|
||||
},
|
||||
{
|
||||
name: '网易音乐',
|
||||
id: 'wy',
|
||||
},
|
||||
],
|
||||
kw,
|
||||
kg,
|
||||
tx,
|
||||
wy,
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import leaderboard from './leaderboard'
|
||||
|
||||
const kg = {
|
||||
leaderboard,
|
||||
}
|
||||
|
||||
export default kg
|
|
@ -0,0 +1,125 @@
|
|||
import { httpGet, cancelHttp } from '../../request'
|
||||
import { formatPlayTime } from '../../index'
|
||||
|
||||
export default {
|
||||
list: [
|
||||
{
|
||||
id: 'kgtop500',
|
||||
name: '酷狗TOP500',
|
||||
bangid: '8888',
|
||||
},
|
||||
{
|
||||
id: 'kgwlhgb',
|
||||
name: '网络红歌榜',
|
||||
bangid: '23784',
|
||||
},
|
||||
{
|
||||
id: 'kgbsb',
|
||||
name: '飙升榜',
|
||||
bangid: '6666',
|
||||
},
|
||||
{
|
||||
id: 'kgfxb',
|
||||
name: '分享榜',
|
||||
bangid: '21101',
|
||||
},
|
||||
{
|
||||
id: 'kgcyyb',
|
||||
name: '纯音乐榜',
|
||||
bangid: '33164',
|
||||
},
|
||||
{
|
||||
id: 'kggfjqb',
|
||||
name: '古风金曲榜',
|
||||
bangid: '33161',
|
||||
},
|
||||
{
|
||||
id: 'kgyyjqb',
|
||||
name: '粤语金曲榜',
|
||||
bangid: '33165',
|
||||
},
|
||||
{
|
||||
id: 'kgomjqb',
|
||||
name: '欧美金曲榜',
|
||||
bangid: '33166',
|
||||
},
|
||||
// {
|
||||
// id: 'kgdyrgb',
|
||||
// name: '电音热歌榜',
|
||||
// bangid: '33160',
|
||||
// },
|
||||
// {
|
||||
// id: 'kgjdrgb',
|
||||
// name: 'DJ热歌榜',
|
||||
// bangid: '24971',
|
||||
// },
|
||||
// {
|
||||
// id: 'kghyxgb',
|
||||
// name: '华语新歌榜',
|
||||
// bangid: '31308',
|
||||
// },
|
||||
],
|
||||
getUrl(p, id) {
|
||||
return `http://www2.kugou.kugou.com/yueku/v9/rank/home/${p}-${id}.html`
|
||||
},
|
||||
regExps: {
|
||||
total: /total: '(\d+)',/,
|
||||
page: /page: '(\d+)',/,
|
||||
limit: /pagesize: '(\d+)',/,
|
||||
listData: /global\.features = (\[.+\]);/,
|
||||
},
|
||||
_cancelIndex: null,
|
||||
_cancelPromiseCancelFn: null,
|
||||
getData(url) {
|
||||
if (this._cancelIndex != null) {
|
||||
cancelHttp(this._cancelIndex)
|
||||
this._cancelPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._cancelPromiseCancelFn = reject
|
||||
this._cancelIndex = httpGet(url, (err, resp, body) => {
|
||||
this._cancelIndex = null
|
||||
this._cancelPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
},
|
||||
filterData(rawList) {
|
||||
return rawList.map(item => ({
|
||||
singer: item.singername,
|
||||
name: item.songname,
|
||||
albumName: item.album_name,
|
||||
albumId: item.album_id,
|
||||
songmid: item.audio_id,
|
||||
source: 'kg',
|
||||
interval: formatPlayTime(item.duration / 1000),
|
||||
img: null,
|
||||
lrc: null,
|
||||
typeUrl: {},
|
||||
}))
|
||||
},
|
||||
getList(id, page) {
|
||||
let type = this.list.find(s => s.id === id)
|
||||
if (!type) return Promise.reject()
|
||||
return this.getData(this.getUrl(page, type.bangid)).then(html => {
|
||||
let total = html.match(this.regExps.total)
|
||||
if (total) total = parseInt(RegExp.$1)
|
||||
page = html.match(this.regExps.page)
|
||||
if (page) page = parseInt(RegExp.$1)
|
||||
let limit = html.match(this.regExps.limit)
|
||||
if (limit) limit = parseInt(RegExp.$1)
|
||||
let listData = html.match(this.regExps.listData)
|
||||
if (listData) listData = this.filterData(JSON.parse(RegExp.$1))
|
||||
return {
|
||||
total,
|
||||
list: listData,
|
||||
limit,
|
||||
page,
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
import { httpGet, cancelHttp } from '../../request'
|
||||
import tempSearch from './tempSearch'
|
||||
import musicSearch from './musicSearch'
|
||||
import { formatSinger } from './util'
|
||||
import leaderboard from './leaderboard'
|
||||
import lyric from './lyric'
|
||||
|
||||
const kw = {
|
||||
_musicInfoIndex: null,
|
||||
_musicInfoPromiseCancelFn: null,
|
||||
_musicPicIndex: null,
|
||||
_musicPicPromiseCancelFn: null,
|
||||
// context: null,
|
||||
|
||||
|
||||
// init(context) {
|
||||
// if (this.isInited) return
|
||||
// this.isInited = true
|
||||
// this.context = context
|
||||
|
||||
// // this.musicSearch.search('我又想你了').then(res => {
|
||||
// // console.log(res)
|
||||
// // })
|
||||
|
||||
// // this.getMusicUrl('62355680', '320k').then(url => {
|
||||
// // console.log(url)
|
||||
// // })
|
||||
// },
|
||||
|
||||
tempSearch,
|
||||
musicSearch,
|
||||
leaderboard,
|
||||
getLyric(songInfo) {
|
||||
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
|
||||
return lyric.getLyric(songInfo.songmid)
|
||||
},
|
||||
handleMusicInfo(songInfo) {
|
||||
return this.getMusicInfo(songInfo).then(info => {
|
||||
// console.log(JSON.stringify(info))
|
||||
songInfo.name = info.name
|
||||
songInfo.singer = formatSinger(info.artist)
|
||||
songInfo.img = info.pic
|
||||
songInfo.albumName = info.album
|
||||
return songInfo
|
||||
// return Object.assign({}, songInfo, {
|
||||
// name: info.name,
|
||||
// singer: formatSinger(info.artist),
|
||||
// img: info.pic,
|
||||
// albumName: info.album,
|
||||
// })
|
||||
})
|
||||
},
|
||||
|
||||
getMusicUrl(songInfo, type) {
|
||||
return new Promise((resolve, reject) => {
|
||||
httpGet(`https://v1.itooi.cn/kuwo/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, (err, resp, body) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
return this.getMusicUrl(songInfo, type)
|
||||
}
|
||||
body.code === 200 ? resolve({ type, url: body.data }) : reject(new Error(body.msg))
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
getMusicInfo(songInfo) {
|
||||
if (this._musicInfoIndex != null) {
|
||||
cancelHttp(this._musicInfoIndex)
|
||||
this._musicInfoPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicInfoPromiseCancelFn = reject
|
||||
this._musicInfoIndex = httpGet(`http://www.kuwo.cn/api/www/music/musicInfo?mid=${songInfo.songmid}`, (err, resp, body) => {
|
||||
this._musicInfoIndex = null
|
||||
this._musicInfoPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
body.code === 200 ? resolve(body.data) : reject(new Error(body.msg))
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
getMusicUrls(musicInfo, cb) {
|
||||
let tasks = []
|
||||
let songId = musicInfo.songmid
|
||||
musicInfo.types.forEach(type => {
|
||||
tasks.push(kw.getMusicUrl(songId, type.type))
|
||||
})
|
||||
Promise.all(tasks).then(urlInfo => {
|
||||
let typeUrl = {}
|
||||
urlInfo.forEach(info => {
|
||||
typeUrl[info.type] = info.url
|
||||
})
|
||||
cb(typeUrl)
|
||||
})
|
||||
},
|
||||
|
||||
getPic(songInfo) {
|
||||
if (this._musicPicIndex != null) {
|
||||
cancelHttp(this._musicPicIndex)
|
||||
this._musicPicPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicPicPromiseCancelFn = reject
|
||||
this._musicPicIndex = httpGet(`https://v1.itooi.cn/kuwo/pic?id=${songInfo.songmid}&isRedirect=0`, (err, resp, body) => {
|
||||
this._musicPicIndex = null
|
||||
this._musicPicPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
console.log(body)
|
||||
body.code === 200 ? resolve(body.data) : reject(new Error(body.msg))
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export default kw
|
|
@ -0,0 +1,190 @@
|
|||
import { httpGet, cancelHttp } from '../../request'
|
||||
import { formatPlayTime } from '../../index'
|
||||
|
||||
export default {
|
||||
list: [
|
||||
{
|
||||
id: 'kwbiaosb',
|
||||
name: '飙升榜',
|
||||
bangid: 93,
|
||||
},
|
||||
{
|
||||
id: 'kwregb',
|
||||
name: '热歌榜',
|
||||
bangid: 16,
|
||||
},
|
||||
{
|
||||
id: 'kwhuiyb',
|
||||
name: '会员榜',
|
||||
bangid: 145,
|
||||
},
|
||||
{
|
||||
id: 'kwdouyb',
|
||||
name: '抖音榜',
|
||||
bangid: 158,
|
||||
},
|
||||
{
|
||||
id: 'kwqsb',
|
||||
name: '趋势榜',
|
||||
bangid: 187,
|
||||
},
|
||||
{
|
||||
id: 'kwhuaijb',
|
||||
name: '怀旧榜',
|
||||
bangid: 26,
|
||||
},
|
||||
{
|
||||
id: 'kwhuayb',
|
||||
name: '华语榜',
|
||||
bangid: 104,
|
||||
},
|
||||
{
|
||||
id: 'kwyueyb',
|
||||
name: '粤语榜',
|
||||
bangid: 182,
|
||||
},
|
||||
{
|
||||
id: 'kwoumb',
|
||||
name: '欧美榜',
|
||||
bangid: 22,
|
||||
},
|
||||
{
|
||||
id: 'kwhanyb',
|
||||
name: '韩语榜',
|
||||
bangid: 184,
|
||||
},
|
||||
{
|
||||
id: 'kwriyb',
|
||||
name: '日语榜',
|
||||
bangid: 183,
|
||||
},
|
||||
],
|
||||
getUrl: (p, l, id) => `http://kbangserver.kuwo.cn/ksong.s?from=pc&fmt=json&pn=${p - 1}&rn=${l}&type=bang&data=content&id=${id}&show_copyright_off=0&pcmp4=1&isbang=1`,
|
||||
getUrl2: (p, l, id) => `http://www.kuwo.cn/api/www/bang/bang/musicList?bangId=${id}&pn=${p}&rn=${l}`,
|
||||
regExps: {
|
||||
|
||||
},
|
||||
limit: 30,
|
||||
_cancelIndex: null,
|
||||
_cancelPromiseCancelFn: null,
|
||||
_cancelIndex2: null,
|
||||
_cancelPromiseCancelFn2: null,
|
||||
getData(url) {
|
||||
if (this._cancelIndex != null) {
|
||||
cancelHttp(this._cancelIndex)
|
||||
this._cancelPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._cancelPromiseCancelFn = reject
|
||||
this._cancelIndex = httpGet(url, (err, resp, body) => {
|
||||
this._cancelIndex = null
|
||||
this._cancelPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
},
|
||||
getData2(url) {
|
||||
if (this._cancelIndex2 != null) {
|
||||
cancelHttp(this._cancelIndex2)
|
||||
this._cancelPromiseCancelFn2(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._cancelPromiseCancelFn2 = reject
|
||||
this._cancelIndex2 = httpGet(url, (err, resp, body) => {
|
||||
this._cancelIndex2 = null
|
||||
this._cancelPromiseCancelFn2 = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
},
|
||||
filterData(rawList, rawList2) {
|
||||
return rawList.map((item, inedx) => {
|
||||
let formats = item.formats.split('|')
|
||||
let types = []
|
||||
let _types = {}
|
||||
if (formats.indexOf('MP3128')) {
|
||||
types.push({ type: '128k', size: null })
|
||||
_types['128k'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
if (formats.indexOf('MP3192')) {
|
||||
types.push({ type: '192k', size: null })
|
||||
_types['192k'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
if (formats.indexOf('MP3H')) {
|
||||
types.push({ type: '320k', size: null })
|
||||
_types['320k'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
if (formats.indexOf('AL')) {
|
||||
types.push({ type: 'ape', size: null })
|
||||
_types['ape'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
if (formats.indexOf('ALFLAC')) {
|
||||
types.push({ type: 'flac', size: null })
|
||||
_types['flac'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
types.reverse()
|
||||
return {
|
||||
singer: item.artist,
|
||||
name: item.name,
|
||||
albumName: item.album,
|
||||
albumId: item.albumid,
|
||||
songmid: item.id,
|
||||
source: 'kw',
|
||||
interval: formatPlayTime(rawList2[inedx].duration),
|
||||
img: item.pic,
|
||||
lrc: null,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
}
|
||||
})
|
||||
},
|
||||
loadData(p1, p2, page, bangid) {
|
||||
return Promise.all([p1, p2]).then(([data1, data2]) => {
|
||||
// console.log(data1, data2)
|
||||
if (!data1.musiclist.length) {
|
||||
return this.loadData(this.getData(this.getUrl(page, this.limit, bangid)),
|
||||
data2.data.musicList.length
|
||||
? Promise.resolve(data2)
|
||||
: this.getData2(this.getUrl2(page, this.limit, bangid)), page, bangid)
|
||||
}
|
||||
if (!data2.data.musicList.length) {
|
||||
return this.loadData(Promise.resolve(data1), this.getData2(this.getUrl2(page, this.limit, bangid)), page, bangid)
|
||||
}
|
||||
return Promise.resolve([data1, data2])
|
||||
})
|
||||
},
|
||||
getList(id, page) {
|
||||
let type = this.list.find(s => s.id === id)
|
||||
if (!type) return Promise.reject()
|
||||
return this.loadData(this.getData(this.getUrl(page, this.limit, type.bangid)), this.getData2(this.getUrl2(page, this.limit, type.bangid)), page, type.bangid).then(([data1, data2]) => {
|
||||
// console.log(data1.musiclist, data2.data)
|
||||
let total = parseInt(data1.num)
|
||||
let list = this.filterData(data1.musiclist, data2.data.musicList)
|
||||
return {
|
||||
total,
|
||||
list,
|
||||
limit: this.limit,
|
||||
page,
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { httpGet, cancelHttp } from '../../request'
|
||||
|
||||
export default {
|
||||
_musicLrcIndex: null,
|
||||
_musicLrcPromiseCancelFn: null,
|
||||
formatTime(time) {
|
||||
let m = parseInt(time / 60)
|
||||
let s = (time % 60).toFixed(2)
|
||||
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
|
||||
},
|
||||
getLyric(songId) {
|
||||
if (this._musicLrcIndex != null) {
|
||||
cancelHttp(this._musicLrcIndex)
|
||||
this._musicLrcPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicLrcPromiseCancelFn = reject
|
||||
this._musicLrcIndex = httpGet(`https://v1.itooi.cn/kuwo/lrc?id=${songId}`, (err, resp, body) => {
|
||||
this._musicLrcIndex = null
|
||||
this._musicLrcPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
// console.log(body.data)
|
||||
// console.log(this.transformLrc(body.data))
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
// import '../../polyfill/array.find'
|
||||
// import jshtmlencode from 'js-htmlencode'
|
||||
import { httpGet, cancelHttp } from '../../request'
|
||||
import { formatPlayTime, decodeName } from '../../index'
|
||||
// import { debug } from '../../utils/env'
|
||||
import { formatSinger } from './util'
|
||||
|
||||
export default {
|
||||
regExps: {
|
||||
mInfo: /bitrate:(\d+),format:(\w+),size:([\w.]+)/,
|
||||
},
|
||||
_musicSearchIndex: null,
|
||||
_musicSearchPromiseCancelFn: null,
|
||||
limit: 30,
|
||||
total: 0,
|
||||
page: 0,
|
||||
allPage: 1,
|
||||
// cancelFn: null,
|
||||
musicSearch(str, page) {
|
||||
if (this._musicSearchIndex != null) {
|
||||
cancelHttp(this._musicSearchIndex)
|
||||
this._musicSearchPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicSearchPromiseCancelFn = reject
|
||||
this._musicSearchIndex = httpGet(`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${this.limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`, (err, resp, body) => {
|
||||
this._musicSearchIndex = null
|
||||
this._musicSearchPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
},
|
||||
// getImg(songId) {
|
||||
// return httpGet(`http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_${songId}`)
|
||||
// },
|
||||
// getLrc(songId) {
|
||||
// return httpGet(`http://mobile.kuwo.cn/mpage/html5/songinfoandlrc?mid=${songId}&flag=0`)
|
||||
// },
|
||||
handleResult(rawData) {
|
||||
const result = []
|
||||
for (let i = 0; i < rawData.length; i++) {
|
||||
const info = rawData[i]
|
||||
let songId = info.MUSICRID.replace('MUSIC_', '')
|
||||
// const format = (info.FORMATS || info.formats).split('|')
|
||||
|
||||
if (!info.MINFO) {
|
||||
console.log('mInfo is undefined')
|
||||
return null
|
||||
}
|
||||
|
||||
const types = []
|
||||
const _types = {}
|
||||
|
||||
let infoArr = info.MINFO.split(';')
|
||||
infoArr.forEach(info => {
|
||||
info = info.match(this.regExps.mInfo)
|
||||
if (info) {
|
||||
switch (info[2]) {
|
||||
case 'flac':
|
||||
types.push({ type: 'flac', size: info[3] })
|
||||
_types.flac = {
|
||||
size: info[3].toLocaleUpperCase(),
|
||||
}
|
||||
break
|
||||
case 'ape':
|
||||
types.push({ type: 'ape', size: info[3] })
|
||||
_types.ape = {
|
||||
size: info[3].toLocaleUpperCase(),
|
||||
}
|
||||
break
|
||||
case 'mp3':
|
||||
switch (info[1]) {
|
||||
case '320':
|
||||
types.push({ type: '320k', size: info[3] })
|
||||
_types['320k'] = {
|
||||
size: info[3].toLocaleUpperCase(),
|
||||
}
|
||||
break
|
||||
case '192':
|
||||
types.push({ type: '192k', size: info[3] })
|
||||
_types['192k'] = {
|
||||
size: info[3].toLocaleUpperCase(),
|
||||
}
|
||||
break
|
||||
case '128':
|
||||
types.push({ type: '128k', size: info[3] })
|
||||
_types['128k'] = {
|
||||
size: info[3].toLocaleUpperCase(),
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
types.reverse()
|
||||
|
||||
let interval = parseInt(info.DURATION)
|
||||
|
||||
result.push({
|
||||
name: decodeName(info.SONGNAME),
|
||||
singer: formatSinger(decodeName(info.ARTIST)),
|
||||
source: 'kw',
|
||||
// img = (info.album.name === '' || info.album.name === '空')
|
||||
// ? `http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_160911.jpg`
|
||||
// : `https://y.gtimg.cn/music/photo_new/T002R500x500M000${info.album.mid}.jpg`
|
||||
songmid: songId,
|
||||
albumId: decodeName(info.ALBUMID || ''),
|
||||
interval: Number.isNaN(interval) ? 0 : formatPlayTime(interval),
|
||||
albumName: info.ALBUM ? decodeName(info.ALBUM) : '',
|
||||
lyric: null,
|
||||
img: null,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
})
|
||||
}
|
||||
return result
|
||||
},
|
||||
search(str, page = 1, { limit }) {
|
||||
if (limit != null) this.limit = limit
|
||||
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
|
||||
return this.musicSearch(str, page).then(result => {
|
||||
if (!result || (result.TOTAL !== '0' && result.SHOW === '0')) return this.search(str, page, { limit })
|
||||
let list = this.handleResult(result.abslist)
|
||||
|
||||
if (list == null) return this.search(str, page, { limit })
|
||||
|
||||
this.total = parseInt(result.TOTAL)
|
||||
this.page = page
|
||||
this.allPage = Math.ceil(this.total / this.limit)
|
||||
|
||||
return Promise.resolve({
|
||||
list,
|
||||
allPage: this.allPage,
|
||||
total: this.total,
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import { httpGet, cancelHttp } from '../../request'
|
||||
import { decodeName } from '../../index'
|
||||
|
||||
export default {
|
||||
regExps: {
|
||||
relWord: /RELWORD=(.+)/,
|
||||
},
|
||||
_musicTempSearchIndex: null,
|
||||
_musicTempSearchPromiseCancelFn: null,
|
||||
tempSearch(str) {
|
||||
if (this._musicTempSearchIndex != null) {
|
||||
cancelHttp(this._musicTempSearchIndex)
|
||||
this._musicTempSearchPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._musicTempSearchPromiseCancelFn = reject
|
||||
this._musicTempSearchIndex = httpGet(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`, (err, resp, body) => {
|
||||
this._musicTempSearchIndex = null
|
||||
this._musicTempSearchPromiseCancelFn = null
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
}
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
},
|
||||
handleResult(rawData) {
|
||||
return rawData.map(info => {
|
||||
let matchResult = info.match(this.regExps.relWord)
|
||||
return matchResult ? decodeName(matchResult[1]) : ''
|
||||
})
|
||||
},
|
||||
cancelTempSearch() {
|
||||
if (this._musicTempSearchIndex != null) {
|
||||
cancelHttp(this._musicTempSearchIndex)
|
||||
this._musicTempSearchPromiseCancelFn(new Error('取消http请求'))
|
||||
}
|
||||
},
|
||||
search(str) {
|
||||
return this.tempSearch(str).then(result => this.handleResult(result.data))
|
||||
},
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
export const formatSinger = rawData => rawData.replace(/&/g, '、')
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue