├── .cz-config.js
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .husky
├── commit-msg
└── pre-commit
├── .prettierrc.js
├── .vscode
└── extensions.json
├── README.md
├── auto-imports.d.ts
├── commitlint.config.js
├── components.d.ts
├── deploy.sh
├── index.html
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── public
├── logo.png
└── ui
│ ├── 008-01.jpg
│ ├── 008-02.jpg
│ ├── 008-03.jpg
│ ├── 008-04.jpg
│ ├── 008-05.jpg
│ └── 008-06.jpg
├── src
├── App.vue
├── assets
│ ├── base.scss
│ ├── img
│ │ ├── OpticalDisk.png
│ │ ├── ScanQrCode.png
│ │ ├── index.ts
│ │ └── logo.png
│ └── theme.scss
├── components
│ ├── Banner.vue
│ ├── CoverPlay.vue
│ ├── IconPark.vue
│ ├── Loading.vue
│ ├── MoreText.vue
│ ├── MvList.vue
│ ├── PlayedList.vue
│ ├── Playing.vue
│ ├── SongList.vue
│ └── Title.vue
├── layout
│ ├── footer
│ │ ├── Footer.vue
│ │ ├── PlayerAction.vue
│ │ ├── PlayerController.vue
│ │ ├── PlayerSlider.vue
│ │ ├── PlayerSong.vue
│ │ └── PlayerVolumeSlider.vue
│ ├── header
│ │ ├── Header.vue
│ │ ├── SearchPop.vue
│ │ ├── SearchSuggest.vue
│ │ └── UserInfo.vue
│ └── menu
│ │ ├── Menu.vue
│ │ ├── MenuList.vue
│ │ └── useMenu.ts
├── main.ts
├── models
│ ├── album.ts
│ ├── artist.ts
│ ├── artist_detail.ts
│ ├── banner.ts
│ ├── dj.ts
│ ├── mv.ts
│ ├── personalized.ts
│ ├── playlist.ts
│ ├── playlist_cat.ts
│ ├── playlist_hot.ts
│ ├── search.ts
│ ├── song.ts
│ ├── song_url.ts
│ ├── top_song.ts
│ ├── toplist_detail.ts
│ ├── user.ts
│ ├── video.ts
│ └── video_detail.ts
├── router
│ └── index.ts
├── stores
│ ├── common.ts
│ ├── index.ts
│ ├── music.ts
│ ├── personalized.ts
│ ├── player.ts
│ ├── search.ts
│ ├── user.ts
│ └── video.ts
├── utils
│ ├── api.ts
│ ├── extend.ts
│ ├── http.ts
│ └── number.ts
├── views
│ ├── Index.vue
│ ├── album
│ │ ├── Desc.vue
│ │ ├── Index.vue
│ │ └── Info.vue
│ ├── artist
│ │ ├── Album.vue
│ │ ├── ArtistDetail.vue
│ │ ├── Desc.vue
│ │ ├── Info.vue
│ │ ├── Mv.vue
│ │ └── Songs.vue
│ ├── discover
│ │ ├── DjProgram.vue
│ │ ├── Index.vue
│ │ ├── Mv.vue
│ │ ├── Personalized.vue
│ │ └── PersonalizedNewSong.vue
│ ├── dj
│ │ ├── Index.vue
│ │ ├── Newcomer.vue
│ │ ├── Pay.vue
│ │ ├── Personalize.vue
│ │ ├── Popular.vue
│ │ └── Today.vue
│ ├── music
│ │ ├── Index.vue
│ │ ├── MusicController.ts
│ │ ├── artist
│ │ │ └── Artist.vue
│ │ ├── category
│ │ │ ├── Category.vue
│ │ │ └── PlaylistHot.vue
│ │ ├── picked
│ │ │ ├── Picked.vue
│ │ │ └── Video.vue
│ │ ├── topList
│ │ │ ├── GlobalList.vue
│ │ │ ├── OfficialList.vue
│ │ │ └── TopList.vue
│ │ └── topSong
│ │ │ └── TopSong.vue
│ ├── mv
│ │ ├── MvDetail
│ │ │ ├── Index.vue
│ │ │ ├── Info.vue
│ │ │ └── SimiMV.vue
│ │ └── MvList
│ │ │ ├── Exclusive.vue
│ │ │ ├── Hot.vue
│ │ │ ├── Index.vue
│ │ │ └── New.vue
│ ├── playlist
│ │ ├── Index.vue
│ │ ├── PlayListInfo.vue
│ │ └── SongsList.vue
│ └── video
│ │ ├── Index.vue
│ │ ├── VideoController.ts
│ │ ├── videoDetail
│ │ ├── Index.vue
│ │ ├── Info.vue
│ │ └── SimiVideo.vue
│ │ └── videoList
│ │ └── Index.vue
└── vite-env.d.ts
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.cz-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | types: [
3 | { value: 'feature', name: 'feature: 新增功能' },
4 | { value: 'bug', name: 'bug: 测试反馈bug列表中的bug号' },
5 | { value: 'fix', name: 'fix: 修复 bug' },
6 | { value: 'ui', name: 'ui: 更新 UI' },
7 | { value: 'docs', name: 'docs: 更新文档' },
8 | { value: 'style', name: 'style: 样式更改' },
9 | { value: 'perf', name: 'perf: 性能优化' },
10 | { value: 'refactor', name: 'refactor: 重构(既不是增加feature,也不是修复bug)' },
11 | { value: 'test', name: 'test: 增加测试' },
12 | { value: 'chore', name: 'chore: 杂项、其他更改' },
13 | { value: 'ci', name: 'ci: 更新 CI/CD 等自动化配置' },
14 | { value: 'revert', name: 'revert: 回退' },
15 | { value: 'merge', name: 'merge: 合并分支' },
16 | { value: 'release', name: 'release: 发布' },
17 | { value: 'deploy', name: 'deploy: 部署' },
18 | { value: 'build', name: 'build: 打包' }
19 | ],
20 | scopes: [''],
21 | // override the messages, defaults are as follows
22 | messages: {
23 | type: '请选择提交类型:',
24 | scope: '请输入您修改的范围(可选):',
25 | subject: '请简要描述提交 message (必填):',
26 | body: '请输入详细描述(可选,待优化去除,跳过即可):',
27 | footer: '请输入要关闭的issue(待优化去除,跳过即可):',
28 | confirmCommit: '确认使用以上信息提交?(y/n/e/h)'
29 | },
30 | allowCustomScopes: true,
31 | // skipEmptyScopes: true,
32 | skipQuestions: ['body', 'footer'],
33 | subjectLimit: 72
34 | }
35 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | index.html
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: 'vue-eslint-parser',
3 |
4 | parserOptions: {
5 | parser: '@typescript-eslint/parser',
6 | ecmaVersion: 2020,
7 | sourceType: 'module',
8 | ecmaFeatures: {
9 | jsx: true
10 | }
11 | },
12 |
13 | extends: ['plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', 'prettier', 'plugin:prettier/recommended'],
14 |
15 | rules: {
16 | // override/add rules settings here, such as:
17 | //关闭组件强制驼峰命名规则
18 | 'vue/multi-word-component-names': 'off'
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 | pnpm-lock.yaml
10 |
11 | node_modules
12 | # dist
13 | dist-ssr
14 | *.local
15 |
16 | # Editor directories and files
17 | .vscode/*
18 | !.vscode/extensions.json
19 | .idea
20 | .DS_Store
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw?
26 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx --no -- commitlint --edit $1
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx --no-install lint-staged
5 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // 一行最多 150 字符
3 | printWidth: 150,
4 | // 使用 2 个空格缩进
5 | tabWidth: 2,
6 | // 不使用 tab 缩进,而使用空格
7 | useTabs: false,
8 | // 行尾需要有分号
9 | semi: false,
10 | // 使用单引号代替双引号
11 | singleQuote: true,
12 | // 末尾使用逗号
13 | trailingComma: 'none',
14 | // 对象的 key 仅在必要时用引号
15 | quoteProps: 'as-needed',
16 | // jsx 不使用单引号,而使用双引号
17 | jsxSingleQuote: false,
18 | // 大括号内的首尾需要空格 { foo: bar }
19 | bracketSpacing: true,
20 | // jsx 标签的反尖括号需要换行
21 | jsxBracketSameLine: false,
22 | // 箭头函数,只有一个参数的时候,也需要括号
23 | arrowParens: 'always',
24 | // 每个文件格式化的范围是文件的全部内容
25 | rangeStart: 0,
26 | rangeEnd: Infinity,
27 | // 不需要写文件开头的 @prettier
28 | requirePragma: false,
29 | // 不需要自动在文件开头插入 @prettier
30 | insertPragma: false,
31 | // 使用默认的折行标准
32 | proseWrap: 'preserve',
33 | // 根据显示样式决定 html 要不要折行
34 | htmlWhitespaceSensitivity: 'css',
35 | // 换行符使用 lf
36 | endOfLine: 'auto'
37 | }
38 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar"]
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
VUE3-MUSIC
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | ## 仿网易云音乐
12 |
13 | 基于 VITE + VUE3 + TS + PINIA + TAILWINDCSS 开发的音乐播放器,界面模仿网易云音乐客户端。
14 |
15 | 参考 [SmallRuralDog/vue3-music](https://github.com/SmallRuralDog/vue3-music) 进行开发、改进和完善。
16 |
17 | 在线体验:[https://m.zugelu.com/](https://m.zugelu.com/)
18 |
19 | 如果觉得项目不错,欢迎 Star 支持,感谢!
20 |
21 | ## 本地安装
22 |
23 | ```
24 | git clone https://github.com/luzhe0359/vue3-music.git
25 | cd vue3-music
26 | pnpm install
27 | pnpm run dev
28 | ```
29 |
30 | ## 网易云音乐 API
31 |
32 | 需运行 API 服务,才能正常访问
33 |
34 | [开发文档](https://binaryify.github.io/NeteaseCloudMusicApi)
35 |
36 | ## 功能模块
37 |
38 | - [ ] 手机号登录
39 | - [x] 二维码登录
40 | - [x] 主题切换
41 |
42 | **推荐**
43 |
44 | - [x] 专属歌单
45 | - [x] 推荐新音乐
46 | - [x] 推荐 MV
47 |
48 | **音乐馆**
49 |
50 | - [x] 精选
51 | - [x] 歌单
52 | - [x] 排行榜
53 | - [x] 歌手
54 | - [x] 新歌速递
55 |
56 | **视频**
57 |
58 | - [x] 视频列表
59 | - [x] mv 列表
60 |
61 | **电台**
62 |
63 | - [x] 推荐电台
64 | - [x] 最热主播榜
65 | - [x] 主播新人榜
66 | - [x] 付费精品
67 |
68 | ## 四、UI 界面
69 |
70 | 
71 |
72 | 
73 |
74 | 
75 |
76 | 
77 |
78 | 
79 |
80 | 
81 |
--------------------------------------------------------------------------------
/auto-imports.d.ts:
--------------------------------------------------------------------------------
1 | // Generated by 'unplugin-auto-import'
2 | export {}
3 | declare global {}
4 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional', 'cz'],
3 | rules: {
4 | // 选项
5 | 'type-enum': [
6 | 2,
7 | 'always',
8 | [
9 | 'feature', // 新增功能
10 | 'bug', // 此项特别针对bug号,用于向测试反馈bug列表的bug修改情况
11 | 'fix', // 修补 bug
12 | 'ui', // 更新 UI
13 | 'docs', // 更新文档
14 | 'style', // 样式更改
15 | 'perf', // 性能优化
16 | 'refactor', // 重构
17 | 'test', // 增加测试
18 | 'chore', // 杂项、其他更改
19 | 'ci', // 更新 CI/CD 等自动化配置
20 | 'revert', // 代码回滚
21 | 'merge', // 合并分支
22 | 'release', // 发布
23 | 'deploy', // 部署
24 | 'build' // 打包
25 | ]
26 | ],
27 | // 格式 小写
28 | 'type-case': [2, 'always', 'lower-case'],
29 | // 不能为空
30 | 'type-empty': [2, 'never'],
31 | // 范围选项
32 | 'scope-enum': [0, 'always'],
33 | // 范围格式
34 | 'scope-case': [0],
35 | // 范围是否为空
36 | 'scope-empty': [0],
37 | // 主要 message 不能为空
38 | 'subject-empty': [2, 'never'],
39 | // 以什么为结束标志,禁用
40 | 'subject-full-stop': [0, 'never'],
41 | // 格式,禁用
42 | 'subject-case': [0, 'never'],
43 | // 以空行开头
44 | 'body-leading-blank': [1, 'always'],
45 | 'header-max-length': [0, 'always', 72]
46 | }
47 | }
48 | /**
49 | * 代码提交规范 - 统一风格提交 (需要用到的包及作用)
50 | * commitizen cz-conventional-changelog
51 | * - 系统会提示您在提交时填写所有必需的提交字段
52 | * cz-customizable
53 | * - 汉化以上插件
54 | * @commitlint/config-conventional @commitlint/cli
55 | * - 限制提交格式
56 | */
57 |
--------------------------------------------------------------------------------
/components.d.ts:
--------------------------------------------------------------------------------
1 | // generated by unplugin-vue-components
2 | // We suggest you to commit this file into source control
3 | // Read more: https://github.com/vuejs/core/pull/3399
4 | import '@vue/runtime-core'
5 |
6 | export {}
7 |
8 | declare module '@vue/runtime-core' {
9 | export interface GlobalComponents {
10 | Banner: typeof import('./src/components/Banner.vue')['default']
11 | CoverPlay: typeof import('./src/components/CoverPlay.vue')['default']
12 | ElAffix: typeof import('element-plus/es')['ElAffix']
13 | ElAvatar: typeof import('element-plus/es')['ElAvatar']
14 | ElBadge: typeof import('element-plus/es')['ElBadge']
15 | ElButton: typeof import('element-plus/es')['ElButton']
16 | ElDialog: typeof import('element-plus/es')['ElDialog']
17 | ElDrawer: typeof import('element-plus/es')['ElDrawer']
18 | ElImage: typeof import('element-plus/es')['ElImage']
19 | ElInput: typeof import('element-plus/es')['ElInput']
20 | ElPopover: typeof import('element-plus/es')['ElPopover']
21 | ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
22 | ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
23 | ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
24 | ElSlider: typeof import('element-plus/es')['ElSlider']
25 | ElSpace: typeof import('element-plus/es')['ElSpace']
26 | ElTable: typeof import('element-plus/es')['ElTable']
27 | ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
28 | ElTabPane: typeof import('element-plus/es')['ElTabPane']
29 | ElTabs: typeof import('element-plus/es')['ElTabs']
30 | IconPark: typeof import('./src/components/IconPark.vue')['default']
31 | Loading: typeof import('./src/components/Loading.vue')['default']
32 | MoreText: typeof import('./src/components/MoreText.vue')['default']
33 | MvList: typeof import('./src/components/MvList.vue')['default']
34 | PlayedList: typeof import('./src/components/PlayedList.vue')['default']
35 | Playing: typeof import('./src/components/Playing.vue')['default']
36 | RouterLink: typeof import('vue-router')['RouterLink']
37 | RouterView: typeof import('vue-router')['RouterView']
38 | SongList: typeof import('./src/components/SongList.vue')['default']
39 | Title: typeof import('./src/components/Title.vue')['default']
40 | }
41 | export interface ComponentCustomProperties {
42 | vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
43 | vLoading: typeof import('element-plus/es')['ElLoadingDirective']
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | # 发生任何错误时终止
2 | set -e
3 |
4 | # 构建
5 | npm run build
6 |
7 | # 进入输出产物文件夹
8 | cd dist
9 |
10 | # 解决github-page刷新 404
11 | cp index.html 404.html
12 |
13 | # 如果你要部署到自定义域名
14 | echo 'm.zugelu.com' > CNAME
15 |
16 | git init
17 | git add -A
18 | git commit -m 'deploy'
19 |
20 | # 如果你要部署在 https://.github.io
21 | # git push -f git@github.com:/.github.io.git master
22 |
23 | # 如果你要部署在 https://.github.io/
24 | git push -f git@github.com:luzhe0359/vue3-music.git master:gh-pages
25 |
26 | cd -
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | vue3 - music
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue3-music",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vue-tsc --noEmit && vite build",
8 | "preview": "vite preview",
9 | "eslint:comment": "使用 ESLint 检查并自动修复 src 目录下所有扩展名为 .js 和 .vue 的文件",
10 | "eslint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
11 | "prettier:comment": "自动格式化当前目录下的所有文件",
12 | "prettier": "prettier . --write",
13 | "commit:comment": "引导设置规范化的提交信息",
14 | "commit": "git-cz"
15 | },
16 | "dependencies": {
17 | "@icon-park/vue-next": "^1.4.2",
18 | "@types/lodash-es": "^4.17.6",
19 | "@vueuse/components": "^9.1.1",
20 | "@vueuse/core": "^9.1.1",
21 | "axios": "^0.27.2",
22 | "dayjs": "^1.11.5",
23 | "element-plus": "2.2.26",
24 | "lodash": "^4.17.21",
25 | "pinia": "^2.0.21",
26 | "swiper": "^8.3.2",
27 | "vue": "^3.2.38",
28 | "vue-router": "^4.1.5"
29 | },
30 | "devDependencies": {
31 | "@commitlint/cli": "^17.1.2",
32 | "@commitlint/config-conventional": "^17.1.0",
33 | "@types/lodash": "^4.14.184",
34 | "@types/node": "^18.7.14",
35 | "@typescript-eslint/eslint-plugin": "^5.36.1",
36 | "@typescript-eslint/parser": "^5.36.1",
37 | "@vitejs/plugin-vue": "^3.0.3",
38 | "autoprefixer": "^10.4.8",
39 | "commitizen": "^4.2.5",
40 | "commitlint-config-cz": "^0.13.3",
41 | "cz-conventional-changelog": "^3.3.0",
42 | "cz-customizable": "^6.9.2",
43 | "eslint": "^8.23.0",
44 | "eslint-config-prettier": "^8.5.0",
45 | "eslint-plugin-prettier": "^4.2.1",
46 | "eslint-plugin-vue": "^9.4.0",
47 | "husky": "^8.0.1",
48 | "lint-staged": "^13.0.3",
49 | "postcss": "^8.4.16",
50 | "prettier": "^2.7.1",
51 | "sass": "^1.54.8",
52 | "tailwindcss": "^3.1.8",
53 | "typescript": "^4.8.2",
54 | "unplugin-auto-import": "^0.11.2",
55 | "unplugin-vue-components": "^0.22.4",
56 | "vite": "^3.0.9",
57 | "vue-tsc": "^0.39.5"
58 | },
59 | "config": {
60 | "commitizen": {
61 | "path": "./node_modules/cz-customizable"
62 | }
63 | },
64 | "lint-staged": {
65 | "*.{vue,js,ts,jsx,tsx}": [
66 | "pnpm run eslint",
67 | "pnpm run prettier"
68 | ]
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/public/logo.png
--------------------------------------------------------------------------------
/public/ui/008-01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/public/ui/008-01.jpg
--------------------------------------------------------------------------------
/public/ui/008-02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/public/ui/008-02.jpg
--------------------------------------------------------------------------------
/public/ui/008-03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/public/ui/008-03.jpg
--------------------------------------------------------------------------------
/public/ui/008-04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/public/ui/008-04.jpg
--------------------------------------------------------------------------------
/public/ui/008-05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/public/ui/008-05.jpg
--------------------------------------------------------------------------------
/public/ui/008-06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/public/ui/008-06.jpg
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/assets/base.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --el-color-primary: #34d399 !important;
7 | --el-color-primary-light-1: #065f46 !important;
8 | --el-color-primary-light-2: #047857 !important;
9 | --el-color-primary-light-3: #059669 !important;
10 | --el-color-primary-light-4: #10b981 !important;
11 | --el-color-primary-light-5: #34d399 !important;
12 | --el-color-primary-light-6: #6ee7b7 !important;
13 | --el-color-primary-light-7: #a7f3d0 !important;
14 | --el-color-primary-light-8: #d1fae5 !important;
15 | --el-color-primary-light-9: #ecfdf5 !important;
16 | --el-color-primary-dark-2: #047857 !important;
17 | --el-text-color-primary: text-slate-700 !important;
18 | }
19 |
20 | :root {
21 | &.dark {
22 | --el-color-primary: #34d399 !important;
23 | --el-color-primary-light-9: transparent !important; // hover按钮背景色
24 | --el-border-color-base: #57534e !important;
25 | --el-text-color-regular: #e2e8f0 !important;
26 | --el-text-color-primary: text-slate-200 !important;
27 | --el-bg-color: #141414 !important;
28 | --el-color-white: #fff !important;
29 | --el-border-color-lighter: #474648 !important;
30 | --el-border-color-light: #474648 !important;
31 | --el-button-active-bg-color: red;
32 |
33 | --el-popover-bg-color: #292524 !important;
34 | --el-fill-color-blank: #141414 !important;
35 | --el-fill-color-light: #222222 !important;
36 | --el-bg-color-overlay: #141414 !important;
37 | --el-mask-color: #222222 !important;
38 | }
39 | }
40 |
41 | @media (prefers-color-scheme: dark) {
42 | :root {
43 | --el-color-primary: #059669 !important;
44 |
45 | --el-color-primary-light-9: #57534e !important;
46 |
47 | --el-border-color-base: #57534e !important;
48 | --el-text-color-regular: #e2e8f0 !important;
49 | --el-text-color-primary: text-slate-200 !important;
50 | --el-bg-color: #474648 !important;
51 | --el-color-white: #141414 !important;
52 | --el-border-color-lighter: #474648 !important;
53 | --el-border-color-light: #474648 !important;
54 |
55 | --el-popover-bg-color: #292524 !important;
56 | }
57 | }
58 |
59 | html,
60 | body {
61 | font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
62 | @apply text-slate-700 bg-white transition-all;
63 | @apply dark:bg-[#141414] dark:text-slate-100;
64 | }
65 |
66 | .hover-text {
67 | @apply hover:text-emerald-400 transition-colors cursor-pointer;
68 | }
69 |
70 | .badge {
71 | .el-badge__content.is-fixed {
72 | @apply scale-75 bg-white text-slate-500 text-xs bg-opacity-50 border-0 left-2 -top-2;
73 | }
74 | }
75 |
76 | .el-popover.el-popper {
77 | min-width: auto;
78 | }
79 |
80 | .el-tabs__nav-wrap::after {
81 | height: 0 !important;
82 | }
83 |
84 | .el-tabs__header {
85 | margin: 0 !important;
86 | }
87 |
88 | /* el-divider样式无效bug */
89 | .el-divider--vertical {
90 | display: inline-block;
91 | width: 1px;
92 | height: 1em;
93 | margin: 0 8px;
94 | vertical-align: middle;
95 | position: relative;
96 | border-left: 1px var(--el-border-color) var(--el-border-style);
97 | @apply dark:border-stone-800;
98 | }
99 |
100 | .el-table {
101 | // 播放中+背景
102 | .table-playing {
103 | @apply bg-emerald-50 dark:bg-[#222];
104 | }
105 | // 边框
106 | td,
107 | th {
108 | border-bottom: none !important;
109 | }
110 | .el-table__inner-wrapper::before {
111 | height: 0;
112 | }
113 | // 过渡
114 | tr {
115 | @apply transition-colors;
116 | }
117 | }
118 |
119 | .el-tabs {
120 | .el-tabs__header {
121 | @apply sticky top-0 z-10 bg-white dark:bg-[#141414] transition-all;
122 | }
123 | }
124 |
125 | .el-skeleton {
126 | font-size: 0; // 骨架屏图片间隙
127 | }
128 |
--------------------------------------------------------------------------------
/src/assets/img/OpticalDisk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/src/assets/img/OpticalDisk.png
--------------------------------------------------------------------------------
/src/assets/img/ScanQrCode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/src/assets/img/ScanQrCode.png
--------------------------------------------------------------------------------
/src/assets/img/index.ts:
--------------------------------------------------------------------------------
1 | import Logo from './logo.png'
2 | import OpticalDisk from './OpticalDisk.png'
3 | import ScanQrCode from './ScanQrCode.png'
4 |
5 | export { Logo, OpticalDisk, ScanQrCode }
6 |
--------------------------------------------------------------------------------
/src/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luzhe0359/vue3-music/c8006f214554917ca48dd1352c35b9d1f2143b82/src/assets/img/logo.png
--------------------------------------------------------------------------------
/src/assets/theme.scss:
--------------------------------------------------------------------------------
1 | // .bg-main {
2 | // @apply bg-gray-50 transition-all;
3 | // @apply dark:bg-[#141414];
4 | // }
5 |
6 | // .hover-bg-main {
7 | // @apply hover:bg-gray-200;
8 | // @apply dark:hover:bg-[#262727];
9 | // }
10 |
11 | .bg-card {
12 | @apply bg-gray-100 transition-all;
13 | @apply dark:bg-[#222];
14 | @apply dark:hover:bg-neutral-800;
15 | }
16 |
17 | .hover-bg-card {
18 | @apply hover:bg-gray-200 transition-all duration-300;
19 | @apply dark:hover:bg-[#262727];
20 | }
21 |
22 | .text-main {
23 | @apply text-slate-700;
24 | @apply dark:text-slate-200;
25 | }
26 |
27 | .text-active {
28 | @apply text-emerald-400;
29 | }
30 |
31 | .text-title {
32 | @apply text-slate-400;
33 | @apply dark:text-[#999];
34 | }
35 |
36 | .text-desc {
37 | @apply text-slate-500;
38 | @apply dark:text-neutral-400;
39 | }
40 |
41 | .text-tag {
42 | @apply text-slate-400;
43 | @apply dark:text-stone-500;
44 | }
45 |
46 | .button {
47 | @apply flex bg-emerald-600 rounded-full justify-center items-center h-8 text-sm transition-all;
48 | @apply text-white;
49 | @apply hover:bg-emerald-700;
50 | }
51 |
52 | .button-outline {
53 | @apply flex rounded-full justify-center items-center h-8 text-sm transition-all border;
54 | @apply hover:border-emerald-500 hover:text-emerald-500;
55 | }
56 |
57 | .button-text {
58 | @apply flex bg-[#e6e6e6] rounded justify-center items-center h-8 text-sm transition-all cursor-pointer;
59 | @apply dark:bg-[#333] dark:text-white;
60 | @apply hover:text-emerald-500;
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/Banner.vue:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
64 |
--------------------------------------------------------------------------------
/src/components/CoverPlay.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{ useFormatDuring(duration / 1000 || 0) }}
27 |
28 |
29 |
30 | {{ useNumberFormat(playCount || 0) }}
31 |
32 |
33 |
34 |
35 |
66 |
--------------------------------------------------------------------------------
/src/components/IconPark.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
27 |
28 |
--------------------------------------------------------------------------------
/src/components/Loading.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
81 |
--------------------------------------------------------------------------------
/src/components/MoreText.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 | {{ text.substring(0, end) }}...
20 | {{ text }}...
21 | [{{ isFold ? '详情' : '收起' }}]
22 |
23 | {{ text }}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/components/MvList.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
39 |
{{ item.name }}
40 |
41 | {{ item.artistName }}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/components/PlayedList.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 | 当前播放
22 |
23 |
总{{ playListCount }}首
24 |
清空列表
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | {{ scope.row.name }}
35 | SQ
36 |
37 |
38 |
39 |
40 |
41 | {{ useFormatDuring(scope.row.dt / 1000) }}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/components/Playing.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{ index + 1 }}
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/components/SongList.vue:
--------------------------------------------------------------------------------
1 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {{ scope.row.name }}
32 | SQ
33 |
34 |
35 |
36 |
37 |
38 |
39 | {{ useFormatDuring(scope.row.dt / 1000) }}
40 |
41 |
42 |
43 |
44 |
45 |
51 |
--------------------------------------------------------------------------------
/src/components/Title.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
{{ title }}
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/layout/footer/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
24 |
25 |
--------------------------------------------------------------------------------
/src/layout/footer/PlayerAction.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
{{ useFormatDuring(currentTime) }} / {{ useFormatDuring(duration) }}
14 |
15 |
16 |
17 | {{ playListCount }}
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/layout/footer/PlayerController.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/layout/footer/PlayerSlider.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
51 |
--------------------------------------------------------------------------------
/src/layout/footer/PlayerSong.vue:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
![]()
14 |
15 |
16 |
17 |
试听
18 |
{{ song.name || '开源云音乐' }}
19 |
- {{ song.ar?.first().name || `SmallRuralDog` }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
44 |
--------------------------------------------------------------------------------
/src/layout/footer/PlayerVolumeSlider.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
24 |
25 |
{{ volume }}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
--------------------------------------------------------------------------------
/src/layout/header/Header.vue:
--------------------------------------------------------------------------------
1 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
69 |
--------------------------------------------------------------------------------
/src/layout/header/SearchPop.vue:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
44 |
45 |
46 |
热门搜索
47 |
48 |
54 |
55 | {{ index + 1 }}.
56 | {{ item.searchWord }}
57 |
58 |
{{ item.score.numberFormat() }}
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/layout/header/SearchSuggest.vue:
--------------------------------------------------------------------------------
1 |
33 |
34 |
35 |
36 |
37 |
{{ getTitle(order) }}
38 |
39 |
40 | {{ item.name }}
41 | - {{ item.artists.first()?.name }}
42 |
43 |
44 |
45 |
51 | {{ item.name }}
52 | - {{ item.artist.name }}
53 |
54 |
55 |
56 |
62 |
63 | {{ item.name }}
64 |
65 |
66 |
67 |
73 |
74 |
{{ item.name }}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/src/layout/header/UserInfo.vue:
--------------------------------------------------------------------------------
1 |
71 |
72 |
73 |
74 |
75 | {{ profile.nickname }}
76 | 点击登录
77 |
78 |
79 |
80 |
81 |
扫码登录
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
二维码已失效
90 |
点击刷新
91 |
92 |
93 |
94 |
使用 网易云音乐APP
扫码登录
95 |
96 |
97 |
选择其他登录模式 >
98 |
99 |
100 |
101 |
102 |
144 |
--------------------------------------------------------------------------------
/src/layout/menu/Menu.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/layout/menu/MenuList.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
22 |
23 |
24 |
25 |
26 |
39 |
--------------------------------------------------------------------------------
/src/layout/menu/useMenu.ts:
--------------------------------------------------------------------------------
1 | import { ref, watch } from 'vue'
2 | import { Planet, Music, VideoOne, Fm, Like, Computer, DownloadThree, PlayTwo } from '@icon-park/vue-next'
3 | import { useRoute, useRouter } from 'vue-router'
4 |
5 | interface IMenu {
6 | name: string
7 | key: string
8 | icon: any
9 | theme: 'outline' | 'filled' | 'two-tone' | 'multi-color'
10 | }
11 |
12 | interface IMenus {
13 | name: string
14 | menus: IMenu[]
15 | }
16 |
17 | export function userMenu() {
18 | const menus: IMenus[] = [
19 | {
20 | name: '在线音乐',
21 | menus: [
22 | {
23 | name: '推荐',
24 | key: 'discover',
25 | icon: Planet,
26 | theme: 'outline'
27 | },
28 | {
29 | name: '音乐馆',
30 | key: 'music',
31 | icon: Music,
32 | theme: 'outline'
33 | },
34 | {
35 | name: '视频',
36 | key: 'video',
37 | icon: VideoOne,
38 | theme: 'outline'
39 | },
40 | {
41 | name: '电台',
42 | key: 'dj',
43 | icon: Fm,
44 | theme: 'outline'
45 | }
46 | ]
47 | },
48 | {
49 | name: '我的音乐',
50 | menus: [
51 | {
52 | name: '我喜欢',
53 | key: 'love',
54 | icon: Like,
55 | theme: 'outline'
56 | },
57 | {
58 | name: '本地歌曲',
59 | key: 'local',
60 | icon: Computer,
61 | theme: 'outline'
62 | },
63 | {
64 | name: '下载歌曲',
65 | key: 'download',
66 | icon: DownloadThree,
67 | theme: 'outline'
68 | },
69 | {
70 | name: '最近播放',
71 | key: 'recently',
72 | icon: PlayTwo,
73 | theme: 'outline'
74 | }
75 | ]
76 | }
77 | ]
78 |
79 | const route = useRoute()
80 |
81 | const currentKey = ref(route.meta.menu)
82 |
83 | const router = useRouter()
84 |
85 | watch(
86 | () => route.meta.menu,
87 | (menu) => {
88 | currentKey.value = menu
89 | }
90 | )
91 |
92 | const click = async (menu: IMenu) => {
93 | await router.push({ name: menu.key, replace: true })
94 | }
95 |
96 | return {
97 | menus,
98 | click,
99 | currentKey
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 | import store from './stores'
4 | import router from './router'
5 |
6 | import '@/utils/extend'
7 | import '@/assets/base.scss'
8 | import '@/assets/theme.scss'
9 | import 'element-plus/theme-chalk/dark/css-vars.css'
10 |
11 | createApp(App).use(router).use(store).mount('#app')
12 |
--------------------------------------------------------------------------------
/src/models/album.ts:
--------------------------------------------------------------------------------
1 | import type { Artist } from '@/models/artist'
2 |
3 | export interface Album {
4 | songs: any[]
5 | paid: boolean
6 | onSale: boolean
7 | mark: number
8 | blurPicUrl: string
9 | companyId: number
10 | alias: string[]
11 | artists: Artist[]
12 | copyrightId: number
13 | picId: number
14 | artist: Artist
15 | publishTime: number
16 | company: string
17 | briefDesc: string
18 | picUrl: string
19 | commentThreadId: string
20 | pic: number
21 | tags: string
22 | description: string
23 | status: number
24 | subType: string
25 | name: string
26 | id: number
27 | type: string
28 | size: number
29 | picId_str: string
30 | }
31 |
--------------------------------------------------------------------------------
/src/models/artist.ts:
--------------------------------------------------------------------------------
1 | export interface Artist {
2 | albumSize: number
3 | alias: string[]
4 | briefDesc: string
5 | fansCount: number
6 | followed: boolean
7 | id: number
8 | img1v1Id: number
9 | img1v1Id_str: string
10 | img1v1Url: string
11 | musicSize: number
12 | name: string
13 | picId: number
14 | picId_str: string
15 | picUrl: string
16 | topicPerson: number
17 | trans: string
18 | }
19 |
20 | export interface Mv {
21 | id: number
22 | name: string
23 | status: number
24 | artistName: string
25 | artist: Artist
26 | imgurl16v9: string
27 | imgurl: string
28 | duration: number
29 | playCount: number
30 | publishTime: string
31 | subed: boolean
32 | }
33 |
34 | export interface Mvs {
35 | artistName: string
36 | duration: number
37 | id: number
38 | imgurl: string
39 | imgurl16v9: string
40 | name: string
41 | playCount: number
42 | publishTime: string
43 | status: number
44 | subed: boolean
45 | }
46 |
--------------------------------------------------------------------------------
/src/models/artist_detail.ts:
--------------------------------------------------------------------------------
1 | export interface ArtistDetail {
2 | videoCount: number
3 | identify: ArtistDetailIdentify
4 | artist: ArtistDetailArtist
5 | blacklist: boolean
6 | preferShow: number
7 | showPriMsg: boolean
8 | secondaryExpertIdentiy: ArtistDetailSecondaryExpertIdentiy[]
9 | }
10 | export interface ArtistDetailIdentify {
11 | imageUrl?: any
12 | imageDesc: string
13 | actionUrl: string
14 | }
15 | export interface ArtistDetailArtistRank {
16 | rank: number
17 | type: number
18 | }
19 | export interface ArtistDetailArtist {
20 | id: number
21 | cover: string
22 | name: string
23 | transNames: string[]
24 | identities: string[]
25 | identifyTag?: any
26 | briefDesc: string
27 | rank: ArtistDetailArtistRank
28 | albumSize: number
29 | musicSize: number
30 | mvSize: number
31 | }
32 | export interface ArtistDetailSecondaryExpertIdentiy {
33 | expertIdentiyId: number
34 | expertIdentiyName: string
35 | expertIdentiyCount: number
36 | }
37 |
38 | export interface ArtistDesc {
39 | introduction: ArtistDescIntroduction[]
40 | briefDesc: string
41 | count: number
42 | topicData: ArtistDescTopicData[]
43 | }
44 | export interface ArtistDescIntroduction {
45 | ti: string
46 | txt: string
47 | }
48 | export interface ArtistDescTopicDataTopicContent {
49 | type: number
50 | id: number
51 | content: string
52 | }
53 | export interface ArtistDescTopicDataTopic {
54 | id: number
55 | addTime: number
56 | mainTitle: string
57 | title: string
58 | content: ArtistDescTopicDataTopicContent[]
59 | userId: number
60 | cover: number
61 | headPic: number
62 | shareContent: string
63 | wxTitle: string
64 | showComment: boolean
65 | status: number
66 | seriesId: number
67 | pubTime: number
68 | readCount: number
69 | tags: string[]
70 | pubImmidiatly: boolean
71 | auditor: string
72 | auditTime: number
73 | auditStatus: number
74 | startText: string
75 | delReason: string
76 | showRelated: boolean
77 | fromBackend: boolean
78 | rectanglePic: number
79 | updateTime: number
80 | reward: boolean
81 | summary: string
82 | memo?: any
83 | adInfo: string
84 | categoryId: number
85 | hotScore: number
86 | recomdTitle: string
87 | recomdContent: string
88 | number: number
89 | }
90 | export interface ArtistDescTopicDataCreatorExperts {
91 | 1: string
92 | }
93 | export interface ArtistDescTopicDataCreator {
94 | userId: number
95 | userType: number
96 | nickname: string
97 | avatarImgId: number
98 | avatarUrl: string
99 | backgroundImgId: number
100 | backgroundUrl: string
101 | signature: string
102 | createTime: number
103 | userName: string
104 | accountType: number
105 | shortUserName: string
106 | birthday: number
107 | authority: number
108 | gender: number
109 | accountStatus: number
110 | province: number
111 | city: number
112 | authStatus: number
113 | description?: any
114 | detailDescription?: any
115 | defaultAvatar: boolean
116 | expertTags?: any
117 | experts: ArtistDescTopicDataCreatorExperts
118 | djStatus: number
119 | locationStatus: number
120 | vipType: number
121 | followed: boolean
122 | mutual: boolean
123 | authenticated: boolean
124 | lastLoginTime: number
125 | lastLoginIP: string
126 | remarkName?: any
127 | viptypeVersion: number
128 | authenticationTypes: number
129 | avatarDetail?: any
130 | anchor: boolean
131 | }
132 | export interface ArtistDescTopicData {
133 | topic: ArtistDescTopicDataTopic
134 | creator: ArtistDescTopicDataCreator
135 | shareCount: number
136 | commentCount: number
137 | likedCount: number
138 | liked: boolean
139 | rewardCount: number
140 | rewardMoney: number
141 | relatedResource?: any
142 | rectanglePicUrl: string
143 | coverUrl: string
144 | categoryId: number
145 | categoryName: string
146 | reward: boolean
147 | shareContent: string
148 | wxTitle: string
149 | addTime: number
150 | seriesId: number
151 | showComment: boolean
152 | showRelated: boolean
153 | memo?: any
154 | summary: string
155 | recmdTitle: string
156 | recmdContent: string
157 | commentThreadId: string
158 | mainTitle: string
159 | tags: string[]
160 | readCount: number
161 | url: string
162 | title: string
163 | id: number
164 | number: number
165 | }
166 |
--------------------------------------------------------------------------------
/src/models/banner.ts:
--------------------------------------------------------------------------------
1 | export interface Banner {
2 | pic: string
3 | targetId: number
4 | targetType: number
5 | typeTitle: string
6 | bannerId: number
7 | imageUrl: string
8 | }
9 |
--------------------------------------------------------------------------------
/src/models/dj.ts:
--------------------------------------------------------------------------------
1 | export interface DJBanner {
2 | targetId: number
3 | targetType: number
4 | pic: string
5 | url: string
6 | typeTitle: string
7 | exclusive: boolean
8 | }
9 |
10 | export interface DjPersonalize {
11 | id: number
12 | dj: DjPersonalizeDj
13 | name: string
14 | picUrl: string
15 | desc: string
16 | subCount: number
17 | programCount: number
18 | createTime: number
19 | categoryId: number
20 | category: string
21 | secondCategoryId: number
22 | secondCategory: string
23 | radioFeeType: number
24 | feeScope: number
25 | buyed: boolean
26 | videos: any
27 | finished: boolean
28 | underShelf: boolean
29 | purchaseCount: number
30 | price: number
31 | originalPrice: number
32 | discountPrice: any
33 | lastProgramCreateTime: number
34 | lastProgramName: string
35 | lastProgramId: number
36 | picId: number
37 | rcmdText: string
38 | hightQuality: boolean
39 | whiteList: boolean
40 | liveInfo: any
41 | playCount: number
42 | icon: any
43 | privacy: boolean
44 | intervenePicUrl: string
45 | intervenePicId: number
46 | dynamic: boolean
47 | shortName: any
48 | taskId: 0
49 | manualTagsDTO: any
50 | scoreInfoDTO: any
51 | descPicList: any
52 | subed: boolean
53 | composeVideo: boolean
54 | rcmdtext: string
55 | lastUpdateProgramName: string
56 | alg: string
57 | }
58 |
59 | export interface DjPersonalizeDj {
60 | defaultAvatar: boolean
61 | province: number
62 | authStatus: number
63 | followed: boolean
64 | avatarUrl: string
65 | accountStatus: number
66 | gender: number
67 | city: number
68 | birthday: number
69 | userId: number
70 | userType: number
71 | nickname: string
72 | signature: string
73 | description: string
74 | detailDescription: string
75 | avatarImgId: number
76 | backgroundImgId: number
77 | backgroundUrl: string
78 | authority: number
79 | mutual: boolean
80 | expertTags: any
81 | experts: any
82 | djStatus: number
83 | vipType: number
84 | remarkName: any
85 | authenticationTypes: number
86 | avatarDetail: any
87 | backgroundImgIdStr: string
88 | avatarImgIdStr: string
89 | anchor: boolean
90 | avatarImgId_str: string
91 | }
92 |
93 | export interface DjTodayPerfered {
94 | id: number
95 | name: string
96 | rcmdText: string
97 | radioFeeType: number
98 | feeScope: number
99 | picUrl: string
100 | programCount: number
101 | subCount: number
102 | subed: boolean
103 | playCount: number
104 | alg: string
105 | originalPrice: any
106 | discountPrice: any
107 | lastProgramName: string
108 | traceId: any
109 | icon: any
110 | }
111 |
112 | export interface DjTodayPerfered {
113 | id: number
114 | name: string
115 | rcmdText: string
116 | radioFeeType: number
117 | feeScope: number
118 | picUrl: string
119 | programCount: number
120 | subCount: number
121 | subed: boolean
122 | playCount: number
123 | alg: string
124 | originalPrice: any
125 | discountPrice: any
126 | lastProgramName: string
127 | traceId: any
128 | icon: any
129 | }
130 |
131 | export interface DjNewcomer {
132 | id: number
133 | rank: number
134 | lastRank: number
135 | score: number
136 | nickName: string
137 | avatarUrl: string
138 | userType: number
139 | userFollowedCount: number
140 | mainAuthDesc: string
141 | liveStatus: number
142 | liveType: number
143 | liveId: number
144 | avatarDetail: DjNewcomerAvatarDetail
145 | roomNo: number
146 | }
147 |
148 | export interface DjNewcomerAvatarDetail {
149 | userType: number
150 | identityLevel: number
151 | identityIconUrl: string
152 | }
153 |
154 | export interface DjPay {
155 | id: number
156 | rank: number
157 | lastRank: number
158 | score: number
159 | name: string
160 | picUrl: string
161 | creatorName: string
162 | }
163 |
--------------------------------------------------------------------------------
/src/models/mv.ts:
--------------------------------------------------------------------------------
1 | export interface MvUrl {
2 | id: number
3 | url: string
4 | r: number
5 | size: number
6 | md5: string
7 | code: number
8 | expi: number
9 | fee: number
10 | mvFee: number
11 | st: number
12 | promotionVo?: any
13 | msg: string
14 | }
15 |
16 | export interface MvDetail {
17 | id: number
18 | name: string
19 | artistId: number
20 | artistName: string
21 | briefDesc: string
22 | desc: string
23 | cover: string
24 | coverId_str: string
25 | coverId: number
26 | playCount: number
27 | subCount: number
28 | shareCount: number
29 | commentCount: number
30 | duration: number
31 | nType: number
32 | publishTime: string
33 | price: null
34 | brs: any[]
35 | artists: MvDetailArtists[]
36 | alias: any[]
37 | commentThreadId: string
38 | videoGroup: any[]
39 | }
40 |
41 | export interface MvDetailArtists {
42 | id: number
43 | name: string
44 | followed: boolean
45 | img1v1Url: string
46 | }
47 |
48 | export interface Mv {
49 | id: number
50 | cover: string
51 | name: string
52 | playCount: number
53 | briefDesc?: any
54 | desc?: any
55 | artistName: string
56 | artistId: number
57 | duration: number
58 | mark: number
59 | subed: boolean
60 | artists: MvArtists[]
61 | }
62 |
63 | export interface MvArtists {
64 | id: number
65 | name: string
66 | alias?: []
67 | transNames?: null
68 | }
69 |
70 | export interface SimiMv {
71 | id: number
72 | cover: string
73 | name: string
74 | playCount: number
75 | briefDesc: string
76 | desc: null
77 | artistName: string
78 | artistId: number
79 | duration: number
80 | mark: number
81 | artists: MvArtists[]
82 | alg: string
83 | }
84 |
--------------------------------------------------------------------------------
/src/models/personalized.ts:
--------------------------------------------------------------------------------
1 | export interface Personalized {
2 | id: number
3 | type: number
4 | name: string
5 | copywriter: string
6 | picUrl: string
7 | canDislike: boolean
8 | trackNumberUpdateTime: number
9 | playCount: number
10 | trackCount: number
11 | highQuality: boolean
12 | alg: string
13 | }
14 |
15 | export interface PersonalizedNewSong {
16 | id: number
17 | type: number
18 | name: string
19 | picUrl: string
20 | canDislike: boolean
21 | song: PNSSong
22 | alg: string
23 | }
24 |
25 | export interface PNSSongArtists {
26 | name: string
27 | id: number
28 | picId: number
29 | img1v1Id: number
30 | briefDesc: string
31 | picUrl: string
32 | img1v1Url: string
33 | albumSize: number
34 | alias: any[]
35 | trans: string
36 | musicSize: number
37 | topicPerson: number
38 | }
39 |
40 | export interface PNSSongAlbumArtist {
41 | name: string
42 | id: number
43 | picId: number
44 | img1v1Id: number
45 | briefDesc: string
46 | picUrl: string
47 | img1v1Url: string
48 | albumSize: number
49 | alias: any[]
50 | trans: string
51 | musicSize: number
52 | topicPerson: number
53 | }
54 |
55 | export interface PNSSongAlbumArtists {
56 | name: string
57 | id: number
58 | picId: number
59 | img1v1Id: number
60 | briefDesc: string
61 | picUrl: string
62 | img1v1Url: string
63 | albumSize: number
64 | alias: any[]
65 | trans: string
66 | musicSize: number
67 | topicPerson: number
68 | }
69 |
70 | export interface PNSSongAlbum {
71 | name: string
72 | id: number
73 | type: string
74 | size: number
75 | picId: number
76 | blurPicUrl: string
77 | companyId: number
78 | pic: number
79 | picUrl: string
80 | publishTime: number
81 | description: string
82 | tags: string
83 | company: string
84 | briefDesc: string
85 | artist: PNSSongAlbumArtist
86 | songs: any[]
87 | alias: any[]
88 | status: number
89 | copyrightId: number
90 | commentThreadId: string
91 | artists: PNSSongAlbumArtists[]
92 | subType: string
93 | onSale: boolean
94 | mark: number
95 | picId_str: string
96 | }
97 |
98 | export interface PNSSongBMusic {
99 | id: number
100 | size: number
101 | extension: string
102 | sr: number
103 | dfsId: number
104 | bitrate: number
105 | playTime: number
106 | volumeDelta: number
107 | }
108 |
109 | export interface PNSSongHMusic {
110 | id: number
111 | size: number
112 | extension: string
113 | sr: number
114 | dfsId: number
115 | bitrate: number
116 | playTime: number
117 | volumeDelta: number
118 | }
119 |
120 | export interface PNSSongMMusic {
121 | id: number
122 | size: number
123 | extension: string
124 | sr: number
125 | dfsId: number
126 | bitrate: number
127 | playTime: number
128 | volumeDelta: number
129 | }
130 |
131 | export interface PNSSongLMusic {
132 | id: number
133 | size: number
134 | extension: string
135 | sr: number
136 | dfsId: number
137 | bitrate: number
138 | playTime: number
139 | volumeDelta: number
140 | }
141 |
142 | export interface PNSSongPrivilegeFreeTrialPrivilege {
143 | resConsumable: boolean
144 | userConsumable: boolean
145 | }
146 |
147 | export interface PNSSongPrivilegeChargeInfoList {
148 | rate: number
149 | chargeType: number
150 | }
151 |
152 | export interface PNSSongPrivilege {
153 | id: number
154 | fee: number
155 | payed: number
156 | st: number
157 | pl: number
158 | dl: number
159 | sp: number
160 | cp: number
161 | subp: number
162 | cs: boolean
163 | maxbr: number
164 | fl: number
165 | toast: boolean
166 | flag: number
167 | preSell: boolean
168 | playMaxbr: number
169 | downloadMaxbr: number
170 | freeTrialPrivilege: PNSSongPrivilegeFreeTrialPrivilege
171 | chargeInfoList: PNSSongPrivilegeChargeInfoList[]
172 | }
173 |
174 | export interface PNSSong {
175 | name: string
176 | id: number
177 | position: number
178 | alias: any[]
179 | status: number
180 | fee: number
181 | copyrightId: number
182 | disc: string
183 | no: number
184 | artists: PNSSongArtists[]
185 | album: PNSSongAlbum
186 | starred: boolean
187 | popularity: number
188 | score: number
189 | starredNum: number
190 | duration: number
191 | playedNum: number
192 | dayPlays: number
193 | hearTime: number
194 | ringtone: string
195 | copyFrom: string
196 | commentThreadId: string
197 | ftype: number
198 | rtUrls: any[]
199 | copyright: number
200 | mark: number
201 | originCoverType: number
202 | single: number
203 | mvid: number
204 | bMusic: PNSSongBMusic
205 | rtype: number
206 | hMusic: PNSSongHMusic
207 | mMusic: PNSSongMMusic
208 | lMusic: PNSSongLMusic
209 | exclusive: boolean
210 | privilege: PNSSongPrivilege
211 | }
212 |
213 | export interface PersonalizedMv {
214 | id: number
215 | type: number
216 | name: string
217 | copywriter: string
218 | picUrl: string
219 | canDislike: boolean
220 | trackNumberUpdateTime?: any
221 | duration: number
222 | playCount: number
223 | subed: boolean
224 | artists: {
225 | id: number
226 | name: string
227 | }[]
228 | artistName: string
229 | artistId: number
230 | alg: string
231 | }
232 | export interface DjProgram {
233 | id: number
234 | type: number
235 | name: string
236 | copywriter: string
237 | picUrl: string
238 | canDislike: boolean
239 | trackNumberUpdateTime?: any
240 | }
241 |
--------------------------------------------------------------------------------
/src/models/playlist.ts:
--------------------------------------------------------------------------------
1 | export interface PlayListDetail {
2 | id: number
3 | name: string
4 | coverImgId: number
5 | coverImgUrl: string
6 | coverImgId_str: string
7 | adType: number
8 | userId: number
9 | createTime: number
10 | status: number
11 | opRecommend: boolean
12 | highQuality: boolean
13 | newImported: boolean
14 | updateTime: number
15 | trackCount: number
16 | specialType: number
17 | privacy: number
18 | trackUpdateTime: number
19 | commentThreadId: string
20 | playCount: number
21 | trackNumberUpdateTime: number
22 | subscribedCount: number
23 | cloudTrackCount: number
24 | ordered: boolean
25 | description: string
26 | tags: string[]
27 | updateFrequency?: any
28 | backgroundCoverId: number
29 | backgroundCoverUrl?: any
30 | titleImage: number
31 | titleImageUrl?: any
32 | englishTitle?: any
33 | officialPlaylistType?: any
34 | subscribers: PlayListDetailSubscribers[]
35 | subscribed: boolean
36 | creator: PlayListDetailCreator
37 | tracks: PlayListDetailTracks[]
38 | videoIds?: any
39 | videos?: any
40 | trackIds: PlayListDetailTrackIds[]
41 | shareCount: number
42 | commentCount: number
43 | remixVideo?: any
44 | sharedUsers?: any
45 | historySharedUsers?: any
46 | }
47 |
48 | export interface PlayListDetailSubscribers {
49 | defaultAvatar: boolean
50 | province: number
51 | authStatus: number
52 | followed: boolean
53 | avatarUrl: string
54 | accountStatus: number
55 | gender: number
56 | city: number
57 | birthday: number
58 | userId: number
59 | userType: number
60 | nickname: string
61 | signature: string
62 | description: string
63 | detailDescription: string
64 | avatarImgId: number
65 | backgroundImgId: number
66 | backgroundUrl: string
67 | authority: number
68 | mutual: boolean
69 | expertTags?: any
70 | experts?: any
71 | djStatus: number
72 | vipType: number
73 | remarkName?: any
74 | authenticationTypes: number
75 | avatarDetail?: any
76 | avatarImgIdStr: string
77 | backgroundImgIdStr: string
78 | anchor: boolean
79 | avatarImgId_str: string
80 | }
81 |
82 | export interface PlayListDetailCreatorAvatarDetail {
83 | userType: number
84 | identityLevel: number
85 | identityIconUrl: string
86 | }
87 |
88 | export interface PlayListDetailCreator {
89 | defaultAvatar: boolean
90 | province: number
91 | authStatus: number
92 | followed: boolean
93 | avatarUrl: string
94 | accountStatus: number
95 | gender: number
96 | city: number
97 | birthday: number
98 | userId: number
99 | userType: number
100 | nickname: string
101 | signature: string
102 | description: string
103 | detailDescription: string
104 | avatarImgId: number
105 | backgroundImgId: number
106 | backgroundUrl: string
107 | authority: number
108 | mutual: boolean
109 | expertTags?: any
110 | experts?: any
111 | djStatus: number
112 | vipType: number
113 | remarkName?: any
114 | authenticationTypes: number
115 | avatarDetail: PlayListDetailCreatorAvatarDetail
116 | avatarImgIdStr: string
117 | backgroundImgIdStr: string
118 | anchor: boolean
119 | avatarImgId_str: string
120 | }
121 |
122 | export interface PlayListDetailTracksAr {
123 | id: number
124 | name: string
125 | tns: any[]
126 | alias: any[]
127 | }
128 |
129 | export interface PlayListDetailTracksAl {
130 | id: number
131 | name: string
132 | picUrl: string
133 | tns: any[]
134 | pic_str: string
135 | pic: number
136 | }
137 |
138 | export interface PlayListDetailTracksH {
139 | br: number
140 | fid: number
141 | size: number
142 | vd: number
143 | }
144 |
145 | export interface PlayListDetailTracksM {
146 | br: number
147 | fid: number
148 | size: number
149 | vd: number
150 | }
151 |
152 | export interface PlayListDetailTracksL {
153 | br: number
154 | fid: number
155 | size: number
156 | vd: number
157 | }
158 |
159 | export interface PlayListDetailTracks {
160 | name: string
161 | id: number
162 | pst: number
163 | t: number
164 | ar: PlayListDetailTracksAr[]
165 | alia: any[]
166 | pop: number
167 | st: number
168 | rt: string
169 | fee: number
170 | v: number
171 | crbt?: any
172 | cf: string
173 | al: PlayListDetailTracksAl
174 | dt: number
175 | h: PlayListDetailTracksH
176 | m: PlayListDetailTracksM
177 | l: PlayListDetailTracksL
178 | a?: any
179 | cd: string
180 | no: number
181 | rtUrl?: any
182 | ftype: number
183 | rtUrls: any[]
184 | djId: number
185 | copyright: number
186 | s_id: number
187 | mark: number
188 | originCoverType: number
189 | originSongSimpleData?: any
190 | single: number
191 | noCopyrightRcmd?: any
192 | cp: number
193 | mv: number
194 | rtype: number
195 | rurl?: any
196 | mst: number
197 | publishTime: number
198 | }
199 |
200 | export interface PlayListDetailTrackIds {
201 | id: number
202 | v: number
203 | t: number
204 | at: number
205 | alg?: any
206 | uid: number
207 | rcmdReason: string
208 | sc?: any
209 | }
210 |
211 | export interface PlaylistHighqualityTag {
212 | id: number
213 | name: string
214 | type: number
215 | category: number
216 | hot: boolean
217 | }
218 |
--------------------------------------------------------------------------------
/src/models/playlist_cat.ts:
--------------------------------------------------------------------------------
1 | export interface PlayListCat {
2 | name: string
3 | resourceCount: number
4 | imgId: number
5 | imgUrl?: any
6 | type: number
7 | category: number
8 | resourceType: number
9 | hot: boolean
10 | activity: boolean
11 | }
12 |
--------------------------------------------------------------------------------
/src/models/playlist_hot.ts:
--------------------------------------------------------------------------------
1 | export interface PlayListHot {
2 | playlistTag: PlayListHotPlaylistTag
3 | activity: boolean
4 | position: number
5 | category: number
6 | hot: boolean
7 | createTime: number
8 | usedCount: number
9 | name: string
10 | id: number
11 | type: number
12 | }
13 |
14 | export interface PlayListHotPlaylistTag {
15 | id: number
16 | name: string
17 | category: number
18 | usedCount: number
19 | type: number
20 | position: number
21 | createTime: number
22 | highQuality: number
23 | highQualityPos: number
24 | officialPos: number
25 | }
26 |
--------------------------------------------------------------------------------
/src/models/search.ts:
--------------------------------------------------------------------------------
1 | export interface SearchHotDetail {
2 | searchWord: string
3 | score: number
4 | content: string
5 | source: number
6 | iconType: number
7 | iconUrl?: string
8 | url: string
9 | alg: string
10 | }
11 |
12 | export interface SearchSuggest {
13 | albums: SearchSuggestAlbums[]
14 | artists: SearchSuggestArtists[]
15 | songs: SearchSuggestSongs[]
16 | playlists: SearchSuggestPlaylists[]
17 | order: string[]
18 | }
19 | export interface SearchSuggestAlbumsArtist {
20 | id: number
21 | name: string
22 | picUrl: string
23 | alias: string[]
24 | albumSize: number
25 | picId: number
26 | img1v1Url: string
27 | img1v1: number
28 | alia: string[]
29 | trans?: any
30 | }
31 | export interface SearchSuggestAlbums {
32 | id: number
33 | name: string
34 | artist: SearchSuggestAlbumsArtist
35 | publishTime: number
36 | size: number
37 | copyrightId: number
38 | status: number
39 | picId: number
40 | mark: number
41 | }
42 | export interface SearchSuggestArtists {
43 | id: number
44 | name: string
45 | picUrl: string
46 | alias: string[]
47 | albumSize: number
48 | picId: number
49 | img1v1Url: string
50 | img1v1: number
51 | transNames: string[]
52 | alia: string[]
53 | trans: string
54 | }
55 | export interface SearchSuggestSongsArtists {
56 | id: number
57 | name: string
58 | picUrl?: any
59 | alias: any[]
60 | albumSize: number
61 | picId: number
62 | img1v1Url: string
63 | img1v1: number
64 | trans?: any
65 | }
66 | export interface SearchSuggestSongsAlbumArtist {
67 | id: number
68 | name: string
69 | picUrl?: any
70 | alias: any[]
71 | albumSize: number
72 | picId: number
73 | img1v1Url: string
74 | img1v1: number
75 | trans?: any
76 | }
77 | export interface SearchSuggestSongsAlbum {
78 | id: number
79 | name: string
80 | artist: SearchSuggestSongsAlbumArtist
81 | publishTime: number
82 | size: number
83 | copyrightId: number
84 | status: number
85 | picId: number
86 | mark: number
87 | }
88 | export interface SearchSuggestSongs {
89 | id: number
90 | name: string
91 | artists: SearchSuggestSongsArtists[]
92 | album: SearchSuggestSongsAlbum
93 | duration: number
94 | copyrightId: number
95 | status: number
96 | alias: any[]
97 | rtype: number
98 | ftype: number
99 | mvid: number
100 | fee: number
101 | rUrl?: any
102 | mark: number
103 | }
104 | export interface SearchSuggestPlaylists {
105 | id: number
106 | name: string
107 | coverImgUrl: string
108 | creator?: any
109 | subscribed: boolean
110 | trackCount: number
111 | userId: number
112 | playCount: number
113 | bookCount: number
114 | specialType: number
115 | officialTags?: any
116 | action?: any
117 | actionType?: any
118 | description?: any
119 | highQuality: boolean
120 | }
121 |
--------------------------------------------------------------------------------
/src/models/song.ts:
--------------------------------------------------------------------------------
1 | export interface Song {
2 | name: string
3 | id: number
4 | pst: number
5 | t: number
6 | ar: SongAr[]
7 | alia: string[]
8 | pop: number
9 | st: number
10 | rt?: any
11 | fee: number
12 | v: number
13 | crbt?: any
14 | cf: string
15 | al: SongAl
16 | dt: number
17 | h: SongH
18 | m: SongM
19 | l: SongL
20 | a?: any
21 | cd: string
22 | no: number
23 | rtUrl?: any
24 | ftype: number
25 | rtUrls: any[]
26 | djId: number
27 | copyright: number
28 | s_id: number
29 | mark: number
30 | originCoverType: number
31 | originSongSimpleData?: any
32 | tagPicList?: any
33 | resourceState: boolean
34 | version: number
35 | songJumpInfo?: any
36 | entertainmentTags?: any
37 | single: number
38 | noCopyrightRcmd?: any
39 | rtype: number
40 | rurl?: any
41 | mst: number
42 | cp: number
43 | mv: number
44 | publishTime: number
45 | }
46 |
47 | export interface SongAr {
48 | id: number
49 | name: string
50 | tns: any[]
51 | alias: any[]
52 | }
53 |
54 | export interface SongAl {
55 | id: number
56 | name: string
57 | picUrl: string
58 | tns: any[]
59 | pic_str: string
60 | pic: number
61 | }
62 |
63 | export interface SongH {
64 | br: number
65 | fid: number
66 | size: number
67 | vd: number
68 | }
69 |
70 | export interface SongM {
71 | br: number
72 | fid: number
73 | size: number
74 | vd: number
75 | }
76 |
77 | export interface SongL {
78 | br: number
79 | fid: number
80 | size: number
81 | vd: number
82 | }
83 |
--------------------------------------------------------------------------------
/src/models/song_url.ts:
--------------------------------------------------------------------------------
1 | export interface SongUrl {
2 | id: number
3 | url: string
4 | br: number
5 | size: number
6 | md5: string
7 | code: number
8 | expi: number
9 | type: string
10 | gain: number
11 | fee: number
12 | payed: number
13 | flag: number
14 | canExtend: boolean
15 | freeTrialPrivilege: RootObjectFreeTrialPrivilege
16 | freeTimeTrialPrivilege: RootObjectFreeTimeTrialPrivilege
17 | urlSource: number
18 | freeTrialInfo: {
19 | start: number
20 | end: number
21 | }
22 | }
23 |
24 | export interface RootObjectFreeTrialPrivilege {
25 | resConsumable: boolean
26 | userConsumable: boolean
27 | }
28 |
29 | export interface RootObjectFreeTimeTrialPrivilege {
30 | resConsumable: boolean
31 | userConsumable: boolean
32 | type: number
33 | remainTime: number
34 | }
35 |
--------------------------------------------------------------------------------
/src/models/top_song.ts:
--------------------------------------------------------------------------------
1 | export interface TopSong {
2 | starred: boolean
3 | popularity: number
4 | starredNum: number
5 | playedNum: number
6 | dayPlays: number
7 | hearTime: number
8 | mp3Url: string
9 | rtUrls?: any
10 | audition: any
11 | ringtone: string
12 | disc: string
13 | no: number
14 | status: number
15 | crbt: any
16 | bMusic: any
17 | rtUrl: any
18 | alias: any[]
19 | artists: any[]
20 | copyFrom: string
21 | mMusic: any
22 | lMusic: any
23 | hMusic: any
24 | score: number
25 | copyrightId: number
26 | album: any
27 | commentThreadId: string
28 | fee: number
29 | mvid: number
30 | ftype: number
31 | rtype: number
32 | rurl: null
33 | position: number
34 | duration: number
35 | name: string
36 | id: number
37 | exclusive: boolean
38 | privilege: any
39 | }
40 |
--------------------------------------------------------------------------------
/src/models/toplist_detail.ts:
--------------------------------------------------------------------------------
1 | export interface TopListDetail {
2 | subscribers: any[]
3 | subscribed?: any
4 | creator?: any
5 | artists?: any
6 | tracks: TopListDetailTracks[]
7 | updateFrequency: string
8 | backgroundCoverId: number
9 | backgroundCoverUrl?: any
10 | titleImage: number
11 | titleImageUrl?: any
12 | englishTitle?: any
13 | opRecommend: boolean
14 | recommendInfo?: any
15 | subscribedCount: number
16 | cloudTrackCount: number
17 | userId: number
18 | highQuality: boolean
19 | createTime: number
20 | specialType: number
21 | coverImgId: number
22 | newImported: boolean
23 | anonimous: boolean
24 | updateTime: number
25 | trackCount: number
26 | coverImgUrl: string
27 | commentThreadId: string
28 | trackUpdateTime: number
29 | totalDuration: number
30 | privacy: number
31 | playCount: number
32 | trackNumberUpdateTime: number
33 | adType: number
34 | description: string
35 | ordered: boolean
36 | tags: any[]
37 | status: number
38 | name: string
39 | id: number
40 | coverImgId_str: string
41 | ToplistType: string
42 | }
43 | export interface TopListDetailTracks {
44 | first: string
45 | second: string
46 | }
47 |
--------------------------------------------------------------------------------
/src/models/user.ts:
--------------------------------------------------------------------------------
1 | export interface UserInfo {
2 | userId: number
3 | userName: string
4 | }
5 |
6 | export interface UserProfile {
7 | userId: number
8 | userType: number
9 | nickname: string
10 | avatarImgId: number
11 | avatarUrl: string
12 | backgroundImgId: number
13 | backgroundUrl: string
14 | signature?: any
15 | createTime: number
16 | userName: string
17 | accountType: number
18 | shortUserName: string
19 | birthday: number
20 | authority: number
21 | gender: number
22 | accountStatus: number
23 | province: number
24 | city: number
25 | authStatus: number
26 | description?: any
27 | detailDescription?: any
28 | defaultAvatar: boolean
29 | expertTags?: any
30 | experts?: any
31 | djStatus: number
32 | locationStatus: number
33 | vipType: number
34 | followed: boolean
35 | mutual: boolean
36 | authenticated: boolean
37 | lastLoginTime: number
38 | lastLoginIP: string
39 | remarkName?: any
40 | viptypeVersion: number
41 | authenticationTypes: number
42 | avatarDetail?: any
43 | anchor: boolean
44 | }
45 |
--------------------------------------------------------------------------------
/src/models/video.ts:
--------------------------------------------------------------------------------
1 | export interface Video {
2 | type: number
3 | displayed: boolean
4 | alg: string
5 | extAlg?: any
6 | data: VideoData
7 | }
8 | export interface VideoDataResolutions {
9 | resolution: number
10 | size: number
11 | }
12 | export interface VideoDataCreator {
13 | defaultAvatar: boolean
14 | province: number
15 | authStatus: number
16 | followed: boolean
17 | avatarUrl: string
18 | accountStatus: number
19 | gender: number
20 | city: number
21 | birthday: number
22 | userId: number
23 | userType: number
24 | nickname: string
25 | signature: string
26 | description: string
27 | detailDescription: string
28 | avatarImgId: number
29 | backgroundImgId: number
30 | backgroundUrl: string
31 | authority: number
32 | mutual: boolean
33 | expertTags?: any
34 | experts?: any
35 | djStatus: number
36 | vipType: number
37 | remarkName?: any
38 | avatarImgIdStr: string
39 | backgroundImgIdStr: string
40 | }
41 | export interface VideoDataUrlInfo {
42 | id: string
43 | url: string
44 | size: number
45 | validityTime: number
46 | needPay: boolean
47 | payInfo?: any
48 | r: number
49 | }
50 | export interface VideoDataVideoGroup {
51 | id: number
52 | name: string
53 | alg?: any
54 | }
55 | export interface VideoDataRelateSongAr {
56 | id: number
57 | name: string
58 | tns: any[]
59 | alias: any[]
60 | }
61 | export interface VideoDataRelateSongAl {
62 | id: number
63 | name: string
64 | picUrl: string
65 | tns: any[]
66 | pic: number
67 | }
68 | export interface VideoDataRelateSongH {
69 | br: number
70 | fid: number
71 | size: number
72 | vd: number
73 | }
74 | export interface VideoDataRelateSongM {
75 | br: number
76 | fid: number
77 | size: number
78 | vd: number
79 | }
80 | export interface VideoDataRelateSongL {
81 | br: number
82 | fid: number
83 | size: number
84 | vd: number
85 | }
86 | export interface VideoDataRelateSongPrivilege {
87 | id: number
88 | fee: number
89 | payed: number
90 | st: number
91 | pl: number
92 | dl: number
93 | sp: number
94 | cp: number
95 | subp: number
96 | cs: boolean
97 | maxbr: number
98 | fl: number
99 | toast: boolean
100 | flag: number
101 | preSell: boolean
102 | }
103 | export interface VideoDataRelateSong {
104 | name: string
105 | id: number
106 | pst: number
107 | t: number
108 | ar: VideoDataRelateSongAr[]
109 | alia: any[]
110 | pop: number
111 | st: number
112 | rt: string
113 | fee: number
114 | v: number
115 | crbt?: any
116 | cf: string
117 | al: VideoDataRelateSongAl
118 | dt: number
119 | h: VideoDataRelateSongH
120 | m: VideoDataRelateSongM
121 | l: VideoDataRelateSongL
122 | a?: any
123 | cd: string
124 | no: number
125 | rtUrl?: any
126 | ftype: number
127 | rtUrls: any[]
128 | djId: number
129 | copyright: number
130 | s_id: number
131 | rtype: number
132 | rurl?: any
133 | mst: number
134 | cp: number
135 | mv: number
136 | publishTime: number
137 | privilege: VideoDataRelateSongPrivilege
138 | }
139 | export interface VideoData {
140 | alg: string
141 | scm: string
142 | threadId: string
143 | coverUrl: string
144 | height: number
145 | width: number
146 | title: string
147 | description?: any
148 | commentCount: number
149 | shareCount: number
150 | resolutions: VideoDataResolutions[]
151 | creator: VideoDataCreator
152 | urlInfo: VideoDataUrlInfo
153 | videoGroup: VideoDataVideoGroup[]
154 | previewUrl?: any
155 | previewDurationms: number
156 | hasRelatedGameAd: boolean
157 | markTypes: number[]
158 | relateSong: VideoDataRelateSong[]
159 | relatedInfo?: any
160 | videoUserLiveInfo?: any
161 | vid: string
162 | durationms: number
163 | playTime: number
164 | praisedCount: number
165 | praised: boolean
166 | subscribed: boolean
167 | liveData?: any
168 | }
169 |
170 | export interface PersonalizedPrivateContent {
171 | id: number
172 | url: string
173 | picUrl: string
174 | sPicUrl: string
175 | type: number
176 | copywriter: string
177 | name: string
178 | time: number
179 | }
180 |
181 | export interface VideoGroup {
182 | id: number
183 | name: string
184 | url?: any
185 | relatedVideoType?: any
186 | selectTab: boolean
187 | abExtInfo?: any
188 | }
189 |
--------------------------------------------------------------------------------
/src/models/video_detail.ts:
--------------------------------------------------------------------------------
1 | export interface VideoDetail {
2 | vid: string
3 | creator: VideoDetailCreator
4 | coverUrl: string
5 | title: string
6 | description: string
7 | durationms: number
8 | threadId: string
9 | playTime: number
10 | praisedCount: number
11 | commentCount: number
12 | shareCount: number
13 | subscribeCount: number
14 | publishTime: number
15 | avatarUrl: string
16 | width: number
17 | height: number
18 | resolutions: VideoDetailResolutions[]
19 | videoGroup: VideoDetailVideoGroup[]
20 | hasRelatedGameAd: boolean
21 | advertisement: boolean
22 | authType: number
23 | markTypes: number[]
24 | videoUserLiveInfo: null
25 | }
26 |
27 | export interface VideoUrl {
28 | id: string
29 | url: string
30 | size: number
31 | validityTime: number
32 | needPay: boolean
33 | payInfo: null
34 | r: number
35 | }
36 |
37 | export interface SimiVideo {
38 | alg: string
39 | type: number
40 | title: string
41 | durationms: number
42 | creator: SimiVideoCreator[]
43 | playTime: number
44 | coverUrl: string
45 | vid: string
46 | aliaName: null
47 | transName: null
48 | markTypes: []
49 | liveRoom: null
50 | }
51 |
52 | export interface SimiVideoCreator {
53 | userId: number
54 | userName: string
55 | }
56 |
57 | export interface VideoDetailCreator {
58 | authStatus: number
59 | followed: boolean
60 | accountStatus: number
61 | userId: number
62 | userType: number
63 | nickname: string
64 | avatarUrl: string
65 | expertTags: null
66 | experts?: any
67 | avatarDetail?: any
68 | }
69 |
70 | export interface VideoDetailResolutions {
71 | size: number
72 | resolution: number
73 | }
74 |
75 | export interface VideoDetailVideoGroup {
76 | id: number
77 | name: string
78 | alg: null
79 | }
80 |
--------------------------------------------------------------------------------
/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
2 |
3 | const routes: Array = [
4 | {
5 | path: '/',
6 | name: 'Home',
7 | component: () => import('@/views/Index.vue'),
8 | redirect: { name: 'discover' },
9 | children: [
10 | {
11 | path: 'discover',
12 | name: 'discover',
13 | component: () => import('@/views/discover/Index.vue'),
14 | meta: {
15 | menu: 'discover',
16 | keepAlive: true
17 | }
18 | },
19 | {
20 | path: 'music',
21 | name: 'music',
22 | component: () => import('@/views/music/Index.vue'),
23 | redirect: { name: 'picked' },
24 | meta: {
25 | menu: 'music'
26 | },
27 | children: [
28 | {
29 | path: 'picked',
30 | name: 'picked',
31 | component: () => import('@/views/music/picked/Picked.vue'),
32 | meta: {
33 | menu: 'music',
34 | title: '精选',
35 | keepAlive: true
36 | }
37 | },
38 | {
39 | path: 'toplist',
40 | name: 'toplist',
41 | component: () => import('@/views/music/topList/TopList.vue'),
42 | meta: {
43 | menu: 'music',
44 | title: '排行榜',
45 | keepAlive: true
46 | }
47 | },
48 | {
49 | path: 'artist',
50 | name: 'artist',
51 | component: () => import('@/views/music/artist/Artist.vue'),
52 | meta: {
53 | menu: 'music',
54 | title: '歌手',
55 | keepAlive: true
56 | }
57 | },
58 | {
59 | path: 'category',
60 | name: 'category',
61 | component: () => import('@/views/music/category/Category.vue'),
62 | meta: {
63 | menu: 'music',
64 | title: '歌单',
65 | keepAlive: true
66 | }
67 | },
68 | {
69 | path: 'topsong',
70 | name: 'topsong',
71 | component: () => import('@/views/music/topSong/TopSong.vue'),
72 | meta: {
73 | menu: 'music',
74 | title: '新歌速递',
75 | keepAlive: true
76 | }
77 | }
78 | ]
79 | },
80 | {
81 | path: 'video',
82 | name: 'video',
83 | component: () => import('@/views/video/Index.vue'),
84 | redirect: { name: 'videoList' },
85 | meta: {
86 | menu: 'video',
87 | title: '视频',
88 | keepAlive: true
89 | },
90 | children: [
91 | {
92 | path: 'list',
93 | name: 'videoList',
94 | component: () => import('@/views/video/videoList/Index.vue'),
95 | meta: {
96 | menu: 'video',
97 | title: '视频',
98 | keepAlive: true
99 | }
100 | },
101 | {
102 | path: 'mv',
103 | name: 'mvList',
104 | component: () => import('@/views/mv/mvList/Index.vue'),
105 | meta: {
106 | menu: 'video',
107 | title: 'mv',
108 | keepAlive: true
109 | }
110 | }
111 | ]
112 | },
113 | {
114 | path: 'dj',
115 | name: 'dj',
116 | component: () => import('@/views/dj/Index.vue'),
117 | meta: {
118 | menu: 'dj',
119 | title: '电台',
120 | keepAlive: true
121 | }
122 | },
123 | {
124 | path: 'playlist',
125 | name: 'playlist',
126 | component: () => import('@/views/playlist/Index.vue')
127 | },
128 | {
129 | path: 'artistDetail',
130 | name: 'artistDetail',
131 | component: () => import('@/views/artist/ArtistDetail.vue')
132 | },
133 | {
134 | path: 'album',
135 | name: 'album',
136 | component: () => import('@/views/album/Index.vue')
137 | },
138 | {
139 | path: 'mvDetail/:id?',
140 | name: 'mvDetail',
141 | component: () => import('@/views/mv/MvDetail/index.vue')
142 | },
143 | {
144 | path: 'videoDetail/:id?',
145 | name: 'videoDetail',
146 | component: () => import('@/views/video/videoDetail/index.vue')
147 | }
148 | ]
149 | }
150 | ]
151 |
152 | const router = createRouter({
153 | history: createWebHistory(),
154 | routes
155 | })
156 | export default router
157 |
--------------------------------------------------------------------------------
/src/stores/common.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 | import { ref } from 'vue'
3 | import { useBanner } from '@/utils/api'
4 | import type { Banner } from '@/models/banner'
5 |
6 | export const useCommonStore = defineStore('common', () => {
7 | const banners = ref([])
8 |
9 | const getBanners = async () => {
10 | if (banners.value.length) return
11 | banners.value = await useBanner()
12 | }
13 |
14 | return {
15 | banners,
16 | getBanners
17 | }
18 | })
19 |
--------------------------------------------------------------------------------
/src/stores/index.ts:
--------------------------------------------------------------------------------
1 | import { createPinia } from 'pinia'
2 |
3 | const store = createPinia()
4 |
5 | export default store
6 |
--------------------------------------------------------------------------------
/src/stores/music.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 | import { ref } from 'vue'
3 | import { usePersonalized, usePersonalizedNewSong, useTopListDetail } from '@/utils/api'
4 | import type { Personalized, PersonalizedNewSong } from '@/models/personalized'
5 | import type { TopListDetail } from '@/models/toplist_detail'
6 |
7 | export const useMusicStore = defineStore('music', () => {
8 | const topListDetailData = ref([])
9 | const getTopListDetailData = async () => {
10 | if (topListDetailData.value.length) return
11 | topListDetailData.value = await useTopListDetail()
12 | }
13 |
14 | const personalized = ref([])
15 | const getPersonalized = async () => {
16 | if (personalized.value.length) return
17 | personalized.value = await usePersonalized()
18 | }
19 |
20 | const personalizedNewSong = ref([])
21 | const getPersonalizedNewSong = async () => {
22 | if (personalizedNewSong.value.length) return
23 | personalizedNewSong.value = await usePersonalizedNewSong()
24 | }
25 |
26 | return {
27 | topListDetailData,
28 | getTopListDetailData,
29 |
30 | personalized,
31 | getPersonalized,
32 |
33 | personalizedNewSong,
34 | getPersonalizedNewSong
35 | }
36 | })
37 |
--------------------------------------------------------------------------------
/src/stores/personalized.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 | import { ref } from 'vue'
3 | import { usePersonalizedDjProgram } from '@/utils/api'
4 | import type { DjProgram } from '@/models/personalized'
5 |
6 | export const usePersonalizedStore = defineStore('personalized', () => {
7 | const djProgram = ref([])
8 | const getDjProgram = async () => {
9 | if (djProgram.value.length) return
10 | djProgram.value = await usePersonalizedDjProgram()
11 | }
12 |
13 | return {
14 | djProgram,
15 | getDjProgram
16 | }
17 | })
18 |
--------------------------------------------------------------------------------
/src/stores/player.ts:
--------------------------------------------------------------------------------
1 | import { defineStore, storeToRefs } from 'pinia'
2 | import { useDetail, useSongUrl } from '@/utils/api'
3 | import { onMounted, onUnmounted, toRefs, watch } from 'vue'
4 | import type { Song } from '@/models/song'
5 | import type { SongUrl } from '@/models/song_url'
6 |
7 | const KEYS = {
8 | volume: 'PLAYER-VOLUME'
9 | }
10 |
11 | export const usePlayerStore = defineStore({
12 | id: 'player',
13 | state: () => ({
14 | audio: new Audio(),
15 | loopType: 0, //循环模式 0-单曲循环 1-列表循环 2-随机播放
16 | volume: localStorage.getItem(KEYS.volume)?.toInt() || 60, //音量
17 | playList: [] as Song[], //播放列表
18 | showPlayList: false,
19 | id: 0,
20 | url: '',
21 | songUrl: {} as SongUrl,
22 | song: {} as Song,
23 | isPlaying: false, //是否播放中
24 | isPause: true, //是否暂停
25 | sliderInput: false, //是否正在拖动进度条
26 | ended: false, //是否播放结束
27 | muted: false, //是否静音
28 | currentTime: 0, //当前播放时间
29 | duration: 0 //总播放时长
30 | }),
31 | getters: {
32 | playListCount: (state) => {
33 | return state.playList.length
34 | },
35 | thisIndex: (state) => {
36 | return state.playList.findIndex((song) => song.id === state.id)
37 | },
38 | nextSong(state): Song {
39 | const { thisIndex, playListCount } = this
40 | if (thisIndex === playListCount - 1) {
41 | return state.playList.first()
42 | } else {
43 | const nextIndex: number = thisIndex + 1
44 | return state.playList[nextIndex]
45 | }
46 | },
47 | prevSong(state): Song {
48 | const { thisIndex } = this
49 | if (thisIndex === 0) {
50 | return state.playList.last()
51 | } else {
52 | const prevIndex: number = thisIndex - 1
53 | return state.playList[prevIndex]
54 | }
55 | }
56 | },
57 | actions: {
58 | init() {
59 | this.audio.volume = this.volume / 100
60 | },
61 | //播放列表里面添加音乐
62 | pushPlayList(replace: boolean, ...list: Song[]) {
63 | console.log('列表')
64 |
65 | if (replace) {
66 | this.playList = list
67 | return
68 | }
69 | list.forEach((song) => {
70 | if (this.playList.filter((s) => s.id == song.id).length <= 0) {
71 | this.playList.push(song)
72 | }
73 | })
74 | },
75 | clearPlayList() {
76 | this.songUrl = {} as SongUrl
77 | this.url = ''
78 | this.id = 0
79 | this.song = {} as Song
80 | this.isPlaying = false
81 | this.isPause = false
82 | this.sliderInput = false
83 | this.ended = false
84 | this.muted = false
85 | this.currentTime = 0
86 | this.playList = [] as Song[]
87 | this.showPlayList = false
88 | this.audio.load()
89 | setTimeout(() => {
90 | this.duration = 0
91 | }, 500)
92 | },
93 | async play(id: number) {
94 | console.log(id)
95 |
96 | if (id == this.id) return
97 | this.isPlaying = false
98 | const data = await useSongUrl(id)
99 | this.audio.src = data.url
100 | this.audio
101 | .play()
102 | .then((res) => {
103 | this.isPlaying = true
104 | this.isPause = false
105 | this.songUrl = data
106 | this.url = data.url
107 | this.id = id
108 | this.songDetail()
109 | })
110 | .catch((res) => {
111 | console.log(res)
112 | })
113 | },
114 | //播放结束
115 | playEnd() {
116 | console.log('播放结束')
117 | switch (this.loopType) {
118 | case 0:
119 | this.rePlay()
120 | break
121 | case 1:
122 | this.next()
123 | break
124 | case 2:
125 | this.randomPlay()
126 | break
127 | }
128 | },
129 | async songDetail() {
130 | this.song = await useDetail(this.id)
131 |
132 | this.pushPlayList(false, this.song)
133 | },
134 | //重新播放
135 | rePlay() {
136 | setTimeout(() => {
137 | this.currentTime = 0
138 | this.audio.play()
139 | }, 1500)
140 | },
141 | //下一曲
142 | next() {
143 | if (this.loopType === 2) {
144 | this.randomPlay()
145 | } else {
146 | this.play(this.nextSong.id)
147 | }
148 | },
149 | //上一曲
150 | prev() {
151 | this.play(this.prevSong.id)
152 | },
153 | //随机播放
154 | randomPlay() {
155 | this.play(this.playList.sample().id)
156 | },
157 | //播放、暂停
158 | togglePlay() {
159 | if (!this.song.id) return
160 | this.isPlaying = !this.isPlaying
161 | if (!this.isPlaying) {
162 | this.audio.pause()
163 | this.isPause = true
164 | } else {
165 | this.audio.play()
166 | this.isPause = false
167 | }
168 | },
169 | setPlay() {
170 | if (!this.song.id) return
171 | this.isPlaying = true
172 | this.audio.play()
173 | this.isPause = false
174 | },
175 | setPause() {
176 | if (!this.song.id) return
177 | this.isPlaying = false
178 | this.audio.pause()
179 | this.isPause = true
180 | },
181 | //切换循环类型
182 | toggleLoop() {
183 | if (this.loopType == 2) {
184 | this.loopType = 0
185 | } else {
186 | this.loopType++
187 | }
188 | },
189 | //静音切换
190 | toggleMuted() {
191 | this.muted = !this.muted
192 | this.audio.muted = this.muted
193 | },
194 | //音量设置
195 | setVolume(n: number) {
196 | n = n > 100 ? 100 : n
197 | n = n < 0 ? 0 : n
198 | this.volume = n
199 | this.audio.volume = n / 100
200 | localStorage.setItem('PLAYER-VOLUME', n.toString())
201 | },
202 | //修改播放时间
203 | onSliderChange(val: number) {
204 | this.currentTime = val
205 | this.sliderInput = false
206 | this.audio.currentTime = val
207 | },
208 | //播放时间拖动中
209 | onSliderInput(val: number) {
210 | this.sliderInput = true
211 | },
212 | //定时器
213 | interval() {
214 | if (this.isPlaying && !this.sliderInput) {
215 | this.currentTime = parseInt(this.audio.currentTime.toString())
216 | this.duration = parseInt(this.audio.duration.toString())
217 | this.ended = this.audio.ended
218 | }
219 | }
220 | }
221 | })
222 |
223 | export const userPlayerInit = () => {
224 | let timer: NodeJS.Timer
225 | const { init, interval, playEnd } = usePlayerStore()
226 |
227 | const { ended } = storeToRefs(usePlayerStore())
228 |
229 | //监听播放结束
230 | watch(ended, (ended) => {
231 | if (!ended) return
232 | playEnd()
233 | })
234 |
235 | //启动定时器
236 | onMounted(() => {
237 | init()
238 | console.log('启动定时器')
239 | timer = setInterval(interval, 1000)
240 | })
241 | //清除定时器
242 | onUnmounted(() => {
243 | console.log('清除定时器')
244 | clearInterval(timer)
245 | })
246 | }
247 |
--------------------------------------------------------------------------------
/src/stores/search.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 | import { useSearchSuggest } from '@/utils/api'
3 | import type { SearchSuggest } from '@/models/search'
4 |
5 | export const useSearchStore = defineStore('search', {
6 | state: () => {
7 | return {
8 | showSearchView: false,
9 | searchKeyword: '',
10 | suggestData: {} as SearchSuggest
11 | }
12 | },
13 | getters: {
14 | showHot: (state) => {
15 | return state.searchKeyword == ''
16 | }
17 | },
18 | actions: {
19 | async suggest() {
20 | this.suggestData = await useSearchSuggest(this.searchKeyword)
21 | }
22 | }
23 | })
24 |
--------------------------------------------------------------------------------
/src/stores/user.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 | import type { UserProfile } from '@/models/user'
3 | import { useLoginStatus } from '@/utils/api'
4 |
5 | export const useUserStore = defineStore({
6 | id: 'user', // id必填,且需要唯一
7 | state: () => ({
8 | showLogin: false,
9 | profile: {} as UserProfile
10 | }),
11 | getters: {
12 | isLogin: (state) => {
13 | return state.profile?.userId > 0
14 | }
15 | },
16 | actions: {
17 | async checkLogin() {
18 | const cookie = localStorage.getItem('USER-COOKIE') ?? ''
19 | const { code, profile } = await useLoginStatus(cookie)
20 | if (code === 200) {
21 | this.profile = profile
22 | this.showLogin = false
23 | }
24 | }
25 | }
26 | })
27 |
--------------------------------------------------------------------------------
/src/stores/video.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 | import { ref } from 'vue'
3 | import type { PersonalizedPrivateContent, Video, VideoGroup } from '@/models/video'
4 | import { usePersonalizedMv, usePersonalizedPrivateContentList, useVideoGroupList, useVideoTimelineRecommend } from '@/utils/api'
5 | import type { PersonalizedMv } from '@/models/personalized'
6 |
7 | export const useVideoStore = defineStore('video', () => {
8 | const videoTimelineRecommend = ref