Compare commits

...

170 Commits

Author SHA1 Message Date
Zyronon
c9e2a5d3b9
Merge pull request #109 from zyronon/dependabot/npm_and_yarn/node/axios-1.7.4
build(deps): bump axios from 1.6.7 to 1.7.4 in /node
2024-09-18 16:18:52 +08:00
dependabot[bot]
0f443d5328
build(deps): bump axios from 1.6.7 to 1.7.4 in /node
Bumps [axios](https://github.com/axios/axios) from 1.6.7 to 1.7.4.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.6.7...v1.7.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-18 02:16:05 +00:00
Zyronon
1e30b71800
Merge pull request #108 from zyronon/dependabot/npm_and_yarn/vite-5.2.14
build(deps-dev): bump vite from 5.2.10 to 5.2.14
2024-09-18 10:15:00 +08:00
dependabot[bot]
f9682a17e5
build(deps-dev): bump vite from 5.2.10 to 5.2.14
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.2.10 to 5.2.14.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.2.14/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.2.14/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-17 22:19:45 +00:00
Zyronon
1fd5c98c49
Merge pull request #104 from wangzhiyonglyk/master
fix: 修复纵向滑动的下标的bug
2024-09-08 20:57:53 +08:00
王志勇
9a23497964 fix: 修复纵向滑动的下标的bug 2024-09-08 20:28:16 +08:00
Zyronon
83caeaf669
Merge pull request #90 from zyronon/test
save
2024-06-06 16:46:54 +08:00
Zyronon
0478cc868c
save 2024-06-06 16:46:04 +08:00
zyronon
27ee8d87d6 docs: update readme.md 2024-05-30 14:41:37 +08:00
zyronon
26b2d240b5 Merge remote-tracking branch 'origin/master' 2024-05-30 10:47:07 +08:00
zyronon
8dcda1bd10 fix: root font-size automatic adaptation 2024-05-30 10:46:41 +08:00
Zyronon
84f4febd2e
Update README.ja.md 2024-05-11 00:18:40 +08:00
Zyronon
c5b4f2eb33
Merge pull request #81 from eltociear/update-jpdoc
Update README.ja.md
2024-05-08 13:39:55 +08:00
Ikko Eltociear Ashimine
f18b7b19f3 Update README.ja.md 2024-05-08 14:26:41 +09:00
zyronon
e4a9ce5df3 docs: update README.md 2024-05-08 11:32:42 +08:00
github-actions[bot]
9e2fb6683e docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-05-08 03:28:40 +00:00
github-actions[bot]
975a95f6da docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-05-08 03:28:35 +00:00
github-actions[bot]
84e68fcf6a docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-05-08 03:28:29 +00:00
github-actions[bot]
4451761e77 docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-05-08 03:28:24 +00:00
github-actions[bot]
b7efb141d1 docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-05-08 03:28:18 +00:00
zyronon
84a38c3678 docs: update README.md 2024-05-08 11:16:48 +08:00
zyronon
a08900bfd8 docs: update README.md 2024-05-07 10:21:23 +08:00
github-actions[bot]
313f38de4f docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:19:26 +00:00
github-actions[bot]
1357d8cf9a docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:19:21 +00:00
github-actions[bot]
92029d3703 docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:19:16 +00:00
github-actions[bot]
dc973483be docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:19:11 +00:00
github-actions[bot]
07f32c8c1f docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:19:05 +00:00
zyronon
d374310e2a docs: update README.md 2024-05-07 10:18:22 +08:00
zyronon
16a1a75ebb docs: update README.md 2024-05-07 10:13:39 +08:00
github-actions[bot]
88dbfc80f8 docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:12:07 +00:00
github-actions[bot]
25e2d59310 docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:12:02 +00:00
github-actions[bot]
988b78a437 docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:11:56 +00:00
github-actions[bot]
696423d656 docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:11:51 +00:00
github-actions[bot]
9d963d8de3 docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-05-07 02:11:45 +00:00
zyronon
8d1a405190 docs: update README.md 2024-05-07 10:10:22 +08:00
zyronon
2ed18ca97f Merge branch 'master' into dev 2024-05-07 10:08:28 +08:00
zyronon
34015d5ba8 docs: update README.md 2024-05-06 10:15:09 +08:00
github-actions[bot]
d86cd052c7 docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-05-06 02:12:51 +00:00
github-actions[bot]
5ecd4a8e8f docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-05-06 02:12:46 +00:00
github-actions[bot]
db0c9a522b docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-05-06 02:12:41 +00:00
github-actions[bot]
9e1d69a55f docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-05-06 02:12:35 +00:00
github-actions[bot]
1542bd673a docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-05-06 02:12:30 +00:00
zyronon
b555aefe11 docs: update README.md 2024-05-06 10:05:25 +08:00
zyronon
e31a5cc7b1 docs: update README.md 2024-05-06 10:03:55 +08:00
zyronon
706bd9fd48 refactor: save 2024-05-04 01:14:00 +08:00
zyronon
91b80ba547 refactor: save 2024-05-04 00:31:47 +08:00
zyronon
eff3277513 refactor: fixed the problem that the scroll component could not remember the height 2024-05-03 17:03:46 +08:00
zyronon
d2b532a760 refactor: fixed the problem that the scroll component could not remember the height 2024-05-03 17:03:28 +08:00
zyronon
de415aeafa refactor: lint code 2024-05-03 00:48:49 +08:00
Zyronon
af2809ec73
Merge pull request #75 from Zihan-Hu/master
refactor: use CSS to prevent selecting
2024-05-02 14:24:23 +08:00
zyronon
2545739964 docs: update README.md 2024-05-02 03:29:04 +08:00
zyronon
d052f60953 refactor: fuck vite defineConfig 2024-05-02 01:08:33 +08:00
zyronon
7f1e157dab refactor: fuck vite defineConfig 2024-05-02 00:31:06 +08:00
zyronon
ca57625cea refactor: fuck vite defineConfig 2024-05-02 00:14:41 +08:00
Zihan Hu
64332a93dd
refactor: use CSS to prevent selecting 2024-05-02 00:01:41 +08:00
zyronon
ea37bb9dff refactor: save 2024-05-01 22:57:01 +08:00
zyronon
939f92da38 refactor: save 2024-05-01 22:53:47 +08:00
zyronon
40d24ae1af refactor: save 2024-05-01 22:43:44 +08:00
zyronon
8b8bef1bff refactor: save 2024-05-01 22:26:37 +08:00
zyronon
b48df2945b refactor: update Dockerfile 2024-05-01 22:14:42 +08:00
zyronon
7cce654633 refactor: update Dockerfile 2024-05-01 22:13:22 +08:00
zyronon
6d95c87a91 refactor: update Dockerfile 2024-05-01 22:07:36 +08:00
zyronon
6b198e8f21 refactor: update Dockerfile 2024-05-01 21:53:13 +08:00
zyronon
6bf2dfedf2 refactor: update Dockerfile 2024-05-01 21:41:20 +08:00
Zyronon
bbbb9ba222
Update .dockerignore 2024-05-01 21:26:24 +08:00
zyronon
2de950fd12 Merge remote-tracking branch 'origin/master' 2024-05-01 21:19:41 +08:00
Zyronon
f99ec38f14
Create docker-image.yml 2024-05-01 21:18:34 +08:00
zyronon
9d8758eb31 refactor: update Dockerfile 2024-05-01 21:09:08 +08:00
zyronon
2a519248fd refactor: update Dockerfile 2024-05-01 20:59:53 +08:00
zyronon
a1192fe7e3 feat: feat: add build docker image by luochao 2024-05-01 19:54:52 +08:00
Zyronon
e98aac5244
Merge pull request #74 from rookie-luochao/master
feat: add build docker image
2024-05-01 13:20:45 +08:00
luochao
8e23ec740b feat: add build docker image 2024-05-01 00:31:49 +08:00
Zyronon
1128b7822d
Merge pull request #73 from AllisonZw/master
fix: 修复图标展示
2024-04-30 16:58:49 +08:00
dzw
4dffcab92b fix: 修复图标展示 2024-04-30 16:52:58 +08:00
zyronon
3dade9855d docs: update README.md 2024-04-30 15:02:48 +08:00
zyronon
26fc5c0781 docs: update README.md 2024-04-30 13:06:02 +08:00
zyronon
efd7569e79 docs: update README.md 2024-04-30 13:03:34 +08:00
github-actions[bot]
1b2a5890ae docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-04-30 05:01:24 +00:00
github-actions[bot]
19aab02c45 docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-04-30 05:01:18 +00:00
github-actions[bot]
722d31f414 docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-04-30 05:01:13 +00:00
github-actions[bot]
578ecadca1 docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-04-30 05:01:08 +00:00
github-actions[bot]
12ca067d15 docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-04-30 05:01:02 +00:00
zyronon
26d1220508 Merge branch 'master' into dev 2024-04-30 12:29:27 +08:00
Zyronon
5f37d17715
Merge pull request #69 from zhangyuhan2016/patch-1
fix: fix search list text exceeding
2024-04-30 11:07:25 +08:00
zyronon
4f4dd5b409 docs: update README.md 2024-04-30 10:32:11 +08:00
张雨涵
fae5fc5979
fix: text wrapping 2024-04-30 10:28:39 +08:00
张雨涵
0c73eadb45
fix: fix search list text exceeding 2024-04-30 10:15:19 +08:00
zyronon
914a1887a8 refactor: optimize the cover map 2024-04-30 09:15:49 +08:00
zyronon
700802d96f refactor: fixed bug that could not be liked and article link errors 2024-04-30 00:05:38 +08:00
zyronon
54b9b6bf70 refactor: save 2024-04-29 18:54:46 +08:00
Zyronon
0c8437e669
Merge pull request #68 from Chanzhaoyu/dev-fix
fix: 修复底部弹窗错误
2024-04-29 15:41:34 +08:00
ChenZhoYu
4de26471e2 fix: 修复底部弹窗错误 2024-04-29 15:30:49 +08:00
zyronon
d41f49701a Merge remote-tracking branch 'origin/master' 2024-04-29 14:23:34 +08:00
zyronon
24b6b4076d refactor: save 2024-04-29 14:21:43 +08:00
Zyronon
9da5f780db
Merge pull request #66 from Chanzhaoyu/dialog-components
perf: 优化 dilog 下组件
2024-04-29 14:15:57 +08:00
ChenZhoYu
6a6554aa9b perf: dialog components 2024-04-29 14:00:08 +08:00
zyronon
596d8897dc docs: update README.md 2024-04-29 04:12:28 +08:00
zyronon
bbc350e729 refactor: update README.md 2024-04-29 04:07:42 +08:00
zyronon
e4a4c5b480 refactor: remove unuse lib 2024-04-29 02:11:07 +08:00
zyronon
3e559914a0 refactor: update README.md 2024-04-28 15:53:13 +08:00
zyronon
e4d9f0cbb0 refactor: update README.md 2024-04-28 15:49:35 +08:00
zyronon
4f80219a5e Merge remote-tracking branch 'origin/master'
# Conflicts:
#	README.md
2024-04-28 15:46:04 +08:00
zyronon
10494fcfe2 docs: update README.md 2024-04-28 02:11:29 +08:00
github-actions[bot]
9b7d8c5568 docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:09:21 +00:00
github-actions[bot]
7d10bdbd32 docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:09:16 +00:00
github-actions[bot]
e98dfd733d docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:09:11 +00:00
github-actions[bot]
8d06e9f1b6 docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:09:05 +00:00
github-actions[bot]
9992926c1c docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:09:00 +00:00
zyronon
6605dd7db0 docs: update README.md 2024-04-28 02:07:03 +08:00
github-actions[bot]
54f71af752 docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:03:30 +00:00
github-actions[bot]
ed2e8e478b docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:03:25 +00:00
github-actions[bot]
fbb742e3d5 docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:03:20 +00:00
github-actions[bot]
73f4283d6d docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:03:15 +00:00
github-actions[bot]
cd8db0ce26 docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-04-27 18:03:09 +00:00
zyronon
e974f38458 docs: update README.md 2024-04-28 02:02:03 +08:00
zyronon
9e5a389906 fix: fixed bug that occasionally slipped to trigger click events, update the first six recommended videos, add gitee judgment, do not load 7z files without gitee 2024-04-28 01:54:56 +08:00
zyronon
8aedae15a5 refactor: remove mitt lib 2024-04-27 12:43:51 +08:00
zyronon
1b82072f17 Merge branch 'refs/heads/master' into dev 2024-04-27 12:24:22 +08:00
zyronon
e8d0b46f5e refactor: fixed bug where the video does not shrink when commenting 2024-04-27 12:23:48 +08:00
zyronon
79d9d613fc refactor: update README.md 2024-04-27 12:08:16 +08:00
zyronon
7c5bdc9883 refactor: solve the mute problem 2024-04-27 02:31:27 +08:00
zyronon
1b6001f6f6 Merge branch 'refs/heads/master' into dev 2024-04-26 22:03:42 +08:00
zyronon
19105b676e refactor: update README.md 2024-04-26 17:43:20 +08:00
zyronon
45792655ae refactor: update README.md 2024-04-26 17:40:51 +08:00
zyronon
c45c7a7c13 refactor: update README.md 2024-04-26 10:12:43 +08:00
github-actions[bot]
51cfbd090b docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-04-26 02:10:27 +00:00
github-actions[bot]
2d7d325343 docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-04-26 02:10:22 +00:00
github-actions[bot]
bf591f8d91 docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-04-26 02:10:15 +00:00
github-actions[bot]
2466511de7 docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-04-26 02:10:10 +00:00
github-actions[bot]
be9d53813b docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-04-26 02:10:05 +00:00
zyronon
aa1ded21ed refactor: update README.md 2024-04-26 10:04:03 +08:00
zyronon
a7665f4a23 refactor: refactor code and add comment 2024-04-26 03:11:10 +08:00
zyronon
982fdc8727 refactor: remove axios response delay 2024-04-25 18:20:54 +08:00
zyronon
bdb5651a65 refactor: optimize the code 2024-04-25 17:46:13 +08:00
zyronon
b18664a3a6 refactor: handling click event conflicts 2024-04-25 02:12:55 +08:00
zyronon
6107b19d03 refactor: optimize the code 2024-04-24 18:58:02 +08:00
zyronon
15b89986b2 refactor: refactor code and add comment 2024-04-24 02:32:47 +08:00
github-actions[bot]
942ae76a0d docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-04-23 17:20:02 +00:00
github-actions[bot]
02f8ed34cd docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-04-23 17:19:57 +00:00
github-actions[bot]
573f3df63e docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-04-23 17:19:51 +00:00
github-actions[bot]
1993c4d087 docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-04-23 17:19:47 +00:00
github-actions[bot]
45e974218b docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-04-23 17:19:42 +00:00
zyronon
7189f107ef refactor: update README.md 2024-04-24 01:18:23 +08:00
zyronon
bf0160b3bc Merge branch 'refs/heads/dev' 2024-04-23 23:52:13 +08:00
zyronon
85a5fefdf2 refactor: optimize the code 2024-04-23 23:46:18 +08:00
zyronon
325057d197 refactor: optimize the code 2024-04-23 18:59:33 +08:00
zyronon
934796b15e refactor: optimize the code 2024-04-23 18:30:03 +08:00
zyronon
5007e88093 Merge branch 'dev' 2024-04-23 17:36:18 +08:00
zyronon
c64f5fad89 refactor: support for pc preview 2024-04-23 17:35:40 +08:00
Zyronon
c1642befab
Update README.md 2024-04-23 17:00:43 +08:00
Zyronon
09a97cbf5d
Update README.md 2024-04-23 17:00:06 +08:00
Zyronon
d2d6a68eae
Update README.md 2024-04-23 16:58:45 +08:00
zyronon
c63c7e2969 refactor: remove jquery 2024-04-23 15:37:55 +08:00
zyronon
db6a7b9aaf refactor: save 2024-04-22 18:54:47 +08:00
zyronon
6976f58757 refactor: optimize the code 2024-04-20 22:34:45 +08:00
zyronon
b8d8354c2d Merge remote-tracking branch 'origin/master' 2024-04-20 17:01:04 +08:00
zyronon
1e0b955462 refactor: update workflow 2024-04-20 17:00:47 +08:00
github-actions[bot]
9a846ee144 docs: Added README."es".md translation via https://github.com/dephraiim/translate-readme 2024-04-20 08:50:49 +00:00
github-actions[bot]
9e61d073bf docs: Added README."fr".md translation via https://github.com/dephraiim/translate-readme 2024-04-20 08:50:43 +00:00
github-actions[bot]
97bcdd4371 docs: Added README."de".md translation via https://github.com/dephraiim/translate-readme 2024-04-20 08:50:36 +00:00
github-actions[bot]
f81c8300e1 docs: Added README."ja".md translation via https://github.com/dephraiim/translate-readme 2024-04-20 08:50:30 +00:00
github-actions[bot]
70abd546a7 docs: Added README."en".md translation via https://github.com/dephraiim/translate-readme 2024-04-20 08:50:24 +00:00
zyronon
4eeab2e9ee refactor: update README.md 2024-04-20 16:48:20 +08:00
zyronon
7775042f67 refactor: change base url 2024-04-20 16:29:31 +08:00
zyronon
2906e7e925 refactor: update workflow 2024-04-20 16:22:59 +08:00
zyronon
c4313f4b20 refactor: update workflow 2024-04-20 16:17:58 +08:00
zyronon
5e84f3ef5a refactor: update workflow 2024-04-20 16:15:21 +08:00
zyronon
8218d75d35 refactor: update workflow 2024-04-20 16:09:34 +08:00
zyronon
fde4597419 refactor: update workflow 2024-04-20 16:08:40 +08:00
zyronon
e14cd94fd9 refactor: update workflow 2024-04-20 16:07:09 +08:00
158 changed files with 10226 additions and 11240 deletions

25
.dockerignore Normal file
View File

@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.DS_Store
coverage
*.local
dist
node_modules
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
docs
node

View File

@ -1,5 +1,5 @@
# 将静态内容部署到 GitHub Pages 的简易工作流程
name: Deploy static content to Pages
name: Deploy Gitee Pages
on:
# 仅在推送到默认分支时运行。
@ -15,10 +15,6 @@ permissions:
pages: write
id-token: write
# 允许一个并发的部署
concurrency:
group: 'pages'
cancel-in-progress: true
jobs:
# 单次部署的工作描述
@ -46,13 +42,14 @@ jobs:
run: pnpm install
- name: Build
run: pnpm run build
run: pnpm run build-gitee-pages
- name: Deploy to gh-pages
- name: Deploy to Github Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
publish_branch: gitee-pages
- name: Sync to Gitee
uses: wearerequired/git-mirror-action@master
@ -60,4 +57,16 @@ jobs:
SSH_PRIVATE_KEY: ${{ secrets.GITEE_PRIVATE_KEY }}
with:
source-repo: git@github.com:zyronon/douyin.git
destination-repo: git@gitee.com:zyronon/douyin.git
destination-repo: git@gitee.com:zyronon/douyin.git
- name: Build Gitee Pages
uses: yanglbme/gitee-pages-action@main
with:
# 注意替换为你的 Gitee 用户名
gitee-username: zyronon
# 注意在 Settings->Secrets 配置 GITEE_PASSWORD
gitee-password: ${{ secrets.GITEE_PASSWORD }}
# 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错
gitee-repo: zyronon/douyin
# 要部署的分支,默认是 master若是其他分支则需要指定指定的分支必须存在
branch: gitee-pages

View File

@ -0,0 +1,57 @@
# 将静态内容部署到 GitHub Pages 的简易工作流程
name: Deploy Github Pages
on:
# 仅在推送到默认分支时运行。
push:
branches: [ 'master' ]
# 这个选项可以使你手动在 Action tab 页面触发工作流
workflow_dispatch:
# 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages。
permissions:
contents: write
pages: write
id-token: write
jobs:
# 单次部署的工作描述
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm run build-gp-pages
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
# Upload dist repository
path: './dist'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

41
.github/workflows/docker-image-ci.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: Docker Image CI
on:
push:
tags:
- v*
# 这个选项可以使你手动在 Action tab 页面触发工作流
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: get version
id: vars
run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\/v/}
- uses: actions/checkout@v4
- name: set up QEMU
uses: docker/setup-qemu-action@v3
- name: set up docker buildx
uses: docker/setup-buildx-action@v3
- name: login ghrc hub
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GHCR_TOKEN }}
- name: build and push
uses: docker/build-push-action@v5
with:
push: true
platforms: linux/amd64,linux/arm64
tags: |
ghcr.io/${{ github.repository_owner }}/douyin-vue:${{ steps.vars.outputs.version }}
ghcr.io/${{ github.repository_owner }}/douyin-vue:latest

View File

@ -1,36 +1,36 @@
#name: Translate README
#
#on:
# push:
# branches:
# - master
#jobs:
# build:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Setup Node.js
# uses: actions/setup-node@v1
# with:
# node-version: 12.x
# # ISO Langusge Codes: https://cloud.google.com/translate/docs/languages
# - name: Adding README - English
# uses: dephraiim/translate-readme@main
# with:
# LANG: en
# - name: Adding README - Japanese
# uses: dephraiim/translate-readme@main
# with:
# LANG: ja
# - name: Adding README - German
# uses: dephraiim/translate-readme@main
# with:
# LANG: de
# - name: Adding README - French
# uses: dephraiim/translate-readme@main
# with:
# LANG: fr
# - name: Adding README - Spanish
# uses: dephraiim/translate-readme@main
# with:
# LANG: es
name: Translate README
on:
# 这个选项可以使你手动在 Action tab 页面触发工作流
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 12.x
# ISO Langusge Codes: https://cloud.google.com/translate/docs/languages
- name: Adding README - English
uses: dephraiim/translate-readme@main
with:
LANG: en
- name: Adding README - Japanese
uses: dephraiim/translate-readme@main
with:
LANG: ja
- name: Adding README - German
uses: dephraiim/translate-readme@main
with:
LANG: de
- name: Adding README - French
uses: dephraiim/translate-readme@main
with:
LANG: fr
- name: Adding README - Spanish
uses: dephraiim/translate-readme@main
with:
LANG: es

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
# syntax = docker/dockerfile:experimental
FROM --platform=${BUILDPLATFORM:-linux/amd64,linux/arm64} node:20-buster AS builder
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /src
COPY ./ ./
# RUN两次方便观察install和build, 也可以用pnpm cache and locked
RUN pnpm install
RUN npm run build
FROM --platform=${BUILDPLATFORM:-linux/amd64,linux/arm64} ghcr.io/rookie-luochao/nginx-runner:latest
COPY --from=builder /src/dist /app

16
Makefile Normal file
View File

@ -0,0 +1,16 @@
PKG = $(shell cat package.json | grep 'name' | sed -e 's/ "name": "//g' -e 's/",//g')
VERSION = $(shell cat package.json | grep 'version' | sed -e 's/ "version": "//g' -e 's/",//g')
# 本地测试构建docker镜像建议删掉node_modulesnode_modules存在有时会导致报错
docker-build:
docker build . -t $(PKG):$(VERSION)
# 请先创建自己的 buildx driver 实例请看https://juejin.cn/post/7296763284647542838
# 显式指定可执行 docker buildx build --platform linux/amd64,linux/arm64 . -t $(PKG):$(VERSION) --push
docker-buildx-build:
docker buildx build --platform linux/amd64 . -t $(PKG):$(VERSION) --load
docker-run:
docker run -d -p 80:80 $(PKG):$(VERSION)
docker-build-run: docker-build docker-run

View File

@ -1,114 +0,0 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`ist eine Parodie`抖音|TikTok`mobiles Kurzvideoprojekt, das auf basiert[`Vue`](https://cn.vuejs.org/)、[`Vite`](https://cn.vitejs.dev/)erreichen. Habe das Neueste verwendet`Vue`FamilyMart-Technologie-Stack. API-Daten werden lokal im Projekt gespeichert und das Video wird daraus gesammelt`抖音|TikTok`, der Atlas stammt aus`小红书|Xiaohongshu`,passieren[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)Die Bibliothek fängt die API ab und gibt lokale JSON-Daten zurück, um echte Back-End-Anfragen zu simulieren.
<div>
<img width="150px" src='docs/imgs/1.gif' />
<img width="150px" src='docs/imgs/2.gif' />
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
<img width="150px" src='docs/imgs/img-1.jpg' />
<img width="150px" src='docs/imgs/img-2.jpg' />
<img width="150px" src='docs/imgs/img-3.jpg' />
<img width="150px" src='docs/imgs/img-4.jpg' />
<img width="150px" src='docs/imgs/img-5.jpg' />
</div>
## Online-Zugang
Github-Seiten:<https://zyronon.github.io/douyin/>
[//]: # "Vercel: [http://dy.ttentau.top/](http://dy.ttentau.top/) (中国推荐访问这个)~~"
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/) (中国以外地区推荐访问这个)~~"
Die 100G kostenlosen Daten von Vercel und Netlify sind aufgebraucht...🤣
Android-Apk:<https://github.com/zyronon/douyin/releases>
**Beachten**`PC`Sie müssen den Browser in den mobilen Modus schalten, zuerst drücken`F12`Um die Konsole aufzurufen, drücken Sie`Ctrl+Shift+M`um eine normale Vorschau anzuzeigen
**Beachten**:手机请用 [über Browser](https://viayoo.com/zh-cn/)oder Chrome-Browservorschau. Andere Browser erzwingen möglicherweise, dass das Video im Vollbildmodus angezeigt wird, was dazu führt, dass es nicht richtig angezeigt wird.
## Haftungsausschluss
Dieses Projekt dient ausschließlich Studien- und Forschungszwecken, nicht der kommerziellen Nutzung
## laufen
### Schnelle Bereitstellung in Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### lokale Entwicklung
```bash
git clone https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
Öffnen Sie Ihren Browser und besuchen Sie:<http://127.0.0.1:3000>
**Hinweis: Der PC muss den Browser in den Mobilmodus schalten, zuerst drücken`F12`Um die Konsole aufzurufen, drücken Sie`Ctrl+Shift+M`um eine normale Vorschau anzuzeigen**
## Verknüpfung
- `开源中国`:<https://mp.weixin.qq.com/s/TWowmZpU_ojE5G2KzXuU8g>
- `V2EX`:<https://www.v2ex.com/t/1028678>
- `掘金`:<https://juejin.cn/post/7352813352051687458>
## Datenquellen
Das Video stammt von folgenden Douyin-Prominenten
- `我是香秀 🐂🍺`:<https://v.douyin.com/iYRAPA2L/>
- `杨老虎 🐯(磕穿下巴掉牙版)`:<https://v.douyin.com/iYRA56de/>
- `条子`:<https://v.douyin.com/iYRAaqjr/>
- `达莎 Digi`<https://v.douyin.com/iYRA6rwT/>
- `小橙子`:<https://v.douyin.com/iYRAnudw/>
- `南恬`:<https://v.douyin.com/iYRAbKm3/>
- `小霸宠牛排 🥩`<https://v.douyin.com/iYRSosVB/>
- `奶茶妹 ◕🌱`:<https://v.douyin.com/iYRACKhP/>
- `我才是岚岚`:<https://v.douyin.com/iYRAQM1C/>
- `周憬艺 ziran`:<https://v.douyin.com/iYRAQs4h/>
- `刘思瑶 nice`:<https://v.douyin.com/iYRAaERn/>
- `彭十六 elf`:<https://v.douyin.com/iYRAHrVG/>
- `李子柒`:<https://v.douyin.com/iYRA5B88/>
Bild aus den öffentlichen Notizen von Xiaohongshu
Bei den oben genannten Inhalten handelt es sich um alle öffentlichen Informationen im Internet
## Funktionen und Vorschläge
Das Projekt befindet sich derzeit in einem frühen Entwicklungsstadium und es werden kontinuierlich neue Funktionen hinzugefügt. Wenn Sie Funktionen oder Vorschläge für die Software haben, können Sie sich gerne an uns wenden.`Issues`aufgewachsen in
Wenn Ihnen die Designideen dieser Software auch gefallen, reichen Sie sie bitte ein`PR`, Vielen Dank für deine Unterstützung!
## kontaktiere mich
Sie können meine E-Mail kontaktieren<a href="mailto:zyronon@163.com">zyronon@163.com</a>
> Teilen Sie meine anderen Open-Source-Projekte:
>
> _[**Wort eingeben**- Software zum Auswendiglernen von Vokabeln, die im Internet verwendet werden kann](https://github.com/zyronon/typing-word)<img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**Webskripte**- Einige nützliche Grease Monkey-Skripte ~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## Vereinbarung
[GPL](LICENSE)

View File

@ -1,114 +0,0 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`is a parody`抖音|TikTok`mobile short video project, which is based on[`Vue`](https://cn.vuejs.org/)、[`Vite`](https://cn.vitejs.dev/)accomplish. Used the latest`Vue`FamilyMart technology stack. Api data is saved locally in the project, and the video is collected from`抖音|TikTok`, the atlas is collected from`小红书|Xiaohongshu`,pass[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)The library intercepts the API and returns local json data to simulate real back-end requests.
<div>
<img width="150px" src='docs/imgs/1.gif' />
<img width="150px" src='docs/imgs/2.gif' />
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
<img width="150px" src='docs/imgs/img-1.jpg' />
<img width="150px" src='docs/imgs/img-2.jpg' />
<img width="150px" src='docs/imgs/img-3.jpg' />
<img width="150px" src='docs/imgs/img-4.jpg' />
<img width="150px" src='docs/imgs/img-5.jpg' />
</div>
## online access
Github pages:<https://zyronon.github.io/douyin/>
[//]: # "Vercel: [http://dy.ttentau.top/](http://dy.ttentau.top/) (中国推荐访问这个)~~"
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/) (中国以外地区推荐访问这个)~~"
The 100G free data provided by Vercel and Netlify have been used up...🤣
Android Apk:<https://github.com/zyronon/douyin/releases>
**Notice**`PC`You must switch the browser to mobile mode, first press`F12`To bring up the console, press`Ctrl+Shift+M`to preview normally
**Notice**Please use mobile phone[via browser](https://viayoo.com/zh-cn/)or Chrome browser preview. Other browsers may force the video to full screen, causing it to not display properly.
## Disclaimer
This project is for study and research only, not for commercial use
## run
### Quickly deploy to Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### local development
```bash
git clone https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
Open your browser and visit:<http://127.0.0.1:3000>
**Note: PC must switch the browser to mobile mode, first press`F12`To bring up the console, press`Ctrl+Shift+M`to preview normally**
## Link
- `开源中国`:<https://mp.weixin.qq.com/s/TWowmZpU_ojE5G2KzXuU8g>
- `V2EX`:<https://www.v2ex.com/t/1028678>
- `掘金`:<https://juejin.cn/post/7352813352051687458>
## Data Sources
The video comes from the following Douyin celebrities
- `我是香秀 🐂🍺`:<https://v.douyin.com/iYRAPA2L/>
- `杨老虎 🐯(磕穿下巴掉牙版)`:<https://v.douyin.com/iYRA56de/>
- `条子`:<https://v.douyin.com/iYRAaqjr/>
- `达莎 Digi`<https://v.douyin.com/iYRA6rwT/>
- `小橙子`:<https://v.douyin.com/iYRAnudw/>
- `南恬`:<https://v.douyin.com/iYRAbKm3/>
- `小霸宠牛排 🥩`<https://v.douyin.com/iYRSosVB/>
- `奶茶妹 ◕🌱`:<https://v.douyin.com/iYRACKhP/>
- `我才是岚岚`:<https://v.douyin.com/iYRAQM1C/>
- `周憬艺 ziran`:<https://v.douyin.com/iYRAQs4h/>
- `刘思瑶 nice`:<https://v.douyin.com/iYRAaERn/>
- `彭十六 elf`:<https://v.douyin.com/iYRAHrVG/>
- `李子柒`:<https://v.douyin.com/iYRA5B88/>
Picture from Xiaohongshu public notes
The above content is all public information on the Internet
## Features and suggestions
The project is currently in the early stages of development and new features are being added continuously. If you have any features or suggestions for the software, please feel free to contact us.`Issues`raised in
If you also like the design ideas of this software, please submit it`PR`, thank you very much for your support!
## contact me
You can contact my email<a href="mailto:zyronon@163.com">zyronon@163.com</a>
> Share my other open source projects:
>
> _[**Typing Word**- Vocabulary memorization software that can be used on the web~](https://github.com/zyronon/typing-word)<img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**Web Scripts**- Some useful Grease Monkey scripts~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## agreement
[GPL](LICENSE)

View File

@ -1,114 +0,0 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`es una parodia`抖音|TikTok`proyecto de vídeo corto móvil, que se basa en[`Vue`](https://cn.vuejs.org/)、[`Vite`](https://cn.vitejs.dev/)lograr. Usado lo último`Vue`Pila de tecnología FamilyMart. Los datos de API se guardan localmente en el proyecto y el video se recopila de`抖音|TikTok`, el atlas se recoge de`小红书|Xiaohongshu`,aprobar[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)La biblioteca intercepta la API y devuelve datos json locales para simular solicitudes de back-end reales.
<div>
<img width="150px" src='docs/imgs/1.gif' />
<img width="150px" src='docs/imgs/2.gif' />
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
<img width="150px" src='docs/imgs/img-1.jpg' />
<img width="150px" src='docs/imgs/img-2.jpg' />
<img width="150px" src='docs/imgs/img-3.jpg' />
<img width="150px" src='docs/imgs/img-4.jpg' />
<img width="150px" src='docs/imgs/img-5.jpg' />
</div>
## Acceso en linea
Páginas de Github:<https://zyronon.github.io/douyin/>
[//]: # "Vercel: [http://dy.ttentau.top/](http://dy.ttentau.top/) (中国推荐访问这个)~~"
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/) (中国以外地区推荐访问这个)~~"
Los 100G de datos gratuitos proporcionados por Vercel y Netlify se han agotado...🤣
Android Apk:<https://github.com/zyronon/douyin/releases>
**Aviso**`PC`Debes cambiar el navegador al modo móvil, primero presiona`F12`Para abrir la consola, presione`Ctrl+Shift+M`para obtener una vista previa normalmente
**Aviso**:Por favor utilice el teléfono móvil[a través del navegador](https://viayoo.com/zh-cn/)o vista previa del navegador Chrome. Otros navegadores pueden forzar el vídeo a pantalla completa, lo que hace que no se muestre correctamente.
## Descargo de responsabilidad
Este proyecto es sólo para estudio e investigación, no para uso comercial.
## correr
### Implemente rápidamente en Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### desarrollo local
```bash
git clone https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
Abra su navegador y visite:<http://127.0.0.1:3000>
**Nota: La PC debe cambiar el navegador al modo móvil, primero presione`F12`Para abrir la consola, presione`Ctrl+Shift+M`para obtener una vista previa normalmente**
## Enlace
- `开源中国`:<https://mp.weixin.qq.com/s/TWowmZpU_ojE5G2KzXuU8g>
- `V2EX`:<https://www.v2ex.com/t/1028678>
- `掘金`:<https://juejin.cn/post/7352813352051687458>
## Fuentes de datos
El video proviene de las siguientes celebridades de Douyin.
- `我是香秀 🐂🍺`:<https://v.douyin.com/iYRAPA2L/>
- `杨老虎 🐯(磕穿下巴掉牙版)`:<https://v.douyin.com/iYRA56de/>
- `条子`:<https://v.douyin.com/iYRAaqjr/>
- `达莎 Digi`<https://v.douyin.com/iYRA6rwT/>
- `小橙子`:<https://v.douyin.com/iYRAnudw/>
- `南恬`:<https://v.douyin.com/iYRAbKm3/>
- `小霸宠牛排 🥩`<https://v.douyin.com/iYRSosVB/>
- `奶茶妹 ◕🌱`:<https://v.douyin.com/iYRACKhP/>
- `我才是岚岚`:<https://v.douyin.com/iYRAQM1C/>
- `周憬艺 ziran`:<https://v.douyin.com/iYRAQs4h/>
- `刘思瑶 nice`:<https://v.douyin.com/iYRAaERn/>
- `彭十六 elf`:<https://v.douyin.com/iYRAHrVG/>
- `李子柒`:<https://v.douyin.com/iYRA5B88/>
Imagen de las notas públicas de Xiaohongshu.
El contenido anterior es toda información pública en Internet.
## Funciones y sugerencias
El proyecto se encuentra actualmente en las primeras etapas de desarrollo y continuamente se agregan nuevas funciones. Si tiene alguna característica o sugerencia para el software, no dude en contactarnos.`Issues`criado en
Si también le gustan las ideas de diseño de este software, envíelas.`PR`, ¡Muchas gracias por tu apoyo!
## contáctame
Puedes contactar a mi correo<a href="mailto:zyronon@163.com">zyronon@163.com</a>
> Comparta mis otros proyectos de código abierto:
>
> _[**Escribir palabra**- Software de memorización de vocabulario que se puede utilizar en la web ~](https://github.com/zyronon/typing-word)<img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**Guiones web**- Algunos scripts útiles de Grease Monkey~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## acuerdo
[GPL](LICENSE)

View File

@ -1,114 +0,0 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`est une parodie`抖音|TikTok`projet de courte vidéo mobile, basé sur[`Vue`](https://cn.vuejs.org/)、[`Vite`](https://cn.vitejs.dev/)accomplir. Utilisé le dernier`Vue`Pile technologique FamilyMart. Les données API sont enregistrées localement dans le projet et la vidéo est collectée à partir de`抖音|TikTok`, l'atlas est collecté auprès de`小红书|Xiaohongshu`,passer[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)La bibliothèque intercepte l'API et renvoie des données json locales pour simuler de véritables requêtes back-end.
<div>
<img width="150px" src='docs/imgs/1.gif' />
<img width="150px" src='docs/imgs/2.gif' />
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
<img width="150px" src='docs/imgs/img-1.jpg' />
<img width="150px" src='docs/imgs/img-2.jpg' />
<img width="150px" src='docs/imgs/img-3.jpg' />
<img width="150px" src='docs/imgs/img-4.jpg' />
<img width="150px" src='docs/imgs/img-5.jpg' />
</div>
## accès en ligne
Pages GitHub :<https://zyronon.github.io/douyin/>
[//]: # "Vercel: [http://dy.ttentau.top/](http://dy.ttentau.top/) (中国推荐访问这个)~~"
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/) (中国以外地区推荐访问这个)~~"
Les 100G de données gratuites fournies par Vercel et Netlify ont été épuisées...🤣
Android Apk :<https://github.com/zyronon/douyin/releases>
**Avis**`PC`Vous devez passer le navigateur en mode mobile, appuyez d'abord sur`F12`Pour afficher la console, appuyez sur`Ctrl+Shift+M`pour prévisualiser normalement
**Avis**Veuillez utiliser un téléphone portable[via un navigateur](https://viayoo.com/zh-cn/)ou aperçu du navigateur Chrome. D'autres navigateurs peuvent forcer la vidéo à passer en plein écran, ce qui l'empêchera de s'afficher correctement.
## Clause de non-responsabilité
Ce projet est destiné uniquement à l'étude et à la recherche, et non à un usage commercial.
## courir
### Déployez rapidement sur Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### développement local
```bash
git clone https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
Ouvrez votre navigateur et visitez :<http://127.0.0.1:3000>
**Remarque : le PC doit passer le navigateur en mode mobile, appuyez d'abord sur`F12`Pour afficher la console, appuyez sur`Ctrl+Shift+M`pour prévisualiser normalement**
## Lien
- `开源中国`:<https://mp.weixin.qq.com/s/TWowmZpU_ojE5G2KzXuU8g>
- `V2EX`:<https://www.v2ex.com/t/1028678>
- `掘金`:<https://juejin.cn/post/7352813352051687458>
## Les sources de données
La vidéo provient des célébrités Douyin suivantes
- `我是香秀 🐂🍺`:<https://v.douyin.com/iYRAPA2L/>
- `杨老虎 🐯(磕穿下巴掉牙版)`:<https://v.douyin.com/iYRA56de/>
- `条子`:<https://v.douyin.com/iYRAaqjr/>
- `达莎 Digi`<https://v.douyin.com/iYRA6rwT/>
- `小橙子`:<https://v.douyin.com/iYRAnudw/>
- `南恬`:<https://v.douyin.com/iYRAbKm3/>
- `小霸宠牛排 🥩`<https://v.douyin.com/iYRSosVB/>
- `奶茶妹 ◕🌱`:<https://v.douyin.com/iYRACKhP/>
- `我才是岚岚`:<https://v.douyin.com/iYRAQM1C/>
- `周憬艺 ziran`:<https://v.douyin.com/iYRAQs4h/>
- `刘思瑶 nice`:<https://v.douyin.com/iYRAaERn/>
- `彭十六 elf`:<https://v.douyin.com/iYRAHrVG/>
- `李子柒`:<https://v.douyin.com/iYRA5B88/>
Photo tirée des notes publiques de Xiaohongshu
Le contenu ci-dessus est toute l'information publique sur Internet
## Fonctionnalités et suggestions
Le projet en est actuellement aux premiers stades de développement et de nouvelles fonctionnalités sont ajoutées continuellement. Si vous avez des fonctionnalités ou des suggestions pour le logiciel, n'hésitez pas à nous contacter.`Issues`élevé dans
Si vous aimez également les idées de conception de ce logiciel, veuillez le soumettre`PR`, Merci beaucoup pour votre soutient!
## Contactez moi
您可以联系我的邮箱 <a href="mailto:zyronon@163.com">zyronon@163.com</a>
> Partagez mes autres projets open source :
>
> _[**Taper un mot**- Logiciel de mémorisation de vocabulaire utilisable sur le web~](https://github.com/zyronon/typing-word)<img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**Scripts Web**- Quelques scripts Grease Monkey utiles ~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## accord
[GPL](LICENSE)

View File

@ -1,114 +0,0 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`パロディです`抖音|TikTok`をベースにしたモバイルショートビデオプロジェクト[`Vue`](https://cn.vuejs.org/)、[`Vite`](https://cn.vitejs.dev/)成し遂げる。最新のものを使用しました`Vue`ファミリーマートのテクノロジースタック。 API データはプロジェクトのローカルに保存され、ビデオはから収集されます。`抖音|TikTok`、アトラスはから収集されています`小红书|Xiaohongshu`、合格[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)ライブラリは API をインターセプトし、ローカルの JSON データを返して、実際のバックエンド リクエストをシミュレートします。
<div>
<img width="150px" src='docs/imgs/1.gif' />
<img width="150px" src='docs/imgs/2.gif' />
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
<img width="150px" src='docs/imgs/img-1.jpg' />
<img width="150px" src='docs/imgs/img-2.jpg' />
<img width="150px" src='docs/imgs/img-3.jpg' />
<img width="150px" src='docs/imgs/img-4.jpg' />
<img width="150px" src='docs/imgs/img-5.jpg' />
</div>
## オンラインアクセス
Github ページ:[hっtps://zyろのん。ぎてゅb。いお/どうyいん/](https://zyronon.github.io/douyin/)
[//]: # "Vercel: [http://dy.ttentau.top/](http://dy.ttentau.top/) (中国推荐访问这个)~~"
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/) (中国以外地区推荐访问这个)~~"
VercelとNetlifyが提供する100Gの無料データは使い切ってしまいました…🤣
アンドロイドAPK:[hっtps://ぎてゅb。こm/zyろのん/どうyいん/れぇあせs](https://github.com/zyronon/douyin/releases)
**注意**`PC`ブラウザをモバイル モードに切り替えて、最初に を押す必要があります。`F12`コンソールを表示するには、 を押します。`Ctrl+Shift+M`通常にプレビューする
**注意**:携帯電話をご利用ください[ブラウザ経由](https://viayoo.com/zh-cn/)またはChromeブラウザのプレビュー。他のブラウザではビデオが強制的に全画面表示になり、正しく表示されない場合があります。
## 免責事項
このプロジェクトは調査と研究のみを目的としており、商用目的ではありません
## 走る
### Vercel への迅速な導入
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### 地域開発
```bash
git clone https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
ブラウザを開いて、以下にアクセスしてください。[hっtp://127。0。0。1:3000](http://127.0.0.1:3000)
**注: PC はブラウザをモバイル モードに切り替える必要があり、最初に押します。`F12`コンソールを表示するには、 を押します。`Ctrl+Shift+M`通常にプレビューする**
## リンク
- `开源中国`[hっtps://mp。うぇいぃん。っq。こm/s/とぉwmZぷ_おじぇ5G2Kzぅう8g](https://mp.weixin.qq.com/s/TWowmZpU_ojE5G2KzXuU8g)
- `V2EX`[hっtps://wっw。v2えx。こm/t/1028678](https://www.v2ex.com/t/1028678)
- `掘金`[hっtps://じゅえじん。cん/ぽst/7352813352051687458](https://juejin.cn/post/7352813352051687458)
## データソース
このビデオは以下のDouyinの有名人からのものです
- `我是香秀 🐂🍺`[hっtps://v。どうyいん。こm/いYらぱ2L/](https://v.douyin.com/iYRAPA2L/)
- `杨老虎 🐯(磕穿下巴掉牙版)`[hっtps://v。どうyいん。こm/いYら56で/](https://v.douyin.com/iYRA56de/)
- `条子`[hっtps://v。どうyいん。こm/いYらあqjr/](https://v.douyin.com/iYRAaqjr/)
- `达莎 Digi`[hっtps://v。どうyいん。こm/いYら6rwT/](https://v.douyin.com/iYRA6rwT/)
- `小橙子`[hっtps://v。どうyいん。こm/いYらぬdw/](https://v.douyin.com/iYRAnudw/)
- `南恬`[hっtps://v。どうyいん。こm/いYらbKm3/](https://v.douyin.com/iYRAbKm3/)
- `小霸宠牛排 🥩`[hっtps://v。どうyいん。こm/いYRそsVB/](https://v.douyin.com/iYRSosVB/)
- `奶茶妹 ◕🌱`[hっtps://v。どうyいん。こm/いYらCKhP/](https://v.douyin.com/iYRACKhP/)
- `我才是岚岚`[hっtps://v。どうyいん。こm/いYらQM1C/](https://v.douyin.com/iYRAQM1C/)
- `周憬艺 ziran`[hっtps://v。どうyいん。こm/いYらQs4h/](https://v.douyin.com/iYRAQs4h/)
- `刘思瑶 nice`[hっtps://v。どうyいん。こm/いYらあえRん/](https://v.douyin.com/iYRAaERn/)
- `彭十六 elf`[hっtps://v。どうyいん。こm/いYらHrVG/](https://v.douyin.com/iYRAHrVG/)
- `李子柒`[hっtps://v。どうyいん。こm/いYら5B88/](https://v.douyin.com/iYRA5B88/)
小紅書公文書からの写真
上記内容は全てインターネット上の公開情報です
## 特徴と提案
プロジェクトは現在開発の初期段階にあり、ソフトウェアの新機能や提案があれば、お気軽にお問い合わせください。`Issues`で育ちました
このソフトウェアのデザインアイデアも気に入っていただけましたら、ぜひ送信してください`PR`、 ご支援ありがとうございました!
## 私に連絡して
私のメールアドレスにご連絡いただけます<a href="mailto:zyronon@163.com">zyろのん@163。こm</a>
> 私の他のオープンソース プロジェクトを共有してください:
>
> _[**単語を入力する**Web上で使える単語暗記ソフト](https://github.com/zyronon/typing-word)<img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**ウェブスクリプト**- いくつかの便利な Grease Monkey スクリプト~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## 合意
[GPL](LICENSE)

View File

@ -3,8 +3,8 @@
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
<a href="docs/README.en.md">English</a> | <a href="docs/README.es.md">Spanish</a> | <a href="docs/README.de.md">German</a> |
<a href="docs/README.fr.md">French</a> | <a href="README.md">简体中文</a> | <a href="docs/README.ja.md">日本語</a>
</p>
<p align="center">
@ -13,8 +13,9 @@
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue` 是一个模仿 `抖音|TikTok` 的移动端短视频项目,它基于 [`Vue`](https://cn.vuejs.org/)、[`Vite`](https://cn.vitejs.dev/)
实现。使用了最新的 `Vue` 全家桶技术栈。Api 数据保存在项目本地,视频采集自`抖音|TikTok`,图集采集自`小红书|Xiaohongshu`,通过 [`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter) 库拦截Api 并返回本地json数据模拟出真实的后端请求
`douyin-vue` 是一个模仿 `抖音|TikTok` 的移动端短视频项目。`Vue` 在移动端的"最佳实践",媲美原生 `App` 丝滑流畅的使用体验。使用了最新的 `Vue` 技术栈,基于 [`Vue3`](https://cn.vuejs.org/)、[`Vite5`](https://cn.vitejs.dev/)
、[`Pinia`](https://pinia.vuejs.org/)实现。数据保存在项目本地,通过 [`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter) 库拦截Api 并返回本地json数据模拟真实后端请求
<div>
<img width="150px" src='docs/imgs/1.gif' />
@ -22,44 +23,53 @@
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
<img width="150px" src='docs/imgs/img-1.jpg' />
<img width="150px" src='docs/imgs/img-2.jpg' />
<img width="150px" src='docs/imgs/img-3.jpg' />
<img width="150px" src='docs/imgs/img-4.jpg' />
<img width="150px" src='docs/imgs/img-5.jpg' />
</div>
## 在线访问
Github pages: [https://zyronon.github.io/douyin/](https://zyronon.github.io/douyin/)
[//]: # (Gitee Pages: [https://zyronon.gitee.io/douyin/]&#40;https://zyronon.gitee.io/douyin/&#41;&#40;中国地区推荐访问这个地址&#41; )
[//]: # (注意Gitee Pages现在无法更新代码不是最新的。如果你能翻墙推荐访问下面地址 )
[//]: # (Vercel: [http://dy.ttentau.top/]&#40;http://dy.ttentau.top/&#41; &#40;中国推荐访问这个&#41;~~)
Github Pages: [https://dy.ttentau.top/](https://dy.ttentau.top/)
[//]: # (Netlify: [https://douyins.netlify.app/]&#40;https://douyins.netlify.app/&#41; &#40;中国以外地区推荐访问这个&#41;~~)
[//]: # (Gitee pages: [https://dy.ttentau.top/]&#40;https://dy.ttentau.top/&#41; &#40;中国地区推荐访问这个地址&#41; )
[//]: # (Github pages: [https://zyronon.github.io/douyin/]&#40;https://zyronon.github.io/douyin/&#41; )
[//]: # (Netlify: [https://douyins.netlify.app/]&#40;https://douyins.netlify.app/&#41;)
[//]: # (Vercel: [https://douyins.vercel.app]&#40;https://douyins.vercel.app&#41;)
[//]: # (Android Apk: https://github.com/zyronon/douyin/releases)
[//]: # (**注意**`PC` 必须将浏览器切到手机模式,先按 `F12` 调出控制台,再按 `Ctrl+Shift+M`才能正常预览)
[//]: # (**注意**:手机请用 [Via 浏览器]&#40;https://viayoo.com/zh-cn/&#41; 或 Chrome 浏览器预览。其它浏览器可能会强制将视频全屏,导致无法正常显示)
Vercel和Netlify分别送的100G免费流量已经用完了...🤣
## 链接
Android Apk: https://github.com/zyronon/douyin/releases
**注意**`PC` 必须将浏览器切到手机模式,先按 `F12` 调出控制台,再按 `Ctrl+Shift+M`才能正常预览
**注意**:手机请用 [Via 浏览器](https://viayoo.com/zh-cn/) 或 Chrome 浏览器预览。其它浏览器可能会强制将视频全屏,导致无法正常显示
## 免责声明
本项目仅适用于学习和研究,不得用于商业使用
【模仿抖音系列】一:[200行代码实现类似Swiper.js的轮播组件](https://juejin.cn/post/7360512664317018146)
【模仿抖音系列】二:[实现抖音 “视频无限滑动“效果](https://juejin.cn/post/7361614921519054883)
【模仿抖音系列】三:[Vue 路由使用介绍以及添加转场动画](https://juejin.cn/post/7362528152777130025)
【模仿抖音系列】四:[Vue 有条件路由缓存,就像传统新闻网站一样](https://juejin.cn/post/7365334891473240101)
【模仿抖音系列】五:[Github Actions 部署 Pages、同步到 Gitee、翻译 README 、 打包 docker 镜像](https://juejin.cn/post/7365757742381957161)
【模仿抖音系列】六:[使用rem、动态vh自适应移动端](https://juejin.cn/post/7374452765273538595)
## 运行
注意:本项目仅适用于学习和研究,不得用于商业使用
### 快速部署至Vercel
### 快速部署至 Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### 部署到 Docker
```bash
# pull Docker image
docker pull ghcr.io/zyronon/douyin-vue:latest
# start container, nginx reverse proxy custom port, for example: docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
```
### 本地开发
**注意:必须 git 命令 clone 下来才能运行,下载 zip 包是无法运行的。如果 clone 速度太慢,推荐使用 gitee 地址**
```bash
git clone https://github.com/zyronon/douyin.git
git clone https://gitee.com/zyronon/douyin.git (中国使用)
https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
@ -67,13 +77,7 @@ npm run dev
打开浏览器并访问: [http://127.0.0.1:3000](http://127.0.0.1:3000)
**注意PC 必须将浏览器切到手机模式,先按 `F12` 调出控制台,再按 `Ctrl+Shift+M` 才能正常预览**
## 链接
- `开源中国`: https://mp.weixin.qq.com/s/TWowmZpU_ojE5G2KzXuU8g
- `V2EX`: https://www.v2ex.com/t/1028678
- `掘金`: https://juejin.cn/post/7352813352051687458
**注意:需要将浏览器切至手机模式,先按 `F12` 调出控制台,再按 `Ctrl+Shift+M` 才能正常预览**
## 数据来源
@ -97,6 +101,7 @@ npm run dev
以上内容均是互联网公开信息
## 功能与建议
目前项目处于开发初期,新功能正在持续添加中,如果你对软件有任何功能与建议,欢迎在 `Issues` 中提出
@ -114,4 +119,4 @@ npm run dev
## 许可协议
[GPL](LICENSE)
[GPL](LICENSE)

View File

@ -9,3 +9,4 @@
- 双指缩放
- AutoInput组件在真机上无法输入
- 真机上100vh显示异常的问题

128
docs/README.de.md Normal file
View File

@ -0,0 +1,128 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="../README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`ist eine Parodie`抖音|TikTok`mobiles Kurzvideoprojekt.`Vue`„Best Practices“ auf der mobilen Seite, vergleichbar mit Native`App`Seidiges und geschmeidiges Erlebnis. Habe das Neueste verwendet`Vue`Technologie-Stack, basierend auf[`Vue3`](https://cn.vuejs.org/)、[`Vite5`](https://cn.vitejs.dev/)、[`Pinia`](https://pinia.vuejs.org/)erreichen. Die Daten werden lokal im Projekt gespeichert[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)Die Bibliothek fängt die API ab und gibt lokale JSON-Daten zurück, um echte Backend-Anfragen zu simulieren.
<div>
<img width="150px" src='imgs/1.gif' />
<img width="150px" src='imgs/2.gif' />
<img width="150px" src='imgs/3.gif' />
<img width="150px" src='imgs/4.gif' />
<img width="150px" src='imgs/5.gif' />
</div>
## Online-Zugang
[//]: # "Gitee Pages: [https://zyronon.gitee.io/douyin/](https://zyronon.gitee.io/douyin/)(中国地区推荐访问这个地址) "
[//]: # "注意Gitee Pages现在无法更新代码不是最新的。如果你能翻墙推荐访问下面地址 "
Github-Seiten:<https://dy.ttentau.top/>
[//]: # "Gitee pages: [https://dy.ttentau.top/](https://dy.ttentau.top/) (中国地区推荐访问这个地址) "
[//]: # "Github pages: [https://zyronon.github.io/douyin/](https://zyronon.github.io/douyin/) "
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/)"
[//]: # "Vercel: [https://douyins.vercel.app](https://douyins.vercel.app)"
[//]: # "Android Apk: https://github.com/zyronon/douyin/releases"
[//]: # "**注意**`PC` 必须将浏览器切到手机模式,先按 `F12` 调出控制台,再按 `Ctrl+Shift+M`才能正常预览"
[//]: # "**注意**:手机请用 [Via 浏览器](https://viayoo.com/zh-cn/) 或 Chrome 浏览器预览。其它浏览器可能会强制将视频全屏,导致无法正常显示"
## Verknüpfung
\[Imitation Douyin-Serie] 1:[200 Codezeilen zur Implementierung einer Karussellkomponente ähnlich Swiper.js](https://juejin.cn/post/7360512664317018146)
\[Imitation Douyin-Serie] 2:[Verwirklichen Sie den „unendlich gleitenden Video“-Effekt auf Douyin](https://juejin.cn/post/7361614921519054883)
\[Imitation Douyin-Serie] Drei:[Einführung in die Verwendung des Vue-Routings und das Hinzufügen von Übergangsanimationen](https://juejin.cn/post/7362528152777130025)
\[Imitation Douyin-Serie] Vier:[Bedingtes Routen-Caching von Vue, genau wie bei herkömmlichen Nachrichtenseiten](https://juejin.cn/post/7365334891473240101)
\[Imitation Douyin-Serie] Fünf:[Github-Aktionen stellen Seiten bereit, synchronisieren mit Gitee, übersetzen README, packen Docker-Image](https://juejin.cn/post/7365757742381957161)
## laufen
Hinweis: Dieses Projekt ist nur für Studien- und Forschungszwecke geeignet, nicht für die kommerzielle Nutzung
### Schnelle Bereitstellung in Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### Auf Docker bereitstellen
```bash
# pull Docker image
docker pull ghcr.io/zyronon/douyin-vue:latest
# start container, nginx reverse proxy custom port, for example: docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
```
### lokale Entwicklung
**Hinweis: Der Git-Befehl muss geklont werden, damit er ausgeführt werden kann. Das Herunterladen des ZIP-Pakets ist nicht möglich. Wenn die Klongeschwindigkeit zu langsam ist, wird empfohlen, die Gitee-Adresse zu verwenden**
```bash
git clone https://gitee.com/zyronon/douyin.git (中国使用)
https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
Öffnen Sie Ihren Browser und besuchen Sie:<http://127.0.0.1:3000>
**Hinweis: Sie müssen den Browser in den Mobilmodus schalten, indem Sie zuerst drücken`F12`Um die Konsole aufzurufen, drücken Sie`Ctrl+Shift+M`um eine normale Vorschau anzuzeigen**
## Datenquellen
Das Video stammt von folgenden Douyin-Prominenten
- `我是香秀 🐂🍺`:<https://v.douyin.com/iYRAPA2L/>
- `杨老虎 🐯(磕穿下巴掉牙版)`:<https://v.douyin.com/iYRA56de/>
- `条子`:<https://v.douyin.com/iYRAaqjr/>
- `达莎 Digi`<https://v.douyin.com/iYRA6rwT/>
- `小橙子`:<https://v.douyin.com/iYRAnudw/>
- `南恬`:<https://v.douyin.com/iYRAbKm3/>
- `小霸宠牛排 🥩`<https://v.douyin.com/iYRSosVB/>
- `奶茶妹 ◕🌱`:<https://v.douyin.com/iYRACKhP/>
- `我才是岚岚`:<https://v.douyin.com/iYRAQM1C/>
- `周憬艺 ziran`:<https://v.douyin.com/iYRAQs4h/>
- `刘思瑶 nice`:<https://v.douyin.com/iYRAaERn/>
- `彭十六 elf`:<https://v.douyin.com/iYRAHrVG/>
- `李子柒`:<https://v.douyin.com/iYRA5B88/>
Bild aus den öffentlichen Notizen von Xiaohongshu
Bei den oben genannten Inhalten handelt es sich um alle öffentlichen Informationen im Internet
## Funktionen und Vorschläge
Das Projekt befindet sich derzeit in einem frühen Entwicklungsstadium und es werden kontinuierlich neue Funktionen hinzugefügt. Wenn Sie Funktionen oder Vorschläge für die Software haben, können Sie sich gerne an uns wenden.`Issues`aufgewachsen in
Wenn Ihnen die Designideen dieser Software auch gefallen, reichen Sie sie bitte ein`PR`, Vielen Dank für deine Unterstützung!
## kontaktiere mich
Sie können meine E-Mail kontaktieren<a href="mailto:zyronon@163.com">zyronon@163.com</a>
> Teilen Sie meine anderen Open-Source-Projekte:
>
> _[**Wort eingeben**- Software zum Auswendiglernen von Vokabeln, die im Internet verwendet werden kann](https://github.com/zyronon/typing-word)<img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**Webskripte**- Einige nützliche Grease Monkey-Skripte ~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## Vereinbarung
[GPL](../LICENSE)

128
docs/README.en.md Normal file
View File

@ -0,0 +1,128 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="../README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`is a parody`抖音|TikTok`mobile short video project.`Vue`"Best practices" on the mobile side, comparable to native`App` 丝滑流畅的使用体验。使用了最新的 `Vue`technology stack, based on[`Vue3`](https://cn.vuejs.org/)、[`Vite5`](https://cn.vitejs.dev/)、[`Pinia`](https://pinia.vuejs.org/)accomplish. The data is saved locally in the project through[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)The library intercepts the API and returns local json data to simulate real backend requests.
<div>
<img width="150px" src='docs/imgs/1.gif' />
<img width="150px" src='docs/imgs/2.gif' />
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
</div>
## Online access
[//]: # "Gitee Pages: [https://zyronon.gitee.io/douyin/](https://zyronon.gitee.io/douyin/)(中国地区推荐访问这个地址) "
[//]: # "注意Gitee Pages现在无法更新代码不是最新的。如果你能翻墙推荐访问下面地址 "
Github Pages:<https://dy.ttentau.top/>
[//]: # "Gitee pages: [https://dy.ttentau.top/](https://dy.ttentau.top/) (中国地区推荐访问这个地址) "
[//]: # "Github pages: [https://zyronon.github.io/douyin/](https://zyronon.github.io/douyin/) "
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/)"
[//]: # "Vercel: [https://douyins.vercel.app](https://douyins.vercel.app)"
[//]: # "Android Apk: https://github.com/zyronon/douyin/releases"
[//]: # "**注意**`PC` 必须将浏览器切到手机模式,先按 `F12` 调出控制台,再按 `Ctrl+Shift+M`才能正常预览"
[//]: # "**注意**:手机请用 [Via 浏览器](https://viayoo.com/zh-cn/) 或 Chrome 浏览器预览。其它浏览器可能会强制将视频全屏,导致无法正常显示"
## Link
\[Imitation Douyin Series] 1:[200 lines of code to implement a carousel component similar to Swiper.js](https://juejin.cn/post/7360512664317018146)
\[Imitation Douyin Series] 2:[Realize the "infinite sliding video" effect on Douyin](https://juejin.cn/post/7361614921519054883)
\[Imitation Douyin Series] Three:[Introduction to using Vue routing and adding transition animations](https://juejin.cn/post/7362528152777130025)
\[Imitation Douyin Series] Four:[Vue conditional route caching, just like traditional news sites](https://juejin.cn/post/7365334891473240101)
\[Imitation Douyin Series] Five:[Github Actions deploy Pages, synchronize to Gitee, translate README, package docker image](https://juejin.cn/post/7365757742381957161)
## run
Note: This project is only suitable for study and research, not for commercial use
### Quickly deploy to Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### Deploy to Docker
```bash
# pull Docker image
docker pull ghcr.io/zyronon/douyin-vue:latest
# start container, nginx reverse proxy custom port, for example: docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
```
### local development
**Note: The git command must be cloned to run. Downloading the zip package cannot run. If the clone speed is too slow, it is recommended to use the gitee address**
```bash
git clone https://gitee.com/zyronon/douyin.git (中国使用)
https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
Open your browser and visit:<http://127.0.0.1:3000>
**注意:需要将浏览器切至手机模式,先按 `F12`To bring up the console, press`Ctrl+Shift+M`to preview normally**
## Data Sources
The video comes from the following Douyin celebrities
- `我是香秀 🐂🍺`:<https://v.douyin.com/iYRAPA2L/>
- `杨老虎 🐯(磕穿下巴掉牙版)`:<https://v.douyin.com/iYRA56de/>
- `条子`:<https://v.douyin.com/iYRAaqjr/>
- `达莎 Digi`<https://v.douyin.com/iYRA6rwT/>
- `小橙子`:<https://v.douyin.com/iYRAnudw/>
- `南恬`:<https://v.douyin.com/iYRAbKm3/>
- `小霸宠牛排 🥩`<https://v.douyin.com/iYRSosVB/>
- `奶茶妹 ◕🌱`:<https://v.douyin.com/iYRACKhP/>
- `我才是岚岚`:<https://v.douyin.com/iYRAQM1C/>
- `周憬艺 ziran`:<https://v.douyin.com/iYRAQs4h/>
- `刘思瑶 nice`:<https://v.douyin.com/iYRAaERn/>
- `彭十六 elf`:<https://v.douyin.com/iYRAHrVG/>
- `李子柒`:<https://v.douyin.com/iYRA5B88/>
Picture from Xiaohongshu public notes
The above content is all public information on the Internet
## Features and suggestions
The project is currently in the early stages of development and new features are being added continuously. If you have any features or suggestions for the software, please feel free to contact us.`Issues`raised in
If you also like the design ideas of this software, please submit it`PR`, thank you very much for your support!
## contact me
You can contact my email<a href="mailto:zyronon@163.com">zyronon@163.com</a>
> Share my other open source projects:
>
> _[**Typing Word**- Vocabulary memorization software that can be used on the web~](https://github.com/zyronon/typing-word)<img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**Web Scripts**- Some useful Grease Monkey scripts~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## agreement
[GPL](../LICENSE)

128
docs/README.es.md Normal file
View File

@ -0,0 +1,128 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="../README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`es una parodia`抖音|TikTok`Proyecto de vídeo corto móvil.`Vue`"Mejores prácticas" en el lado móvil, comparables a las nativas`App`Experiencia sedosa y suave. Usado lo último`Vue`pila de tecnología, basada en[`Vue3`](https://cn.vuejs.org/)、[`Vite5`](https://cn.vitejs.dev/)、[`Pinia`](https://pinia.vuejs.org/)lograr. Los datos se guardan localmente en el proyecto a través de[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)La biblioteca intercepta la API y devuelve datos json locales para simular solicitudes de backend reales.
<div>
<img width="150px" src='docs/imgs/1.gif' />
<img width="150px" src='docs/imgs/2.gif' />
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
</div>
## Acceso en linea
[//]: # "Gitee Pages: [https://zyronon.gitee.io/douyin/](https://zyronon.gitee.io/douyin/)(中国地区推荐访问这个地址) "
[//]: # "注意Gitee Pages现在无法更新代码不是最新的。如果你能翻墙推荐访问下面地址 "
Páginas de Github:<https://dy.ttentau.top/>
[//]: # "Gitee pages: [https://dy.ttentau.top/](https://dy.ttentau.top/) (中国地区推荐访问这个地址) "
[//]: # "Github pages: [https://zyronon.github.io/douyin/](https://zyronon.github.io/douyin/) "
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/)"
[//]: # "Vercel: [https://douyins.vercel.app](https://douyins.vercel.app)"
[//]: # "Android Apk: https://github.com/zyronon/douyin/releases"
[//]: # "**注意**`PC` 必须将浏览器切到手机模式,先按 `F12` 调出控制台,再按 `Ctrl+Shift+M`才能正常预览"
[//]: # "**注意**:手机请用 [Via 浏览器](https://viayoo.com/zh-cn/) 或 Chrome 浏览器预览。其它浏览器可能会强制将视频全屏,导致无法正常显示"
## Enlace
\[Serie Imitación Douyin] 1:[200 líneas de código para implementar un componente carrusel similar a Swiper.js](https://juejin.cn/post/7360512664317018146)
\[Serie Imitación Douyin] 2:[Realice el efecto de "vídeo deslizante infinito" en Douyin](https://juejin.cn/post/7361614921519054883)
\[Serie Imitación Douyin] Tres:[Vue 路由使用介绍以及添加转场动画](https://juejin.cn/post/7362528152777130025)
\[Serie Imitación Douyin] Cuatro:[Almacenamiento en caché de rutas condicionales de Vue, al igual que los sitios de noticias tradicionales](https://juejin.cn/post/7365334891473240101)
\[Serie Imitación Douyin] Cinco:[Github Actions implementa páginas, sincroniza con Gitee, traduce README, empaqueta la imagen de la ventana acoplable](https://juejin.cn/post/7365757742381957161)
## correr
Nota: Este proyecto sólo es apto para estudio e investigación, no para uso comercial.
### Implemente rápidamente en Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### Implementar en Docker
```bash
# pull Docker image
docker pull ghcr.io/zyronon/douyin-vue:latest
# start container, nginx reverse proxy custom port, for example: docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
```
### desarrollo local
**Nota: El comando git debe clonarse para ejecutarse. No se puede ejecutar la descarga del paquete zip. Si la velocidad de clonación es demasiado lenta, se recomienda utilizar la dirección del albergue**
```bash
git clone https://gitee.com/zyronon/douyin.git (中国使用)
https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
Abra su navegador y visite:<http://127.0.0.1:3000>
**Nota: Debe cambiar el navegador al modo móvil, primero presione`F12`Para abrir la consola, presione`Ctrl+Shift+M`para obtener una vista previa normalmente**
## Fuentes de datos
El video proviene de las siguientes celebridades de Douyin.
- `我是香秀 🐂🍺`:<https://v.douyin.com/iYRAPA2L/>
- `杨老虎 🐯(磕穿下巴掉牙版)`:<https://v.douyin.com/iYRA56de/>
- `条子`:<https://v.douyin.com/iYRAaqjr/>
- `达莎 Digi`<https://v.douyin.com/iYRA6rwT/>
- `小橙子`:<https://v.douyin.com/iYRAnudw/>
- `南恬`:<https://v.douyin.com/iYRAbKm3/>
- `小霸宠牛排 🥩`<https://v.douyin.com/iYRSosVB/>
- `奶茶妹 ◕🌱`:<https://v.douyin.com/iYRACKhP/>
- `我才是岚岚`:<https://v.douyin.com/iYRAQM1C/>
- `周憬艺 ziran`:<https://v.douyin.com/iYRAQs4h/>
- `刘思瑶 nice`:<https://v.douyin.com/iYRAaERn/>
- `彭十六 elf`:<https://v.douyin.com/iYRAHrVG/>
- `李子柒`:<https://v.douyin.com/iYRA5B88/>
Imagen de las notas públicas de Xiaohongshu.
以上内容均是互联网公开信息
## Funciones y sugerencias
El proyecto se encuentra actualmente en las primeras etapas de desarrollo y continuamente se agregan nuevas funciones. Si tiene alguna característica o sugerencia para el software, no dude en contactarnos.`Issues`criado en
Si también le gustan las ideas de diseño de este software, envíelas.`PR`, ¡Muchas gracias por tu apoyo!
## contáctame
Puedes contactar a mi correo<a href="mailto:zyronon@163.com">zyronon@163.com</a>
> Comparta mis otros proyectos de código abierto:
>
> _[**Escribir palabra**- Software de memorización de vocabulario que se puede utilizar en la web ~](https://github.com/zyronon/typing-word) <img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**Guiones web**- Algunos scripts útiles de Grease Monkey~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## acuerdo
[GPL](../LICENSE)

128
docs/README.fr.md Normal file
View File

@ -0,0 +1,128 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="../README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`est une parodie`抖音|TikTok`projet de courte vidéo mobile.`Vue`Des « bonnes pratiques » côté mobile, comparables au natif`App`Expérience soyeuse et douce. Utilisé le dernier`Vue`pile technologique, basée sur[`Vue3`](https://cn.vuejs.org/)、[`Vite5`](https://cn.vitejs.dev/)、[`Pinia`](https://pinia.vuejs.org/)accomplir. Les données sont enregistrées localement dans le projet via[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)La bibliothèque intercepte l'API et renvoie des données json locales pour simuler de véritables requêtes backend.
<div>
<img width="150px" src='docs/imgs/1.gif' />
<img width="150px" src='docs/imgs/2.gif' />
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
</div>
## Accès en ligne
[//]: # "Gitee Pages: [https://zyronon.gitee.io/douyin/](https://zyronon.gitee.io/douyin/)(中国地区推荐访问这个地址) "
[//]: # "注意Gitee Pages现在无法更新代码不是最新的。如果你能翻墙推荐访问下面地址 "
Pages GitHub :<https://dy.ttentau.top/>
[//]: # "Gitee pages: [https://dy.ttentau.top/](https://dy.ttentau.top/) (中国地区推荐访问这个地址) "
[//]: # "Github pages: [https://zyronon.github.io/douyin/](https://zyronon.github.io/douyin/) "
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/)"
[//]: # "Vercel: [https://douyins.vercel.app](https://douyins.vercel.app)"
[//]: # "Android Apk: https://github.com/zyronon/douyin/releases"
[//]: # "**注意**`PC` 必须将浏览器切到手机模式,先按 `F12` 调出控制台,再按 `Ctrl+Shift+M`才能正常预览"
[//]: # "**注意**:手机请用 [Via 浏览器](https://viayoo.com/zh-cn/) 或 Chrome 浏览器预览。其它浏览器可能会强制将视频全屏,导致无法正常显示"
## Lien
\[Série Imitation Douyin] 1 :[200 lignes de code pour implémenter un composant carrousel similaire à Swiper.js](https://juejin.cn/post/7360512664317018146)
\[Série Imitation Douyin] 2 :[Réaliser l'effet "vidéo coulissante infinie" sur Douyin](https://juejin.cn/post/7361614921519054883)
\[Série Imitation Douyin] Trois :[Introduction à l'utilisation du routage Vue et à l'ajout d'animations de transition](https://juejin.cn/post/7362528152777130025)
\[Série Imitation Douyin] Quatre :[Mise en cache des routes conditionnelles Vue, tout comme les sites d'information traditionnels](https://juejin.cn/post/7365334891473240101)
\[Série Imitation Douyin] Cinq :[Les actions Github déploient des pages, synchronisent avec Gitee, traduisent README, emballent l'image Docker](https://juejin.cn/post/7365757742381957161)
## courir
Remarque : ce projet convient uniquement à l'étude et à la recherche, et non à un usage commercial.
### Déployez rapidement sur Vercel
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### Déployer sur Docker
```bash
# pull Docker image
docker pull ghcr.io/zyronon/douyin-vue:latest
# start container, nginx reverse proxy custom port, for example: docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
```
### développement local
**Remarque : La commande git doit être clonée pour s'exécuter. Le téléchargement du package zip ne peut pas s'exécuter. Si la vitesse de clonage est trop lente, il est recommandé d'utiliser l'adresse du gîte**
```bash
git clone https://gitee.com/zyronon/douyin.git (中国使用)
https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
Ouvrez votre navigateur et visitez :<http://127.0.0.1:3000>
**Remarque : Vous devez passer le navigateur en mode mobile, appuyez d'abord sur`F12`Pour afficher la console, appuyez sur`Ctrl+Shift+M`pour prévisualiser normalement**
## Les sources de données
La vidéo provient des célébrités Douyin suivantes
- `我是香秀 🐂🍺`:<https://v.douyin.com/iYRAPA2L/>
- `杨老虎 🐯(磕穿下巴掉牙版)`:<https://v.douyin.com/iYRA56de/>
- `条子`:<https://v.douyin.com/iYRAaqjr/>
- `达莎 Digi`<https://v.douyin.com/iYRA6rwT/>
- `小橙子`:<https://v.douyin.com/iYRAnudw/>
- `南恬`:<https://v.douyin.com/iYRAbKm3/>
- `小霸宠牛排 🥩`<https://v.douyin.com/iYRSosVB/>
- `奶茶妹 ◕🌱`:<https://v.douyin.com/iYRACKhP/>
- `我才是岚岚`:<https://v.douyin.com/iYRAQM1C/>
- `周憬艺 ziran`:<https://v.douyin.com/iYRAQs4h/>
- `刘思瑶 nice`:<https://v.douyin.com/iYRAaERn/>
- `彭十六 elf`:<https://v.douyin.com/iYRAHrVG/>
- `李子柒`:<https://v.douyin.com/iYRA5B88/>
Photo tirée des notes publiques de Xiaohongshu
Le contenu ci-dessus est toute l'information publique sur Internet
## Fonctionnalités et suggestions
Le projet en est actuellement aux premiers stades de développement et de nouvelles fonctionnalités sont ajoutées continuellement. Si vous avez des fonctionnalités ou des suggestions pour le logiciel, n'hésitez pas à nous contacter.`Issues`élevé dans
Si vous aimez également les idées de conception de ce logiciel, veuillez le soumettre`PR`, Merci beaucoup pour votre soutient!
## Contactez moi
Vous pouvez contacter mon email<a href="mailto:zyronon@163.com">zyronon@163.com</a>
> Partagez mes autres projets open source :
>
> _[**Taper un mot**- Logiciel de mémorisation de vocabulaire utilisable sur le web~](https://github.com/zyronon/typing-word)<img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**Scripts Web**- Quelques scripts Grease Monkey utiles ~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## accord
[GPL](../LICENSE)

128
docs/README.ja.md Normal file
View File

@ -0,0 +1,128 @@
<h1 align="center">
Douyin-Vue
</h1>
<p align="center">
<a href="README.en.md">English</a> | <a href="README.es.md">Spanish</a> | <a href="README.de.md">German</a> |
<a href="README.fr.md">French</a> | <a href="../README.md">简体中文</a> | <a href="README.ja.md">日本語</a>
</p>
<p align="center">
<a href="https://github.com/zyronon/douyin/blob/master/LICENSE"><img src="https://img.shields.io/github/license/zyronon/douyin" alt="License"></a>
<a><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg"/></a>
<a><img src="https://img.shields.io/badge/Powered%20by-Vue-blue"/></a>
</p>
`douyin-vue`パロディです`抖音|TikTok`モバイルショートビデオプロジェクト。`Vue`ネイティブと同等のモバイル側の「ベスト プラクティス」`App`シルキーで滑らかな使い心地。最新のものを使用しました`Vue`テクノロジースタック、に基づく[`Vue3`](https://cn.vuejs.org/)、[`Vite5`](https://cn.vitejs.dev/)、[`Pinia`](https://pinia.vuejs.org/)成し遂げる。データはプロジェクト内でローカルに保存されます。[`axios-mock-adapter`](https://github.com/ctimmerm/axios-mock-adapter)ライブラリは API をインターセプトし、ローカルの JSON データを返して、実際のバックエンド リクエストをシミュレートします。
<div>
<img width="150px" src='docs/imgs/1.gif' />
<img width="150px" src='docs/imgs/2.gif' />
<img width="150px" src='docs/imgs/3.gif' />
<img width="150px" src='docs/imgs/4.gif' />
<img width="150px" src='docs/imgs/5.gif' />
</div>
## オンラインアクセス
[//]: # "Gitee Pages: [https://zyronon.gitee.io/douyin/](https://zyronon.gitee.io/douyin/)(中国地区推荐访问这个地址) "
[//]: # "注意Gitee Pages现在无法更新代码不是最新的。如果你能翻墙推荐访问下面地址 "
Github ページ:[https://dy.ttentau.top/](https://dy.ttentau.top/)
[//]: # "Gitee pages: [https://dy.ttentau.top/](https://dy.ttentau.top/) (中国地区推荐访问这个地址) "
[//]: # "Github pages: [https://zyronon.github.io/douyin/](https://zyronon.github.io/douyin/) "
[//]: # "Netlify: [https://douyins.netlify.app/](https://douyins.netlify.app/)"
[//]: # "Vercel: [https://douyins.vercel.app](https://douyins.vercel.app)"
[//]: # "Android Apk: https://github.com/zyronon/douyin/releases"
[//]: # "**注意**`PC` 必须将浏览器切到手机模式,先按 `F12` 调出控制台,再按 `Ctrl+Shift+M`才能正常预览"
[//]: # "**注意**:手机请用 [Via 浏览器](https://viayoo.com/zh-cn/) 或 Chrome 浏览器预览。其它浏览器可能会强制将视频全屏,导致无法正常显示"
## リンク
【模倣同音シリーズ】 1:[Swiper.js に似たカルーセル コンポーネントを実装するための 200 行のコード](https://juejin.cn/post/7360512664317018146)
【模倣同音シリーズ】 2[Douyinで「無限スライドビデオ」効果を実現](https://juejin.cn/post/7361614921519054883)
【模倣同音シリーズ】 3[Vue ルーティングの使用とトランジション アニメーションの追加の概要](https://juejin.cn/post/7362528152777130025)
【模倣同音シリーズ】 4[従来のニュースサイトと同様に、Vue の条件付きルート キャッシュ](https://juejin.cn/post/7365334891473240101)
【模倣同音シリーズ】 5[Github Actions ページのデプロイ、Gitee への同期、README の翻訳、Docker イメージのパッケージ化](https://juejin.cn/post/7365757742381957161)
## 実行
注: このプロジェクトは研究と研究のみに適しており、商業利用には適していません。
### Vercel への迅速な導入
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/zyronon/douyin)
### Dockerへのデプロイ
```bash
# pull Docker image
docker pull ghcr.io/zyronon/douyin-vue:latest
# start container, nginx reverse proxy custom port, for example: docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
docker run -d -p 80:80 ghcr.io/zyronon/douyin-vue:latest
```
### 地域開発
**注: git コマンドを実行するには、クローンを作成する必要があります。zip パッケージのダウンロードは実行できません。クローン速度が遅すぎる場合は、gitee アドレスを使用することをお勧めします。**
```bash
git clone https://gitee.com/zyronon/douyin.git (中国使用)
https://github.com/zyronon/douyin.git
cd douyin
npm install
npm run dev
```
ブラウザを開いて、以下にアクセスしてください。[http://127.0.0.1:3000](http://127.0.0.1:3000)
**注: ブラウザをモバイル モードに切り替える必要があります。最初に を押します。`F12`コンソールを表示するには、`Ctrl+Shift+M` を押します。通常にプレビューする**
## データソース
このビデオは以下のDouyinの有名人からのものです
- `我是香秀 🐂🍺`: [https://v.douyin.com/iYRAPA2L/](https://v.douyin.com/iYRAPA2L/)
- `杨老虎 🐯(磕穿下巴掉牙版)`: [https://v.douyin.com/iYRA56de/](https://v.douyin.com/iYRA56de/)
- `条子`: [https://v.douyin.com/iYRAaqjr/](https://v.douyin.com/iYRAaqjr/)
- `达莎 Digi`[https://v.douyin.com/iYRA6rwT/](https://v.douyin.com/iYRA6rwT/)
- `小橙子`: [https://v.douyin.com/iYRAnudw/](https://v.douyin.com/iYRAnudw/)
- `南恬`: [https://v.douyin.com/iYRAbKm3/](https://v.douyin.com/iYRAbKm3/)
- `小霸宠牛排 🥩`[https://v.douyin.com/iYRSosVB/](https://v.douyin.com/iYRSosVB/)
- `奶茶妹 ◕🌱`: [https://v.douyin.com/iYRACKhP/](https://v.douyin.com/iYRACKhP/)
- `我才是岚岚`: [https://v.douyin.com/iYRAQM1C/](https://v.douyin.com/iYRAQM1C/)
- `周憬艺 ziran`: [https://v.douyin.com/iYRAQs4h/](https://v.douyin.com/iYRAQs4h/)
- `刘思瑶 nice`: [https://v.douyin.com/iYRAaERn/](https://v.douyin.com/iYRAaERn/)
- `彭十六 elf`: [https://v.douyin.com/iYRAHrVG/](https://v.douyin.com/iYRAHrVG/)
- `李子柒`: [https://v.douyin.com/iYRA5B88/](https://v.douyin.com/iYRA5B88/)
小紅書公文書からの写真
上記内容は全てインターネット上の公開情報です
## 特徴と提案
プロジェクトは現在開発の初期段階にあり、ソフトウェアの新機能や提案があれば、お気軽にお問い合わせください。`Issues`で育ちました
このソフトウェアのデザインアイデアも気に入っていただけましたら、ぜひ `PR` を送信してください、 ご支援ありがとうございました!
## 私に連絡して
私のメールアドレスにご連絡いただけます<a href="mailto:zyronon@163.com">zyronon@163.com</a>
> 私の他のオープンソース プロジェクトを共有してください:
>
> _[**単語を入力する**Web上で使える単語暗記ソフト](https://github.com/zyronon/typing-word)<img src="https://img.shields.io/github/stars/zyronon/typing-word.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
> _[**ウェブスクリプト**- いくつかの便利な Grease Monkey スクリプト~](https://github.com/zyronon/web-scripts)<img src="https://img.shields.io/github/stars/zyronon/web-scripts.svg?style=flat-square&label=Star&color=4285dd&logo=github" height="16px" />_
## 合意
[GPL](../LICENSE)

7
env.d.ts vendored
View File

@ -1,8 +1,15 @@
/// <reference types="vite/client" />
/// <reference types="unplugin-vue-macros/macros-global" />
declare const LATEST_COMMIT_HASH: string
declare global {
interface Window {
isMoved: boolean
isMuted: boolean
showMutedNotice: boolean
}
interface Navigator {
control: any
webkitGetUserMedia: any

View File

@ -1,8 +0,0 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}

View File

@ -5734,7 +5734,7 @@
"user_id": "1028766474441803",
"sec_uid": "MS4wLjABAAAAonK7FndgFYn4mKBQwHc34iEiCCwvBI3tXNqGXqd18qFM9p_ZSxC1y9Gyv1e0XuG_",
"short_user_id": "3643612610",
"user_unique_id": "LL991221.z",
"user_unique_id": "LL991221",
"user_signature": "🧣我才是岚岚s\nX Eva: 我才是岚岚\n仅此一个抖音号谨防被骗",
"nickname": "我才是岚岚",
"avatar": "https://p3-pc.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-avt-0015_78ecf820d31560e298e32684589c00b3.jpeg?from=2956013662",
@ -5954,7 +5954,7 @@
"user_id": "1028766474441803",
"sec_uid": "MS4wLjABAAAAonK7FndgFYn4mKBQwHc34iEiCCwvBI3tXNqGXqd18qFM9p_ZSxC1y9Gyv1e0XuG_",
"short_user_id": "3643612610",
"user_unique_id": "LL991221.z",
"user_unique_id": "LL991221",
"user_signature": "🧣我才是岚岚s\nX Eva: 我才是岚岚\n仅此一个抖音号谨防被骗",
"nickname": "我才是岚岚",
"avatar": "https://p3-pc.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-avt-0015_78ecf820d31560e298e32684589c00b3.jpeg?from=2956013662",

View File

@ -1,392 +1,346 @@
lockfileVersion: '6.0'
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
axios:
specifier: ^1.6.7
version: 1.6.7
nanoid:
specifier: ^5.0.6
version: 5.0.6
request:
specifier: ^2.88.2
version: 2.88.2
importers:
.:
dependencies:
axios:
specifier: ^1.6.7
version: 1.7.4
nanoid:
specifier: ^5.0.6
version: 5.0.6
request:
specifier: ^2.88.2
version: 2.88.2
packages:
/ajv@6.12.6:
resolution:
{
integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==,
}
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
asn1@0.2.6:
resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
assert-plus@1.0.0:
resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
engines: {node: '>=0.8'}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
aws-sign2@0.7.0:
resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
aws4@1.12.0:
resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==}
axios@1.7.4:
resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==}
bcrypt-pbkdf@1.0.2:
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
dashdash@1.14.1:
resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
engines: {node: '>=0.10'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
ecc-jsbn@0.1.2:
resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
extsprintf@1.3.0:
resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==}
engines: {'0': node >=0.6.0}
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
follow-redirects@1.15.6:
resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
forever-agent@0.6.1:
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
form-data@2.3.3:
resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
engines: {node: '>= 0.12'}
form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
getpass@0.1.7:
resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==}
har-schema@2.0.0:
resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==}
engines: {node: '>=4'}
har-validator@5.1.5:
resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==}
engines: {node: '>=6'}
deprecated: this library is no longer supported
http-signature@1.2.0:
resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
engines: {node: '>=0.8', npm: '>=1.3.7'}
is-typedarray@1.0.0:
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
isstream@0.1.2:
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
jsbn@0.1.1:
resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
json-schema@0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
json-stringify-safe@5.0.1:
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
jsprim@1.4.2:
resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
engines: {node: '>=0.6.0'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
nanoid@5.0.6:
resolution: {integrity: sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==}
engines: {node: ^18 || >=20}
hasBin: true
oauth-sign@0.9.0:
resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
psl@1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
qs@6.5.3:
resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
engines: {node: '>=0.6'}
request@2.88.2:
resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
engines: {node: '>= 6'}
deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
sshpk@1.18.0:
resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==}
engines: {node: '>=0.10.0'}
hasBin: true
tough-cookie@2.5.0:
resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
engines: {node: '>=0.8'}
tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
tweetnacl@0.14.5:
resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
uuid@3.4.0:
resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
hasBin: true
verror@1.10.0:
resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
engines: {'0': node >=0.6.0}
snapshots:
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
fast-json-stable-stringify: 2.1.0
json-schema-traverse: 0.4.1
uri-js: 4.4.1
dev: false
/asn1@0.2.6:
resolution:
{
integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==,
}
asn1@0.2.6:
dependencies:
safer-buffer: 2.1.2
dev: false
/assert-plus@1.0.0:
resolution:
{
integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==,
}
engines: { node: '>=0.8' }
dev: false
assert-plus@1.0.0: {}
/asynckit@0.4.0:
resolution:
{
integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==,
}
dev: false
asynckit@0.4.0: {}
/aws-sign2@0.7.0:
resolution:
{
integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==,
}
dev: false
aws-sign2@0.7.0: {}
/aws4@1.12.0:
resolution:
{
integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==,
}
dev: false
aws4@1.12.0: {}
/axios@1.6.7:
resolution:
{
integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==,
}
axios@1.7.4:
dependencies:
follow-redirects: 1.15.6
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
dev: false
/bcrypt-pbkdf@1.0.2:
resolution:
{
integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==,
}
bcrypt-pbkdf@1.0.2:
dependencies:
tweetnacl: 0.14.5
dev: false
/caseless@0.12.0:
resolution:
{
integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==,
}
dev: false
caseless@0.12.0: {}
/combined-stream@1.0.8:
resolution:
{
integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==,
}
engines: { node: '>= 0.8' }
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
dev: false
/core-util-is@1.0.2:
resolution:
{
integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==,
}
dev: false
core-util-is@1.0.2: {}
/dashdash@1.14.1:
resolution:
{
integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==,
}
engines: { node: '>=0.10' }
dashdash@1.14.1:
dependencies:
assert-plus: 1.0.0
dev: false
/delayed-stream@1.0.0:
resolution:
{
integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==,
}
engines: { node: '>=0.4.0' }
dev: false
delayed-stream@1.0.0: {}
/ecc-jsbn@0.1.2:
resolution:
{
integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==,
}
ecc-jsbn@0.1.2:
dependencies:
jsbn: 0.1.1
safer-buffer: 2.1.2
dev: false
/extend@3.0.2:
resolution:
{
integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==,
}
dev: false
extend@3.0.2: {}
/extsprintf@1.3.0:
resolution:
{
integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==,
}
engines: { '0': node >=0.6.0 }
dev: false
extsprintf@1.3.0: {}
/fast-deep-equal@3.1.3:
resolution:
{
integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==,
}
dev: false
fast-deep-equal@3.1.3: {}
/fast-json-stable-stringify@2.1.0:
resolution:
{
integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==,
}
dev: false
fast-json-stable-stringify@2.1.0: {}
/follow-redirects@1.15.6:
resolution:
{
integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==,
}
engines: { node: '>=4.0' }
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dev: false
follow-redirects@1.15.6: {}
/forever-agent@0.6.1:
resolution:
{
integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==,
}
dev: false
forever-agent@0.6.1: {}
/form-data@2.3.3:
resolution:
{
integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==,
}
engines: { node: '>= 0.12' }
form-data@2.3.3:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/form-data@4.0.0:
resolution:
{
integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==,
}
engines: { node: '>= 6' }
form-data@4.0.0:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/getpass@0.1.7:
resolution:
{
integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==,
}
getpass@0.1.7:
dependencies:
assert-plus: 1.0.0
dev: false
/har-schema@2.0.0:
resolution:
{
integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==,
}
engines: { node: '>=4' }
dev: false
har-schema@2.0.0: {}
/har-validator@5.1.5:
resolution:
{
integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==,
}
engines: { node: '>=6' }
deprecated: this library is no longer supported
har-validator@5.1.5:
dependencies:
ajv: 6.12.6
har-schema: 2.0.0
dev: false
/http-signature@1.2.0:
resolution:
{
integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==,
}
engines: { node: '>=0.8', npm: '>=1.3.7' }
http-signature@1.2.0:
dependencies:
assert-plus: 1.0.0
jsprim: 1.4.2
sshpk: 1.18.0
dev: false
/is-typedarray@1.0.0:
resolution:
{
integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==,
}
dev: false
is-typedarray@1.0.0: {}
/isstream@0.1.2:
resolution:
{
integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==,
}
dev: false
isstream@0.1.2: {}
/jsbn@0.1.1:
resolution:
{
integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==,
}
dev: false
jsbn@0.1.1: {}
/json-schema-traverse@0.4.1:
resolution:
{
integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==,
}
dev: false
json-schema-traverse@0.4.1: {}
/json-schema@0.4.0:
resolution:
{
integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==,
}
dev: false
json-schema@0.4.0: {}
/json-stringify-safe@5.0.1:
resolution:
{
integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==,
}
dev: false
json-stringify-safe@5.0.1: {}
/jsprim@1.4.2:
resolution:
{
integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==,
}
engines: { node: '>=0.6.0' }
jsprim@1.4.2:
dependencies:
assert-plus: 1.0.0
extsprintf: 1.3.0
json-schema: 0.4.0
verror: 1.10.0
dev: false
/mime-db@1.52.0:
resolution:
{
integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==,
}
engines: { node: '>= 0.6' }
dev: false
mime-db@1.52.0: {}
/mime-types@2.1.35:
resolution:
{
integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==,
}
engines: { node: '>= 0.6' }
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
dev: false
/nanoid@5.0.6:
resolution:
{
integrity: sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==,
}
engines: { node: ^18 || >=20 }
hasBin: true
dev: false
nanoid@5.0.6: {}
/oauth-sign@0.9.0:
resolution:
{
integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==,
}
dev: false
oauth-sign@0.9.0: {}
/performance-now@2.1.0:
resolution:
{
integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==,
}
dev: false
performance-now@2.1.0: {}
/proxy-from-env@1.1.0:
resolution:
{
integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==,
}
dev: false
proxy-from-env@1.1.0: {}
/psl@1.9.0:
resolution:
{
integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==,
}
dev: false
psl@1.9.0: {}
/punycode@2.3.1:
resolution:
{
integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==,
}
engines: { node: '>=6' }
dev: false
punycode@2.3.1: {}
/qs@6.5.3:
resolution:
{
integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==,
}
engines: { node: '>=0.6' }
dev: false
qs@6.5.3: {}
/request@2.88.2:
resolution:
{
integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==,
}
engines: { node: '>= 6' }
deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
request@2.88.2:
dependencies:
aws-sign2: 0.7.0
aws4: 1.12.0
@ -408,29 +362,12 @@ packages:
tough-cookie: 2.5.0
tunnel-agent: 0.6.0
uuid: 3.4.0
dev: false
/safe-buffer@5.2.1:
resolution:
{
integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==,
}
dev: false
safe-buffer@5.2.1: {}
/safer-buffer@2.1.2:
resolution:
{
integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==,
}
dev: false
safer-buffer@2.1.2: {}
/sshpk@1.18.0:
resolution:
{
integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==,
}
engines: { node: '>=0.10.0' }
hasBin: true
sshpk@1.18.0:
dependencies:
asn1: 0.2.6
assert-plus: 1.0.0
@ -441,61 +378,26 @@ packages:
jsbn: 0.1.1
safer-buffer: 2.1.2
tweetnacl: 0.14.5
dev: false
/tough-cookie@2.5.0:
resolution:
{
integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==,
}
engines: { node: '>=0.8' }
tough-cookie@2.5.0:
dependencies:
psl: 1.9.0
punycode: 2.3.1
dev: false
/tunnel-agent@0.6.0:
resolution:
{
integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==,
}
tunnel-agent@0.6.0:
dependencies:
safe-buffer: 5.2.1
dev: false
/tweetnacl@0.14.5:
resolution:
{
integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==,
}
dev: false
tweetnacl@0.14.5: {}
/uri-js@4.4.1:
resolution:
{
integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==,
}
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
dev: false
/uuid@3.4.0:
resolution:
{
integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==,
}
deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
hasBin: true
dev: false
uuid@3.4.0: {}
/verror@1.10.0:
resolution:
{
integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==,
}
engines: { '0': node >=0.6.0 }
verror@1.10.0:
dependencies:
assert-plus: 1.0.0
core-util-is: 1.0.2
extsprintf: 1.3.0
dev: false

33
node/remove-dist-json.js Normal file
View File

@ -0,0 +1,33 @@
import fs from 'fs'
import path from 'path'
let read = 'dist/data'
function deleteFile(url, name) {
var files = []
if (fs.existsSync(url)) {
//判断给定的路径是否存在
files = fs.readdirSync(url) //返回文件和子目录的数组
files.forEach(function (file) {
var curPath = path.join(url, file)
if (fs.statSync(curPath).isDirectory()) {
//同步读取文件夹文件,如果是文件夹,则函数回调
deleteFile(curPath, name)
} else {
if (file.indexOf(name) > -1) {
//是指定文件,则删除
fs.unlinkSync(curPath)
console.log('删除文件:' + curPath)
}
}
})
} else {
console.log('给定的路径不存在!')
}
}
deleteFile(read, '.json')

View File

@ -727,7 +727,7 @@ export const users = [
signature: '🧣我才是岚岚s\nX Eva: 我才是岚岚\n仅此一个抖音号谨防被骗',
total_favorited: 16475958,
uid: '1028766474441803',
unique_id: 'LL991221.z',
unique_id: 'LL991221',
user_age: 23,
white_cover_url: [
{

View File

@ -693,7 +693,7 @@
"signature": "🧣我才是岚岚s\nX Eva: 我才是岚岚\n仅此一个抖音号谨防被骗",
"total_favorited": 16475958,
"uid": "1028766474441803",
"unique_id": "LL991221.z",
"unique_id": "LL991221",
"user_age": 23,
"white_cover_url": [
{

View File

@ -2,20 +2,22 @@
"name": "douyin-vue",
"version": "1.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --host",
"start": "vite --host",
"serve": "vite --host",
"build": "vite build --mode prod",
"build": "vite build",
"test-del-json": "node node/remove-dist-json.js",
"build-uni-app": "vite build --mode uni",
"build-gp-pages": "vite build --mode gp_pages",
"build-gitee-pages": "vite build --mode gitee_pages",
"build-gitee-pages": "vite build --mode gitee_pages && pnpm run test-del-json",
"build-only": "vite build",
"build-check": "run-p type-check \"build-only {@}\" --",
"type-check": "vue-tsc --build --force",
"report": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"lint": "eslint --fix . --ext .vue,.js,.ts,.tsx,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"format": "prettier --write src/",
"prepare": "husky",
"commit": "git-cz"
@ -25,28 +27,20 @@
"axios": "^1.6.8",
"axios-mock-adapter": "^1.22.0",
"core-js": "3.21.1",
"dayjs": "1.11.0",
"gl-matrix": "3.4.3",
"jquery": "^3.7.1",
"mitt": "3.0.0",
"mobile-select": "1.1.2",
"libarchive-wasm": "^1.1.0",
"mockjs": "^1.1.0",
"pinia": "^2.1.7",
"vue": "3.4.21",
"vue-router": "4.3.0",
"vue-switches": "2.0.1",
"libarchive-wasm": "^1.1.0"
"vue-router": "4.3.0"
},
"devDependencies": {
"@commitlint/cli": "^19.2.1",
"@commitlint/config-conventional": "^19.1.0",
"@iconify/vue": "^4.1.1",
"@rollup/plugin-commonjs": "^25.0.7",
"@rushstack/eslint-patch": "^1.3.3",
"@tsconfig/node20": "^20.1.2",
"@types/jquery": "3.5.29",
"@types/node": "^20.11.28",
"@vitejs/plugin-vue": "4.0.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
@ -55,18 +49,17 @@
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.17.0",
"git-last-commit": "^1.0.1",
"husky": "^9.0.11",
"less": "4.1.3",
"lint-staged": "^15.2.2",
"prettier": "^3.2.5",
"rollup-plugin-visualizer": "^5.9.2",
"typescript": "5.3.3",
"unplugin-vue-define-options": "^1.4.1",
"vite": "^5.1.7",
"unplugin-vue-macros": "^2.9.1",
"vite": "^5.2.14",
"vite-plugin-cdn-import": "0.3.5",
"vite-plugin-commonjs": "^0.10.1",
"vue-tsc": "^2.0.6",
"git-last-commit": "^1.0.1"
"vue-tsc": "^2.0.6"
},
"lint-staged": {
"*.{js,ts,vue,jsx,tsx}": [

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
"user_id": "1028766474441803",
"sec_uid": "MS4wLjABAAAAonK7FndgFYn4mKBQwHc34iEiCCwvBI3tXNqGXqd18qFM9p_ZSxC1y9Gyv1e0XuG_",
"short_user_id": "3643612610",
"user_unique_id": "LL991221.z",
"user_unique_id": "LL991221",
"user_signature": "🧣我才是岚岚s\nX Eva: 我才是岚岚\n仅此一个抖音号谨防被骗",
"nickname": "我才是岚岚",
"avatar": "https://p3-pc.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-avt-0015_78ecf820d31560e298e32684589c00b3.jpeg?from=2956013662",
@ -168,7 +168,7 @@
"user_id": "1028766474441803",
"sec_uid": "MS4wLjABAAAAonK7FndgFYn4mKBQwHc34iEiCCwvBI3tXNqGXqd18qFM9p_ZSxC1y9Gyv1e0XuG_",
"short_user_id": "3643612610",
"user_unique_id": "LL991221.z",
"user_unique_id": "LL991221",
"user_signature": "🧣我才是岚岚s\nX Eva: 我才是岚岚\n仅此一个抖音号谨防被骗",
"nickname": "我才是岚岚",
"avatar": "https://p3-pc.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-avt-0015_78ecf820d31560e298e32684589c00b3.jpeg?from=2956013662",

View File

@ -137,6 +137,9 @@
},
"interact_info": { "liked_count": "2147", "liked": false },
"cover": { "url_default": "daZ662BX0lVMTq0Y6hIwR.png" },
"image_list": [
{ "info_list": [{ "url": "daZ662BX0lVMTq0Y6hIwR.png" }] }
],
"type": "normal",
"display_title": "无所谓了,互联网没有我在乎的人"
},
@ -176,7 +179,10 @@
"nickname": "芙•"
},
"interact_info": { "liked": false, "liked_count": "1966" },
"cover": { "url_default": "4a4TtExpq7RXnKK3hylKU.png" }
"cover": { "url_default": "4a4TtExpq7RXnKK3hylKU.png" },
"image_list": [
{ "info_list": [{ "url": "4a4TtExpq7RXnKK3hylKU.png" }] }
]
},
"track_id": "2d0udv0xvhqne0t0nmd83",
"ignore": false,
@ -233,6 +239,9 @@
},
"interact_info": { "liked": false, "liked_count": "1005" },
"cover": { "url_default": "dg16eh25m2SSI9Hc1fJuE.png" },
"image_list": [
{ "info_list": [{ "url": "dg16eh25m2SSI9Hc1fJuE.png" }] }
],
"type": "normal",
"display_title": "拍旗袍没人看 办公室随手拍就有流量是吧"
},
@ -321,7 +330,10 @@
"nick_name": "林木婷子"
},
"interact_info": { "liked": false, "liked_count": "2700" },
"cover": { "url_default": "z-XSh-g9MhKpYrFDHcxQT.png" }
"cover": { "url_default": "z-XSh-g9MhKpYrFDHcxQT.png" },
"image_list": [
{ "info_list": [{ "url": "z-XSh-g9MhKpYrFDHcxQT.png" }] }
]
},
"track_id": "2d0udv0xvhqne0t0nmd83"
},
@ -629,7 +641,10 @@
"user_id": "5bf37f6651783a194c1e1da2"
},
"interact_info": { "liked": false, "liked_count": "119" },
"cover": { "url_default": "UaxqkI4aZ5LDu7k8KLw48.png" }
"cover": { "url_default": "UaxqkI4aZ5LDu7k8KLw48.png" },
"image_list": [
{ "info_list": [{ "url": "UaxqkI4aZ5LDu7k8KLw48.png" }] }
]
}
},
{
@ -888,6 +903,7 @@
},
"interact_info": { "liked": false, "liked_count": "1786" },
"cover": { "url_default": "n0SAcEY5gLucU7Ik7yP2P.png" },
"image_list": [{ "info_list": [{ "url": "n0SAcEY5gLucU7Ik7yP2P.png" }] }],
"type": "normal"
},
"track_id": "2d0udv0xvhqne0t0nmd83",
@ -936,6 +952,9 @@
"model_type": "note",
"note_card": {
"cover": { "url_default": "Sf5mzf68e2GwZHZv7h1G2.png" },
"image_list": [
{ "info_list": [{ "url": "Sf5mzf68e2GwZHZv7h1G2.png" }] }
],
"type": "normal",
"display_title": "行吧,我重发 ",
"user": {

Binary file not shown.

View File

@ -400,163 +400,6 @@
]
}
},
{
"aweme_id": "7346191904205327631",
"desc": "我的女友",
"create_time": 1710418599,
"music": {
"id": 7346191940615933000,
"title": "@条子创作的原声",
"author": "条子",
"cover_medium": {
"uri": "720x720/aweme-avatar/tos-cn-avt-0015_75c5ef8973e1d665252306ea58f7d10b",
"url_list": [
"https://p3-pc.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-avt-0015_75c5ef8973e1d665252306ea58f7d10b.jpeg?from=116350172"
],
"width": 720,
"height": 720
},
"cover_thumb": {
"uri": "100x100/aweme-avatar/tos-cn-avt-0015_75c5ef8973e1d665252306ea58f7d10b",
"url_list": [
"https://p3-pc.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-avt-0015_75c5ef8973e1d665252306ea58f7d10b.jpeg?from=116350172"
],
"width": 720,
"height": 720
},
"play_url": {
"uri": "https://sf5-hl-cdn-tos.douyinstatic.com/obj/ies-music/7346191933737356059.mp3",
"url_list": [
"https://sf5-hl-cdn-tos.douyinstatic.com/obj/ies-music/7346191933737356059.mp3",
"https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/7346191933737356059.mp3"
],
"width": 720,
"height": 720,
"url_key": "7346191940615932722"
},
"duration": 7,
"user_count": 0,
"owner_id": "83160533866",
"owner_nickname": "条子",
"is_original": false
},
"video": {
"play_addr": {
"uri": "v0300fg10000cnpel5bc77u9pqctt5hg",
"url_list": [
"https://www.douyin.com/aweme/v1/play/?video_id=v0300fg10000cnpel5bc77u9pqctt5hg&line=0&file_id=0ab371e7a5ac419fb88d8b616f1b9a70&sign=c689ddfb4d16aec5ae605822e5b2c4f6&is_play_url=1&source=PackSourceEnum_PUBLISH"
],
"width": 1870,
"height": 1052,
"url_key": "v0300fg10000cnpel5bc77u9pqctt5hg_h264_1080p_2690491",
"data_size": 2467517,
"file_hash": "c689ddfb4d16aec5ae605822e5b2c4f6",
"file_cs": "c:0-7442-daae|d:0-1233757-8a1a,1233758-2467516-f27e|a:v0300fg10000cnpel5bc77u9pqctt5hg"
},
"cover": {
"uri": "tos-cn-i-0813/owgAVhXilAAZ6yTAAzCsDnqefAVEDIN9qALqyg",
"url_list": ["Ig9gQdI0FjieZ_SnquVR-.png"],
"width": 720,
"height": 720
},
"height": 1052,
"width": 1870,
"ratio": "1080p",
"use_static_cover": true,
"duration": 7337,
"horizontal_type": 1
},
"share_url": "https://www.iesdouyin.com/share/video/7346191904205327631/?region=CN&mid=7346191940615932722&u_code=13kgm680k&did=MS4wLjABAAAAiOgYyZm8XbWZMr5o3OvhR-TEOuNygb_hQOwkie-VXJpDYaR4vZfpiIGBfAWKCFHB&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ&with_sec_did=1&titleType=title&share_sign=GBtbcDEKOakwrzsCjMztEGgSeaHW_xrERQNESLGFXFw-&share_version=170400&ts=1710489511&from_aid=6383&from_ssr=1",
"statistics": {
"admire_count": 1,
"comment_count": 602,
"digg_count": 17913,
"collect_count": 2393,
"play_count": 0,
"share_count": 13227
},
"status": {
"listen_video_status": 0,
"is_delete": false,
"allow_share": true,
"is_prohibited": false,
"in_reviewing": false,
"part_see": 0,
"private_status": 0,
"review_result": {
"review_status": 0
}
},
"text_extra": [],
"is_top": 0,
"share_info": {
"share_url": "https://www.iesdouyin.com/share/video/7346191904205327631/?region=CN&mid=7346191940615932722&u_code=13kgm680k&did=MS4wLjABAAAAiOgYyZm8XbWZMr5o3OvhR-TEOuNygb_hQOwkie-VXJpDYaR4vZfpiIGBfAWKCFHB&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ&with_sec_did=1&titleType=title&share_sign=GBtbcDEKOakwrzsCjMztEGgSeaHW_xrERQNESLGFXFw-&share_version=170400&ts=1710489511&from_aid=6383&from_ssr=1",
"share_link_desc": "2.89 G@V.LW mDU:/ 11/22 我的女友 %s 复制此链接打开Dou音搜索直接观看视频"
},
"duration": 7337,
"image_infos": null,
"risk_infos": {
"vote": false,
"warn": false,
"risk_sink": false,
"type": 0,
"content": ""
},
"position": null,
"author_user_id": 83160533866,
"prevent_download": false,
"long_video": null,
"aweme_control": {
"can_forward": true,
"can_share": true,
"can_comment": true,
"can_show_comment": true
},
"images": null,
"suggest_words": {
"suggest_words": [
{
"words": [
{
"word": "条子被黑社会堵到超市后续",
"word_id": "7129083023886554400",
"info": "{\"qrec_for_search\":\"{}\"}"
}
],
"scene": "comment_top_rec",
"icon_url": "",
"hint_text": "大家都在搜:",
"extra_info": "{}"
},
{
"words": [
{
"word": "埃安y plus70乐享版",
"word_id": "7147998601692026127",
"info": "{\"qrec_for_search\":\"{\\\"query_ecom\\\":\\\"1\\\"}\"}"
}
],
"scene": "feed_bottom_rec",
"icon_url": "",
"hint_text": "相关搜索",
"extra_info": "{}"
},
{
"words": [
{
"word": "条子被黑社会堵到超市后续",
"word_id": "7129083023886554400",
"info": "{\"qrec_for_search\":\"{}\"}"
}
],
"scene": "detail_inbox_rex",
"icon_url": "",
"hint_text": "",
"extra_info": "{}"
}
]
}
},
{
"aweme_id": "7345439525113433384",
"desc": "",

View File

@ -455,160 +455,6 @@
]
}
},
{
"aweme_id": "7346457849054235913",
"desc": "#小蛮腰马甲线 他居然把脸转过去",
"create_time": 1710480523,
"music": {
"id": 7346457898803154000,
"title": "@奶茶妹◕🌱创作的原声",
"author": "奶茶妹◕🌱",
"cover_medium": {
"uri": "720x720/aweme-avatar/tos-cn-avt-0015_c7784af3a0bd2e7c5fc92ece450f12e0",
"url_list": [
"https://p3-pc.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-avt-0015_c7784af3a0bd2e7c5fc92ece450f12e0.jpeg?from=116350172"
],
"width": 720,
"height": 720
},
"cover_thumb": {
"uri": "100x100/aweme-avatar/tos-cn-avt-0015_c7784af3a0bd2e7c5fc92ece450f12e0",
"url_list": [
"https://p3-pc.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-avt-0015_c7784af3a0bd2e7c5fc92ece450f12e0.jpeg?from=116350172"
],
"width": 720,
"height": 720
},
"play_url": {
"uri": "https://sf5-hl-cdn-tos.douyinstatic.com/obj/ies-music/7346457902686882570.mp3",
"url_list": [
"https://sf5-hl-cdn-tos.douyinstatic.com/obj/ies-music/7346457902686882570.mp3",
"https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/7346457902686882570.mp3"
],
"width": 720,
"height": 720,
"url_key": "7346457898803153673"
},
"duration": 11,
"user_count": 0,
"owner_id": "62790495105",
"owner_nickname": "奶茶妹◕🌱",
"is_original": false
},
"video": {
"play_addr": {
"uri": "v0d00fg10000cnptoirc77u3atmcsskg",
"url_list": [
"https://www.douyin.com/aweme/v1/play/?video_id=v0d00fg10000cnptoirc77u3atmcsskg&line=0&file_id=3b2bcdfe44f0468aacc265bb778c2e2c&sign=fccec71c7a910d752161cb82b89c8474&is_play_url=1&source=PackSourceEnum_PUBLISH"
],
"width": 1080,
"height": 1920,
"url_key": "v0d00fg10000cnptoirc77u3atmcsskg_h264_1080p_2062873",
"data_size": 3017468,
"file_hash": "fccec71c7a910d752161cb82b89c8474",
"file_cs": "c:0-10842-2631|d:0-1508733-7add,1508734-3017467-357f|a:v0d00fg10000cnptoirc77u3atmcsskg"
},
"cover": {
"uri": "tos-cn-i-0813c001/oIzsg8zAAIlAfDH5ANtC9BE6IANk3eAyAhAn0E",
"url_list": ["R3o8KAoPnLSiZLkSs66H3.png"],
"width": 720,
"height": 720
},
"height": 1920,
"width": 1080,
"ratio": "1080p",
"use_static_cover": true,
"duration": 11702
},
"share_url": "https://www.iesdouyin.com/share/video/7346457849054235913/?region=CN&mid=7346457898803153673&u_code=13kgm680k&did=MS4wLjABAAAAiOgYyZm8XbWZMr5o3OvhR-TEOuNygb_hQOwkie-VXJpDYaR4vZfpiIGBfAWKCFHB&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ&with_sec_did=1&titleType=title&share_sign=KmI.XiEwwiCE.iLBUp3QNtoJoUyXFirMExGYSa9SBI8-&share_version=170400&ts=1710488702&from_aid=6383&from_ssr=1",
"statistics": {
"admire_count": 0,
"comment_count": 20,
"digg_count": 1572,
"collect_count": 137,
"play_count": 0,
"share_count": 112
},
"status": {
"listen_video_status": 0,
"is_delete": false,
"allow_share": true,
"is_prohibited": false,
"in_reviewing": false,
"part_see": 0,
"private_status": 0,
"review_result": {
"review_status": 0
}
},
"text_extra": [
{
"start": 0,
"end": 7,
"type": 1,
"hashtag_name": "小蛮腰马甲线",
"hashtag_id": "1622790756032686",
"is_commerce": false,
"caption_start": 0,
"caption_end": 7
}
],
"is_top": 0,
"share_info": {
"share_url": "https://www.iesdouyin.com/share/video/7346457849054235913/?region=CN&mid=7346457898803153673&u_code=13kgm680k&did=MS4wLjABAAAAiOgYyZm8XbWZMr5o3OvhR-TEOuNygb_hQOwkie-VXJpDYaR4vZfpiIGBfAWKCFHB&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ&with_sec_did=1&titleType=title&share_sign=KmI.XiEwwiCE.iLBUp3QNtoJoUyXFirMExGYSa9SBI8-&share_version=170400&ts=1710488702&from_aid=6383&from_ssr=1",
"share_link_desc": "8.99 09/14 P@X.MJ dAT:/ # 小蛮腰马甲线 他居然把脸转过去 %s 复制此链接打开Dou音搜索直接观看视频"
},
"duration": 11702,
"image_infos": null,
"risk_infos": {
"vote": false,
"warn": false,
"risk_sink": false,
"type": 0,
"content": ""
},
"position": null,
"author_user_id": 62790495105,
"prevent_download": false,
"long_video": null,
"aweme_control": {
"can_forward": true,
"can_share": true,
"can_comment": true,
"can_show_comment": true
},
"images": null,
"suggest_words": {
"suggest_words": [
{
"words": [
{
"word": "章若楠",
"word_id": "6585508016810890499",
"info": "{\"qrec_for_search\":\"{}\"}"
}
],
"scene": "comment_top_rec",
"icon_url": "",
"hint_text": "大家都在搜:",
"extra_info": "{}"
},
{
"words": [
{
"word": "章若楠",
"word_id": "6585508016810890499",
"info": "{\"qrec_for_search\":\"{}\"}"
}
],
"scene": "detail_inbox_rex",
"icon_url": "",
"hint_text": "",
"extra_info": "{}"
}
]
}
},
{
"aweme_id": "7346190579807702310",
"desc": "#背影杀 哈~",

View File

@ -449,184 +449,6 @@
]
}
},
{
"aweme_id": "7345796955571784997",
"desc": "体验调酒师的一天🍸 Bartender\n#微醺时刻 #记录生活",
"create_time": 1710326646,
"music": {
"id": 7023044224681511000,
"title": "Starboy (抖音热播)",
"author": "佐助",
"cover_medium": {
"uri": "tos-cn-v-2774c002/b3583f56f54e4451b7d301187585bcdb",
"url_list": [
"https://p11.douyinpic.com/aweme/200x200/tos-cn-v-2774c002/b3583f56f54e4451b7d301187585bcdb.jpeg",
"https://p3.douyinpic.com/aweme/200x200/tos-cn-v-2774c002/b3583f56f54e4451b7d301187585bcdb.jpeg"
],
"width": 720,
"height": 720
},
"cover_thumb": {
"uri": "tos-cn-v-2774c002/b3583f56f54e4451b7d301187585bcdb",
"url_list": [
"https://p11.douyinpic.com/aweme/100x100/tos-cn-v-2774c002/b3583f56f54e4451b7d301187585bcdb.jpeg",
"https://p3.douyinpic.com/aweme/100x100/tos-cn-v-2774c002/b3583f56f54e4451b7d301187585bcdb.jpeg"
],
"width": 720,
"height": 720
},
"play_url": {
"uri": "https://sf5-hl-cdn-tos.douyinstatic.com/obj/tos-cn-ve-2774/ogjEQCutCnDe2gAG2KGBb8ieUACWSoFhZRgMQg",
"url_list": [
"https://sf5-hl-cdn-tos.douyinstatic.com/obj/tos-cn-ve-2774/ogjEQCutCnDe2gAG2KGBb8ieUACWSoFhZRgMQg",
"https://sf3-cdn-tos.douyinstatic.com/obj/tos-cn-ve-2774/ogjEQCutCnDe2gAG2KGBb8ieUACWSoFhZRgMQg"
],
"width": 720,
"height": 720,
"url_key": "7023044224681510949"
},
"duration": 194,
"user_count": 0,
"owner_nickname": "",
"is_original": false
},
"video": {
"play_addr": {
"uri": "v0d00fg10000cnoo6mjc77ubta3125dg",
"url_list": [
"https://www.douyin.com/aweme/v1/play/?video_id=v0d00fg10000cnoo6mjc77ubta3125dg&line=0&file_id=b4fc9157732e4eee81b0d3d071b4d32f&sign=8057edcee0fd27ffce9615b1048d2e2c&is_play_url=1&source=PackSourceEnum_PUBLISH"
],
"width": 1080,
"height": 1920,
"url_key": "v0d00fg10000cnoo6mjc77ubta3125dg_h264_1080p_2264177",
"data_size": 5443649,
"file_hash": "8057edcee0fd27ffce9615b1048d2e2c",
"file_cs": "c:0-17318-d0c8|d:0-2721823-7d6d,2721824-5443648-e625|a:v0d00fg10000cnoo6mjc77ubta3125dg"
},
"cover": {
"uri": "tos-cn-i-0813c001/oo3M3ze0IAN67MAAJAghvhGARED9gAfElxC0BN",
"url_list": ["2ww4W1Pu86xeLjl07K28u.png"],
"width": 720,
"height": 720
},
"height": 1920,
"width": 1080,
"ratio": "1080p",
"use_static_cover": true,
"duration": 19234
},
"share_url": "https://www.iesdouyin.com/share/video/7345796955571784997/?region=CN&mid=7023044224681510949&u_code=13kgm680k&did=MS4wLjABAAAAiOgYyZm8XbWZMr5o3OvhR-TEOuNygb_hQOwkie-VXJpDYaR4vZfpiIGBfAWKCFHB&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ&with_sec_did=1&titleType=title&share_sign=XUuhtQ9LwR_2_xNHoNSw2nfWpq_SiDhebnwzUPGq.Yw-&share_version=170400&ts=1710491939&from_aid=6383&from_ssr=1",
"statistics": {
"admire_count": 59,
"comment_count": 1644,
"digg_count": 256919,
"collect_count": 5279,
"play_count": 0,
"share_count": 8961
},
"status": {
"listen_video_status": 0,
"is_delete": false,
"allow_share": true,
"is_prohibited": false,
"in_reviewing": false,
"part_see": 0,
"private_status": 0,
"review_result": {
"review_status": 0
}
},
"text_extra": [
{
"start": 21,
"end": 26,
"type": 1,
"hashtag_name": "微醺时刻",
"hashtag_id": "1647996185380875",
"is_commerce": false,
"caption_start": 21,
"caption_end": 26
},
{
"start": 27,
"end": 32,
"type": 1,
"hashtag_name": "记录生活",
"hashtag_id": "1767586437643341",
"is_commerce": false,
"caption_start": 27,
"caption_end": 32
}
],
"is_top": 0,
"share_info": {
"share_url": "https://www.iesdouyin.com/share/video/7345796955571784997/?region=CN&mid=7023044224681510949&u_code=13kgm680k&did=MS4wLjABAAAAiOgYyZm8XbWZMr5o3OvhR-TEOuNygb_hQOwkie-VXJpDYaR4vZfpiIGBfAWKCFHB&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ&with_sec_did=1&titleType=title&share_sign=XUuhtQ9LwR_2_xNHoNSw2nfWpq_SiDhebnwzUPGq.Yw-&share_version=170400&ts=1710491939&from_aid=6383&from_ssr=1",
"share_link_desc": "5.10 02/04 z@t.eo GvF:/ 体验调酒师的一天🍸 Bartender # 微醺时刻 # 记录生活 %s 复制此链接打开Dou音搜索直接观看视频"
},
"duration": 19234,
"image_infos": null,
"risk_infos": {
"vote": false,
"warn": false,
"risk_sink": false,
"type": 0,
"content": ""
},
"position": null,
"author_user_id": 24058267831,
"prevent_download": false,
"long_video": null,
"aweme_control": {
"can_forward": true,
"can_share": true,
"can_comment": true,
"can_show_comment": true
},
"images": null,
"suggest_words": {
"suggest_words": [
{
"words": [
{
"word": "冰淇淋kiki",
"word_id": "6732949666079446276",
"info": "{\"qrec_for_search\":\"{}\"}"
}
],
"scene": "comment_top_rec",
"icon_url": "",
"hint_text": "大家都在搜:",
"extra_info": "{}"
},
{
"words": [
{
"word": "调酒师工资一般是多少",
"word_id": "6572390336566400259",
"info": "{\"qrec_for_search\":\"{}\"}"
}
],
"scene": "feed_bottom_rec",
"icon_url": "",
"hint_text": "相关搜索",
"extra_info": "{}"
},
{
"words": [
{
"word": "冰淇淋kiki",
"word_id": "6732949666079446276",
"info": "{\"qrec_for_search\":\"{}\"}"
}
],
"scene": "detail_inbox_rex",
"icon_url": "",
"hint_text": "",
"extra_info": "{}"
}
]
}
},
{
"aweme_id": "7345057414233836850",
"desc": "开在酒瓶里的春天呀🌷🌸",

View File

@ -425,149 +425,6 @@
]
}
},
{
"aweme_id": "7338089394705894682",
"desc": "陈年老库存🐿️",
"create_time": 1708532099,
"music": {
"id": 7338089490520509000,
"title": "@周子然JingYi创作的原声",
"author": "周子然JingYi",
"cover_medium": {
"uri": "720x720/aweme-avatar/tos-cn-avt-0015_f59bfced5c6a3b56d152f1e0437f06ec",
"url_list": [
"https://p3-pc.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-avt-0015_f59bfced5c6a3b56d152f1e0437f06ec.jpeg?from=116350172"
],
"width": 720,
"height": 720
},
"cover_thumb": {
"uri": "100x100/aweme-avatar/tos-cn-avt-0015_f59bfced5c6a3b56d152f1e0437f06ec",
"url_list": [
"https://p3-pc.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-avt-0015_f59bfced5c6a3b56d152f1e0437f06ec.jpeg?from=116350172"
],
"width": 720,
"height": 720
},
"play_url": {
"uri": "https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/7338089476511550234.mp3",
"url_list": [
"https://sf6-cdn-tos.douyinstatic.com/obj/ies-music/7338089476511550234.mp3",
"https://sf5-hl-cdn-tos.douyinstatic.com/obj/ies-music/7338089476511550234.mp3"
],
"width": 720,
"height": 720,
"url_key": "7338089490520509235"
},
"duration": 9,
"user_count": 0,
"owner_id": "62839305427",
"owner_nickname": "周子然JingYi",
"is_original": false
},
"video": {
"play_addr": {
"uri": "v0300fg10000cnb217rc77uah3ri5o00",
"url_list": [
"https://www.douyin.com/aweme/v1/play/?video_id=v0300fg10000cnb217rc77uah3ri5o00&line=0&file_id=d0de6a0d7fc0498a812e8527e94a887a&sign=ab93708a219baa2ffde338e8bf652e0d&is_play_url=1&source=PackSourceEnum_PUBLISH"
],
"width": 1080,
"height": 1920,
"url_key": "v0300fg10000cnb217rc77uah3ri5o00_h264_1080p_2556391",
"data_size": 3078535,
"file_hash": "ab93708a219baa2ffde338e8bf652e0d",
"file_cs": "c:0-9310-07ec|d:0-1539266-16e6,1539267-3078534-e6cd|a:v0300fg10000cnb217rc77uah3ri5o00"
},
"cover": {
"uri": "tos-cn-i-0813c001/oAiCAVAbDWOleAAIEGYb9IengDrADCAjAW1pIA",
"url_list": ["7SFxN5HcILUxUdJe-EXfX.png"],
"width": 720,
"height": 720
},
"height": 3840,
"width": 2160,
"ratio": "1080p",
"use_static_cover": true,
"duration": 9634
},
"share_url": "https://www.iesdouyin.com/share/video/7338089394705894682/?region=CN&mid=7338089490520509235&u_code=13kgm680k&did=MS4wLjABAAAAiOgYyZm8XbWZMr5o3OvhR-TEOuNygb_hQOwkie-VXJpDYaR4vZfpiIGBfAWKCFHB&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ&with_sec_did=1&titleType=title&share_sign=ig8SDm7Oq1NBxQweWS4eMzrdjlHmjkr7WIazmFWKoZY-&share_version=170400&ts=1710490324&from_aid=6383&from_ssr=1",
"statistics": {
"admire_count": 2,
"comment_count": 149,
"digg_count": 6367,
"collect_count": 635,
"play_count": 0,
"share_count": 784
},
"status": {
"listen_video_status": 0,
"is_delete": false,
"allow_share": true,
"is_prohibited": false,
"in_reviewing": false,
"part_see": 0,
"private_status": 0,
"review_result": {
"review_status": 0
}
},
"text_extra": [],
"is_top": 0,
"share_info": {
"share_url": "https://www.iesdouyin.com/share/video/7338089394705894682/?region=CN&mid=7338089490520509235&u_code=13kgm680k&did=MS4wLjABAAAAiOgYyZm8XbWZMr5o3OvhR-TEOuNygb_hQOwkie-VXJpDYaR4vZfpiIGBfAWKCFHB&iid=MS4wLjABAAAANwkJuWIRFOzg5uCpDRpMj4OX-QryoDgn-yYlXQnRwQQ&with_sec_did=1&titleType=title&share_sign=ig8SDm7Oq1NBxQweWS4eMzrdjlHmjkr7WIazmFWKoZY-&share_version=170400&ts=1710490324&from_aid=6383&from_ssr=1",
"share_link_desc": "3.07 CUL:/ 05/15 n@Q.kp 陈年老库存🐿️ %s 复制此链接打开Dou音搜索直接观看视频"
},
"duration": 9634,
"image_infos": null,
"risk_infos": {
"vote": false,
"warn": false,
"risk_sink": false,
"type": 0,
"content": ""
},
"position": null,
"author_user_id": 62839305427,
"prevent_download": false,
"long_video": null,
"aweme_control": {
"can_forward": true,
"can_share": true,
"can_comment": true,
"can_show_comment": true
},
"images": null,
"suggest_words": {
"suggest_words": [
{
"words": [
{
"word": "周子然早期照片",
"word_id": "6900178207296673032",
"info": "{\"qrec_for_search\":\"{}\"}"
}
],
"scene": "comment_top_rec",
"icon_url": "",
"hint_text": "大家都在搜:",
"extra_info": "{}"
},
{
"words": [
{
"word": "我有个朋友周子然",
"word_id": "6925278082983957772",
"info": "{\"qrec_for_search\":\"{}\"}"
}
],
"scene": "detail_inbox_rex",
"icon_url": "",
"hint_text": "",
"extra_info": "{}"
}
]
}
},
{
"aweme_id": "7322483619946040585",
"desc": "🍦。我是难伺候的小雪糕\n没有礼貌随时在考虑化掉",

View File

@ -693,7 +693,7 @@
"signature": "🧣我才是岚岚s\nX Eva: 我才是岚岚\n仅此一个抖音号谨防被骗",
"total_favorited": 16475958,
"uid": "1028766474441803",
"unique_id": "LL991221.z",
"unique_id": "LL991221",
"user_age": 23,
"white_cover_url": [
{

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 61 KiB

BIN
public/images/out1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
public/images/out2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
public/images/out3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
public/images/out4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
public/images/out6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
public/images/out7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

BIN
public/images/out8.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
public/images/out9.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,21 +1,11 @@
<template>
<router-view v-slot="{ Component }">
<transition :name="transitionName">
<keep-alive :exclude="store.excludeRoutes">
<keep-alive :exclude="store.excludeNames">
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
<BaseMask v-if="!isMobile" />
<div v-if="!isMobile" class="guide">
<Icon icon="mynaui:danger-triangle" />
<div class="txt">
<h2>切换至手机模式才可正常使用</h2>
<h3>1. F12 调出控制台</h3>
<h3>2. Ctrl+Shift+M或点击下面图标</h3>
</div>
<img src="@/assets/img/guide.png" alt="" />
</div>
<Call />
</template>
<script setup lang="ts">
@ -34,7 +24,6 @@ import { BASE_URL } from '@/config'
const store = useBaseStore()
const route = useRoute()
const isMobile = ref(/Mobi|Android|iPhone/i.test(navigator.userAgent))
const transitionName = ref('go')
// watch $route 使
@ -42,7 +31,7 @@ watch(
() => route.path,
(to, from) => {
store.setMaskDialog({ state: false, mode: store.maskDialogMode })
//footer5
//tab
let noAnimation = [
'/',
'/home',
@ -64,27 +53,30 @@ watch(
}
)
function setVh() {
function resetVhAndPx() {
let vh = window.innerHeight * 0.01
document.documentElement.style.setProperty('--vh', `${vh}px`)
//document.documentElement.style.fontSize = document.documentElement.clientWidth / 375 + 'px'
}
onMounted(() => {
store.init()
setVh()
resetVhAndPx()
// resize 1vh
window.addEventListener('resize', () => {
location.href = BASE_URL + '/'
setVh()
resetVhAndPx()
})
//
document.onselectstart = new Function('return false') as any
})
</script>
<style lang="less">
@import './assets/less/index';
* {
user-select: none;
}
#app {
height: 100%;
width: 100%;
@ -92,32 +84,12 @@ onMounted(() => {
font-size: 14rem;
}
.guide {
color: white;
z-index: 999;
background: var(--active-main-bg);
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 16rem;
overflow: hidden;
text-align: center;
svg {
margin-top: 10rem;
font-size: 40rem;
color: red;
}
.txt {
text-align: left;
padding: 0 24rem;
}
img {
display: block;
width: 350rem;
@media screen and (min-width: 500px) {
#app {
width: 500px !important;
position: relative;
left: 50%;
transform: translateX(-50%);
}
}

View File

@ -1,29 +1,29 @@
import request from '../utils/request'
import { request } from '@/utils/request'
export function userinfo(params, data) {
export function userinfo(params?: any, data?: any) {
return request({ url: '/user/userinfo', method: 'get', params, data })
}
export function userVideoList(params, data) {
export function userVideoList(params?: any, data?: any) {
return request({ url: '/user/video_list', method: 'get', params, data })
}
export function panel(params, data) {
export function panel(params?: any, data?: any) {
return request({ url: '/user/panel', method: 'get', params, data })
}
export function friends(params, data) {
export function friends(params?: any, data?: any) {
return request({ url: '/user/friends', method: 'get', params, data })
}
export function userCollect(params, data) {
export function userCollect(params?: any, data?: any) {
return request({ url: '/user/collect', method: 'get', params, data })
}
export function recommendedPost(params, data) {
export function recommendedPost(params?: any, data?: any) {
return request({ url: '/post/recommended', method: 'get', params, data })
}
export function recommendedShop(params, data) {
export function recommendedShop(params?: any, data?: any) {
return request({ url: '/shop/recommended', method: 'get', params, data })
}

View File

@ -1,28 +1,33 @@
import request from '../utils/request'
import { request } from '@/utils/request'
export function historyOther(params, data) {
export function historyOther(params?: any, data?: any) {
return request({ url: '/video/historyOther', method: 'get', params, data })
}
export function historyVideo(params, data) {
export function historyVideo(params?: any, data?: any) {
return request({ url: '/video/history', method: 'get', params, data })
}
export function recommendedVideo(params, data) {
export function recommendedVideo(params?: any, data?: any) {
return request({ url: '/video/recommended', method: 'get', params, data })
}
export function myVideo(params, data) {
export function recommendedLongVideo(params?: any, data?: any) {
return request({ url: '/video/long/recommended/', method: 'get', params, data })
}
export function myVideo(params?: any, data?: any) {
return request({ url: '/video/my', method: 'get', params, data })
}
export function privateVideo(params, data) {
export function privateVideo(params?: any, data?: any) {
return request({ url: '/video/private', method: 'get', params, data })
}
export function likeVideo(params, data) {
export function likeVideo(params?: any, data?: any) {
return request({ url: '/video/like', method: 'get', params, data })
}
export function videoComments(params, data) {
export function videoComments(params?: any, data?: any) {
return request({ url: '/video/comments', method: 'get', params, data })
}

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
--color-me: rgb(21, 23, 35);
--color-user: rgb(22, 24, 36);
--color-message: rgb(21, 21, 21);
--color-share-bg: rgb(38, 38, 38);
--home-header-height: 44rem;
--footer-height: 56rem;
--common-header-height: 50rem;

View File

@ -1,45 +1,45 @@
<template>
<img class="close" ref="img" :src="src" />
<img class="close" ref="imgEl" :src="src" />
</template>
<script>
export default {
name: 'Back',
props: {
mode: {
type: String,
default: 'gray'
},
img: {
type: String,
default: 'back'
},
direction: {
type: String,
default: 'left'
},
scale: {
type: [Number, String],
default: 1
}
<script setup lang="ts">
import { _css } from '@/utils/dom'
import { onMounted } from 'vue'
defineOptions({
name: 'Back'
})
const props = defineProps({
mode: {
type: String,
default: 'gray'
},
data() {
return {}
img: {
type: String,
default: 'back'
},
computed: {
src() {
return new URL(`../assets/img/icon/components/${this.mode}-${this.img}.png`, import.meta.url)
.href
}
direction: {
type: String,
default: 'left'
},
mounted() {
this.$setCss(
this.$refs.img,
'transform',
`rotate(${this.direction === 'left' ? '0' : '180'}deg) scale(${this.scale})`
)
},
methods: {}
}
scale: {
type: [Number, String],
default: 1
}
})
const imgEl = $ref()
const src = $computed(() => {
return new URL(`../assets/img/icon/components/${props.mode}-${props.img}.png`, import.meta.url)
.href
})
onMounted(() => {
_css(
imgEl,
'transform',
`rotate(${props.direction === 'left' ? '0' : '180'}deg) scale(${props.scale})`
)
})
</script>
<style scoped lang="less">

View File

@ -51,7 +51,7 @@ export default {
},
radius: {
type: String,
default: '3'
default: '6'
}
},
data() {
@ -134,7 +134,7 @@ export default {
}
&.dark2 {
background: rgb(36, 36, 36);
background: rgb(51, 51, 51);
color: #fff;
}
@ -159,9 +159,9 @@ export default {
}
&.white {
background: white;
background: white !important;
color: black;
border: 1px solid gainsboro;
border: 1px solid gainsboro !important;
}
&.info {

View File

@ -47,6 +47,9 @@ export default {
bus.off(EVENT_KEY.EXIT_FULLSCREEN)
},
methods: {
$nav(path) {
this.$router.push(path)
},
tab(index) {
switch (index) {
case 1:
@ -128,6 +131,7 @@ export default {
}
.add-ctn {
cursor: pointer;
@height: 27rem;
@width: 36rem;
height: @height;
@ -147,6 +151,8 @@ export default {
}
span {
cursor: pointer;
font-weight: bold;
opacity: 0.7;
@ -156,8 +162,8 @@ export default {
}
.badge {
right: 10rem;
top: 6rem;
right: 14rem;
top: 12rem;
position: absolute;
}
}

View File

@ -1,36 +1,50 @@
<template>
<div class="music-wrapper">
<div
class="mute-icon"
:class="showMutedNotice && 'notice'"
v-click="() => bus.emit(EVENT_KEY.REMOVE_MUTED)"
v-if="isMuted"
>
<div class="wrap">
<Icon icon="flowbite:volume-mute-solid" />
<span :style="{ opacity: showMutedNotice ? 1 : 0 }">取消静音</span>
</div>
</div>
<img
class="music"
:src="props.item.music?.cover_thumb.url_list[0]"
:src="item.music?.cover_thumb.url_list[0]"
:style="style"
@click.stop="
bus.emit(EVENT_KEY.NAV, {
path: '/home/music',
query: { id: props.item.id }
})
v-click="
() =>
bus.emit(EVENT_KEY.NAV, {
path: '/home/music',
query: { id: item.aweme_id }
})
"
/>
</div>
</template>
<script setup>
import { computed, inject } from 'vue'
<script setup lang="ts">
import { inject, onMounted } from 'vue'
import bus, { EVENT_KEY } from '@/utils/bus'
import { Icon } from '@iconify/vue'
import { useClick } from '@/utils/hooks/useClick'
const props = defineProps({
item: {
type: Object,
default: () => {
return {}
}
}
})
const isPlaying = inject<boolean>('isPlaying')
const isMuted = inject('isMuted')
const item = inject<any>('item')
const vClick = useClick()
let showMutedNotice = $ref(window.showMutedNotice)
const isPlaying = inject('isPlaying')
const style = computed(() => {
const style = $computed(() => {
return { webkitAnimationPlayState: isPlaying.value ? 'running' : 'paused' }
})
onMounted(() => {
bus.on(EVENT_KEY.HIDE_MUTED_NOTICE, () => {
showMutedNotice = false
})
})
</script>
<style lang="less">
@ -38,11 +52,14 @@ const style = computed(() => {
display: flex;
justify-content: center;
@w: 45rem;
width: @w;
height: @w;
position: relative;
.music {
border-radius: 50%;
width: @w;
height: @w;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
@ -59,5 +76,43 @@ const style = computed(() => {
transform: rotate(360deg);
}
}
.mute-icon {
.music;
cursor: pointer;
position: absolute;
z-index: 1;
right: 0;
background: white;
animation: unset;
color: black;
transition: all 0.5s;
overflow: hidden;
.wrap {
width: 100rem;
position: absolute;
left: 0;
display: flex;
align-items: center;
justify-content: center;
}
&.notice {
border-radius: 50rem;
width: 100rem;
}
svg {
font-size: 22rem;
}
span {
margin-left: 5rem;
font-size: 13rem;
word-break: keep-all;
transition: all 0.5s;
}
}
}
</style>

View File

@ -1,11 +1,11 @@
<template>
<div
class="call-float"
v-if="isSmall"
v-if="state.isSmall"
:style="callFloatStyle"
@touchmove="touchmove"
@touchend="touchend"
@click="isSmall = false"
@click="state.isSmall = false"
>
<img src="@/assets/img/icon/message/chat/call-float.png" alt="" />
<span>呼叫中</span>
@ -14,134 +14,145 @@
<transition name="scale">
<div
class="audio-call"
:style="isSmall ? callFloatStyle : { zIndex: 10 }"
:class="isSmall ? 'small' : ''"
v-if="isShowAudioCall"
:style="state.isSmall ? callFloatStyle : { zIndex: 10 }"
:class="state.isSmall ? 'small' : ''"
v-if="state.isShowAudioCall"
>
<div class="float">
<div class="header">
<div class="left">
<img @click="isSmall = true" src="@/assets/img/icon/message/chat/narrow.png" alt="" />
<img
@click="state.isSmall = true"
src="@/assets/img/icon/message/chat/narrow.png"
alt=""
/>
</div>
<span class="center">等待对方接听...</span>
<div class="right">
<div class="option">
<img
v-show="!isOpenCamera"
@click="isOpenCamera = !isOpenCamera"
v-show="!state.isOpenCamera"
@click="state.isOpenCamera = !state.isOpenCamera"
src="@/assets/img/icon/message/chat/disabled-camera.png"
alt=""
/>
<img
v-show="isOpenCamera"
@click="isOpenCamera = !isOpenCamera"
v-show="state.isOpenCamera"
@click="state.isOpenCamera = !state.isOpenCamera"
src="@/assets/img/icon/message/chat/able-camera.png"
alt=""
/>
<span>摄像头</span>
</div>
<div class="option" v-if="isExpand">
<div class="option" v-if="state.isExpand">
<img
v-show="!isOpenAudio"
@click="isOpenAudio = !isOpenAudio"
v-show="!state.isOpenAudio"
@click="state.isOpenAudio = !state.isOpenAudio"
src="@/assets/img/icon/message/chat/disabled-volume.png"
alt=""
/>
<img
v-show="isOpenAudio"
@click="isOpenAudio = !isOpenAudio"
v-show="state.isOpenAudio"
@click="state.isOpenAudio = !state.isOpenAudio"
src="@/assets/img/icon/message/chat/able-volume.png"
alt=""
/>
<span>免提</span>
</div>
<div class="option">
<dy-back mode="light" @click="isExpand = !isExpand" img="back" class="shrink" />
<dy-back
mode="light"
@click="state.isExpand = !state.isExpand"
img="back"
class="shrink"
/>
<!-- <img src="@/assets/img/icon/message/chat/narrow.png" alt="">-->
</div>
</div>
</div>
<img src="@/assets/img/icon/avatar/2.png" alt="" class="big-avatar" />
<div class="footer">
<img @click="isShowAudioCall = false" src="@/assets/img/icon/message/chat/call-end.png" />
<img
@click="state.isShowAudioCall = false"
src="@/assets/img/icon/message/chat/call-end.png"
/>
<span>挂断</span>
</div>
</div>
</div>
</transition>
</template>
<script>
import { inject } from 'vue'
<script setup lang="ts">
import { onMounted, reactive, watch } from 'vue'
import bus, { EVENT_KEY } from '@/utils/bus'
export default {
name: 'Call',
components: {},
props: {
modelValue: {
type: Boolean,
default() {
return false
}
defineOptions({
name: 'Call'
})
defineProps({
modelValue: {
type: Boolean,
default() {
return false
}
},
data() {
return {
mitt: inject('mitt'),
callFloatTransitionTime: 300,
callFloatLeft: 15,
callFloatTop: 100,
isOpenCamera: false,
isOpenAudio: true,
isExpand: true,
isSmall: false,
isShowAudioCall: false,
height: 0,
width: 0
}
})
const state = reactive({
callFloatTransitionTime: 300,
callFloatLeft: 15,
callFloatTop: 100,
isOpenCamera: false,
isOpenAudio: true,
isExpand: true,
isSmall: false,
isShowAudioCall: false,
height: 0,
width: 0
})
const callFloatStyle = $computed(() => {
return {
'transition-duration': state.callFloatTransitionTime + 'ms',
left: state.callFloatLeft + 'px',
top: state.callFloatTop + 'px'
}
})
watch(
() => state.isShowAudioCall,
(newVal) => {
if (!newVal) {
state.isOpenCamera = false
state.isOpenAudio = true
}
},
computed: {
callFloatStyle() {
return {
'transition-duration': this.callFloatTransitionTime + 'ms',
left: this.callFloatLeft + 'px',
top: this.callFloatTop + 'px'
}
}
)
onMounted(() => {
bus.on(EVENT_KEY.SHOW_AUDIO_CALL, () => {
if (state.isShowAudioCall) {
state.isSmall = false
} else {
state.isShowAudioCall = true
}
},
watch: {
isShowAudioCall(newVal) {
if (!newVal) {
this.isOpenCamera = false
this.isOpenAudio = true
}
}
},
created() {},
methods: {
touchmove(e) {
this.callFloatTransitionTime = 0
this.callFloatLeft = e.touches[0].pageX - 35
this.callFloatTop = e.touches[0].pageY - 40
},
touchend() {
this.callFloatTransitionTime = 300
if (this.callFloatLeft < this.width / 2) {
this.callFloatLeft = 15
} else {
this.callFloatLeft = this.width - 15 - 70
}
}
},
mounted() {
this.mitt.on('showAudioCall', () => {
if (this.isShowAudioCall) {
this.isSmall = false
} else {
this.isShowAudioCall = true
}
})
this.height = document.body.clientHeight
this.width = document.body.clientWidth
})
state.height = document.body.clientHeight
state.width = document.body.clientWidth
})
function touchmove(e) {
state.callFloatTransitionTime = 0
state.callFloatLeft = e.touches[0].pageX - 35
state.callFloatTop = e.touches[0].pageY - 40
}
function touchend() {
state.callFloatTransitionTime = 300
if (state.callFloatLeft < state.width / 2) {
state.callFloatLeft = 15
} else {
state.callFloatLeft = state.width - 15 - 70
}
}
</script>

View File

@ -15,8 +15,8 @@
<dy-back mode="dark" img="close" direction="right" style="opacity: 0" />
<div class="num">{{ _formatNumber(comments.length) }}条评论</div>
<div class="right">
<Icon icon="prime:arrow-up-right-and-arrow-down-left-from-center" @click.stop="$no" />
<Icon icon="ic:round-close" @click.stop="cancel" />
<Icon icon="prime:arrow-up-right-and-arrow-down-left-from-center" @click.stop="_no" />
<Icon icon="ic:round-close" v-click="cancel" />
</div>
</div>
</template>
@ -157,7 +157,7 @@
<AutoInput v-model="comment" placeholder="善语结善缘,恶言伤人心"></AutoInput>
<div class="right">
<img src="../assets/img/icon/message/call.png" @click="isCall = !isCall" />
<img src="../assets/img/icon/message/emoji-black.png" @click="$no" />
<img src="../assets/img/icon/message/emoji-black.png" @click="_no" />
</div>
</div>
<img v-if="comment" src="../assets/img/icon/message/up.png" @click="send" />
@ -178,9 +178,9 @@ import FromBottomDialog from './dialog/FromBottomDialog.vue'
import Loading from './Loading.vue'
import Search from './Search.vue'
import {
$no,
_checkImgUrl,
_formatNumber,
_no,
_showSelectDialog,
_sleep,
_time,
@ -251,10 +251,10 @@ export default {
},
mounted() {},
methods: {
_no,
_time,
_formatNumber,
_checkImgUrl,
$no,
async handShowChildren(item) {
this.loadChildrenItemCId = item.comment_id
this.loadChildren = true
@ -328,10 +328,8 @@ export default {
@import '../assets/less/index';
.title {
z-index: 2;
position: fixed;
left: 0;
right: 0;
box-sizing: border-box;
width: 100%;
height: 40rem;
padding: 0 15rem;
background: white;
@ -370,16 +368,13 @@ export default {
.comment {
color: #000;
width: 100vw;
height: v-bind(height);
width: 100%;
background: #fff;
z-index: 5;
border-radius: 10rem 10rem 0 0;
.wrapper {
width: 100%;
position: relative;
padding-top: 40rem;
padding-bottom: 60rem;
}
@ -529,7 +524,7 @@ export default {
border-radius: 10rem 10rem 0 0;
background: white;
position: fixed;
width: 100vw;
width: 100%;
bottom: 0;
z-index: 3;
@ -601,7 +596,7 @@ export default {
}
.auto-input {
width: calc(100vw - 180rem);
width: calc(100% - 160rem);
}
}

View File

@ -16,23 +16,23 @@
<div class="toolbar">
<div class="title">分享到</div>
<div class="shares">
<div class="share-to" @click="$no">
<div class="share-to" @click="_no">
<img src="../assets/img/icon/components/video/toqq.webp" alt="" />
<span>QQ好友</span>
</div>
<div class="share-to" @click="$no">
<div class="share-to" @click="_no">
<img src="../assets/img/icon/components/video/tozone.webp" alt="" />
<span>QQ空间</span>
</div>
<div class="share-to" @click="$no">
<div class="share-to" @click="_no">
<img src="../assets/img/icon/components/video/towechatchat.webp" alt="" />
<span>微信好友</span>
</div>
<div class="share-to" @click="$no">
<div class="share-to" @click="_no">
<img src="../assets/img/icon/components/video/towechat.webp" alt="" />
<span>朋友圈</span>
</div>
<div class="share-to" @click="$no">
<div class="share-to" @click="_no">
<img src="../assets/img/icon/components/video/todownload.webp" alt="" />
<span>保存到相册</span>
</div>
@ -43,7 +43,7 @@
</transition>
</template>
<script>
import { _checkImgUrl } from '@/utils'
import { _checkImgUrl, _no } from '@/utils'
export default {
name: 'DouyinCode',
@ -62,6 +62,7 @@ export default {
computed: {},
created() {},
methods: {
_no,
_checkImgUrl,
cancel() {
this.$emit('update:modelValue', false)

View File

@ -14,7 +14,7 @@ export default {
@import '../assets/less/index';
.NoMore {
font-size: 12rem;
font-size: 13rem;
height: 60rem;
display: flex;
align-items: center;

View File

@ -2,10 +2,13 @@
<div class="posters">
<div class="poster-item" :key="index" v-for="(i, index) in list" @click="goDetail(index)">
<img class="poster" v-lazy="_checkImgUrl(i.video.cover.url_list[0])" alt="" />
<div class="num" v-if="mode === 'normal'">
<Icon icon="icon-park-outline:like" />
<span>{{ _formatNumber(i.statistics.digg_count) }}</span>
</div>
<template v-if="mode === 'normal'">
<div class="num">
<Icon icon="icon-park-outline:like" />
<span>{{ _formatNumber(i.statistics.digg_count) }}</span>
</div>
<div class="top" v-if="i.is_top">置顶</div>
</template>
<div class="date" v-if="mode === 'date'">
<div class="day">{{ getDay(i.create_time) }}</div>
<div class="month">{{ getMonth(i.create_time) }}</div>
@ -87,11 +90,12 @@ function getMonth(time) {
<style scoped lang="less">
.posters {
display: grid;
grid-template-columns: 33.33vw 33.33vw 33.33vw;
grid-template-columns: 33.33% 33.33% 33.33%;
}
.poster-item {
height: calc(33.33vw * 1.2);
height: 200rem;
max-height: calc(33.33vw * 1.3);
border: 0.5px solid black;
overflow: hidden;
position: relative;
@ -103,6 +107,7 @@ function getMonth(time) {
object-fit: cover;
}
.top,
.music {
position: absolute;
font-size: 12rem;

View File

@ -123,6 +123,7 @@ export default {
@import '../assets/less/index';
.scroll-wrapper {
touch-action: pan-y;
overflow: auto;
.scroll-content {

View File

@ -1,5 +1,10 @@
<template>
<Scroll :loading="state.loading" :full-loading="!state.list.length" @pulldown="loadData">
<Scroll
ref="scroll"
:loading="state.loading"
:full-loading="!state.list.length"
@pulldown="loadData"
>
<slot :list="state.list"></slot>
<NoMore v-if="state.total !== 0 && state.total === state.list.length" />
</Scroll>
@ -10,6 +15,7 @@ import { onMounted, reactive } from 'vue'
import { _notice } from '@/utils'
import Scroll from '@/components/Scroll.vue'
import NoMore from '@/components/NoMore.vue'
import { useScroll } from '@/utils/hooks/useScroll.ts'
const props = defineProps({
api: {
@ -19,6 +25,7 @@ const props = defineProps({
}
}
})
const scroll = useScroll()
const state = reactive({
list: [],
@ -61,5 +68,3 @@ async function getData(refresh = false) {
onMounted(getData)
</script>
<style scoped lang="less"></style>

View File

@ -7,7 +7,7 @@
:show-heng-gang="false"
:touch-moved="false"
maskMode="light"
height="370rem"
height="320rem"
mode="dark"
>
<div class="share">
@ -15,221 +15,229 @@
<span>分享给朋友</span>
<dy-back mode="light" img="close" direction="right" @click.stop="closeShare"></dy-back>
</div>
<div class="friends list">
<div
class="option"
:key="i"
v-for="(item, i) in friends.all"
@click.stop="toggleCall(item)"
>
<img
:style="item.select ? 'opacity: .5;' : ''"
class="avatar"
:src="$imgPreview(item.avatar)"
alt=""
/>
<span>{{ item.name }}</span>
<img
v-if="item.select"
class="checked"
src="../assets/img/icon/components/check/check-red-share.png"
/>
<div class="content">
<div class="friends list">
<div
class="option"
:key="i"
v-for="(item, i) in store.friends.all"
@click.stop="toggleCall(item)"
>
<img
:style="item.select ? 'opacity: .5;' : ''"
class="avatar"
:src="_checkImgUrl(item.avatar)"
alt=""
/>
<span>{{ item.name }}</span>
<img
v-if="item.select"
class="checked"
src="../assets/img/icon/components/check/check-red-share.png"
/>
</div>
<div class="option" @click.stop="closeShare($router.push('/message/share-to-friend'))">
<dy-back class="more" mode="light" direction="right"></dy-back>
<span>更多朋友</span>
</div>
</div>
<div class="option" @click.stop="closeShare($nav('/message/share-to-friend'))">
<dy-back class="more" mode="light" direction="right"></dy-back>
<span>更多朋友</span>
</div>
</div>
<div class="shares list">
<template v-if="mode === 'video'">
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<img class="avatar" src="../assets/img/icon/components/video/torichang.png" alt="" />
<span>转发</span>
<div class="bottom">
<div class="share2friend" v-if="store.selectFriends.length">
<div class="line"></div>
<div class="comment">
<textarea placeholder="有什么想和好友说的..."></textarea>
<img class="poster" src="../assets/img/poster/1.jpg" alt="" />
</div>
<div class="btns">
<dy-button type="dark2" radius="7" v-if="store.selectFriends.length > 1" @click="_no"
>建群并发送
</dy-button>
<dy-button type="primary" radius="7" @click="_no"
>{{ store.selectFriends.length > 1 ? '分别发送' : '发送' }}
</dy-button>
</div>
</div>
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<Icon icon="icon-park-solid:good-two" />
<span>推荐给朋友</span>
</div>
<div class="option" @click.stop="copyLink">
<Icon icon="humbleicons:link" />
<span>复制链接</span>
</div>
<div class="option" @click.stop="$no">
<img class="small" src="../assets/img/icon/components/video/comeonplay.png" alt="" />
<span>合拍</span>
</div>
<div class="option" @click.stop="$no">
<img class="small" src="../assets/img/icon/components/video/dou.webp" alt="" />
<span>帮上热门</span>
</div>
<div class="option" @click.stop="$nav('/home/report', { mode: this.mode })">
<img class="small" src="../assets/img/icon/components/video/warring.png" alt="" />
<span>举报</span>
</div>
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<Icon icon="ion:paper-plane" />
<span>私信朋友</span>
</div>
<div class="option" v-if="canDownload" @click.stop="closeShare($emit('download'))">
<Icon icon="mingcute:download-fill" />
<span>保存本地</span>
</div>
<div class="option" @click.stop="$no">
<!--TODO icon不对 -->
<img class="small" src="../assets/img/icon/components/video/feedback.webp" alt="" />
<span>建群分享</span>
</div>
<div class="option" @click.stop="$no">
<img class="small" src="../assets/img/icon/components/video/comeonlook.webp" alt="" />
<span>一起看视频</span>
</div>
<div class="option" @click.stop="closeShare($emit('dislike'))">
<img class="small" src="../assets/img/icon/components/video/dislike.png" alt="" />
<span>不感兴趣</span>
</div>
<div class="option" @click.stop="closeShare($emit('showDouyinCode'))">
<Icon icon="tabler:photo" />
<span>生成图片</span>
</div>
<div class="option" @click.stop="$no">
<img class="small" src="../assets/img/icon/components/video/bizhi.webp" alt="" />
<span>动态壁纸</span>
</div>
<div class="option" @click.stop="closeShare($emit('play-feedback'))">
<img class="small" src="../assets/img/icon/components/video/feedback.webp" alt="" />
<span>播放反馈</span>
</div>
</template>
<template v-if="mode === 'music'">
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<img class="small" src="../assets/img/icon/components/video/tofriend.webp" alt="" />
<span>私信朋友</span>
</div>
<div class="option" @click.stop="$nav('/home/report', { mode: this.mode })">
<img class="small" src="../assets/img/icon/components/video/warring.png" alt="" />
<span>举报音乐</span>
</div>
</template>
<template v-if="mode === 'my-music'">
<div class="option" @click.stop="$no">
<img class="small" src="../assets/img/icon/components/video/torichang.png" alt="" />
<span>转发到日常</span>
</div>
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<img class="small" src="../assets/img/icon/components/video/tofriend.webp" alt="" />
<span>私信朋友</span>
</div>
</template>
</div>
<div class="share2friend" v-if="selectFriends.length">
<div class="comment">
<textarea placeholder="有什么想和好友说的..."></textarea>
<img class="poster" src="../assets/img/poster/1.jpg" alt="" />
</div>
<div class="btns">
<dy-button type="dark2" radius="7" v-if="selectFriends.length > 1" @click.stop="$no"
>建群并发送
</dy-button>
<dy-button type="primary" radius="7" @click.stop="$no"
>{{ selectFriends.length > 1 ? '分别发送' : '发送' }}
</dy-button>
<div class="shares list" v-else>
<template v-if="mode === 'video'">
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<img
class="avatar"
src="../assets/img/icon/components/video/torichang.png"
alt=""
/>
<span>转发</span>
</div>
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<Icon icon="icon-park-solid:good-two" />
<span>推荐给朋友</span>
</div>
<div class="option" @click.stop="copyLink">
<Icon icon="humbleicons:link" />
<span>复制链接</span>
</div>
<div class="option" @click.stop="_no">
<img
class="small"
src="../assets/img/icon/components/video/comeonplay.png"
alt=""
/>
<span>合拍</span>
</div>
<div class="option" @click.stop="_no">
<img class="small" src="../assets/img/icon/components/video/dou.webp" alt="" />
<span>帮上热门</span>
</div>
<div class="option" @click.stop="$router.push('/home/report', { mode: this.mode })">
<img class="small" src="../assets/img/icon/components/video/warring.png" alt="" />
<span>举报</span>
</div>
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<Icon icon="ion:paper-plane" />
<span>私信朋友</span>
</div>
<div class="option" v-if="canDownload" @click.stop="closeShare($emit('download'))">
<Icon icon="mingcute:download-fill" />
<span>保存本地</span>
</div>
<div class="option" @click.stop="_no">
<!--TODO icon不对 -->
<img class="small" src="../assets/img/icon/components/video/feedback.webp" alt="" />
<span>建群分享</span>
</div>
<div class="option" @click.stop="_no">
<img
class="small"
src="../assets/img/icon/components/video/comeonlook.webp"
alt=""
/>
<span>一起看视频</span>
</div>
<div class="option" @click.stop="closeShare($emit('dislike'))">
<img class="small" src="../assets/img/icon/components/video/dislike.png" alt="" />
<span>不感兴趣</span>
</div>
<div class="option" @click.stop="closeShare($emit('showDouyinCode'))">
<Icon icon="tabler:photo" />
<span>生成图片</span>
</div>
<div class="option" @click.stop="_no">
<img class="small" src="../assets/img/icon/components/video/bizhi.webp" alt="" />
<span>动态壁纸</span>
</div>
<div class="option" @click.stop="closeShare($emit('play-feedback'))">
<img class="small" src="../assets/img/icon/components/video/feedback.webp" alt="" />
<span>播放反馈</span>
</div>
</template>
<template v-if="mode === 'music'">
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<img class="small" src="../assets/img/icon/components/video/tofriend.webp" alt="" />
<span>私信朋友</span>
</div>
<div class="option" @click.stop="$router.push('/home/report', { mode: this.mode })">
<img class="small" src="../assets/img/icon/components/video/warring.png" alt="" />
<span>举报音乐</span>
</div>
</template>
<template v-if="mode === 'my-music'">
<div class="option" @click.stop="_no">
<img class="small" src="../assets/img/icon/components/video/torichang.png" alt="" />
<span>转发到日常</span>
</div>
<div class="option" @click.stop="closeShare($emit('ShareToFriend'))">
<img class="small" src="../assets/img/icon/components/video/tofriend.webp" alt="" />
<span>私信朋友</span>
</div>
</template>
</div>
</div>
</div>
</div>
</from-bottom-dialog>
</template>
<script>
import { mapState } from 'pinia'
<script setup>
import FromBottomDialog from './dialog/FromBottomDialog'
import { useBaseStore } from '@/store/pinia'
import { $no, _copy } from '@/utils'
import { _checkImgUrl, _copy, _hideLoading, _no, _notice, _showLoading, _sleep } from '@/utils'
export default {
name: 'Share',
components: {
FromBottomDialog
},
props: {
modelValue: {
type: Boolean,
default() {
return false
}
},
item: {},
videoId: {
type: String,
default() {
return null
}
},
pageId: {
type: String,
default() {
return 'home-index'
}
},
canDownload: {
type: Boolean,
default() {
return true
}
},
mode: {
type: String,
default() {
return 'video'
//music
//qrcode
}
defineOptions({
name: 'Share'
})
const props = defineProps({
modelValue: {
type: Boolean,
default() {
return false
}
},
computed: {
...mapState(useBaseStore, ['friends']),
selectFriends() {
return this.friends.all.filter((v) => v.select)
item: {},
videoId: {
type: String,
default() {
return null
}
},
data() {
return {}
pageId: {
type: String,
default() {
return 'home-index'
}
},
methods: {
$no,
async copyLink() {
this.closeShare()
this.$showLoading()
await this.$sleep(500)
this.$hideLoading()
_copy(this.item.share_info.share_link_desc + this.item.share_info.share_url)
//TODO
this.$notice('复制成功')
},
toggleCall(item) {
item.select = !item.select
},
closeShare() {
this.friends.all = this.friends.all.map((v) => {
v.select = false
return v
})
this.$emit('update:modelValue', false)
canDownload: {
type: Boolean,
default() {
return true
}
},
mode: {
type: String,
default() {
return 'video'
//music
//qrcode
}
}
})
const store = useBaseStore()
const emit = defineEmits(['update:item'])
async function copyLink() {
closeShare()
_showLoading()
await _sleep(500)
_hideLoading()
_copy(props.item.share_info.share_link_desc + props.item.share_info.share_url)
//TODO
_notice('复制成功')
}
function toggleCall(item) {
item.select = !item.select
}
function closeShare() {
store.friends.all = store.friends.all.map((v) => {
v.select = false
return v
})
emit('update:modelValue', false)
}
</script>
<style lang="less" scoped>
@import '../assets/less/index';
.share {
width: 100%;
background: black;
height: 100%;
background: var(--color-share-bg);
border-radius: 10px 10px 0 0;
color: white;
box-sizing: border-box;
display: flex;
flex-direction: column;
@space-width: 26rem;
@avatar-width: 58rem;
@ -253,12 +261,25 @@ export default {
}
}
.content {
flex: 1;
display: flex;
flex-direction: column;
gap: 10rem;
.bottom {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
}
.list {
overflow-x: scroll;
display: flex;
padding: 0 20rem;
gap: 22rem;
padding-bottom: 50rem;
}
@c: rgb(51, 51, 51);
@ -321,13 +342,11 @@ export default {
}
.share2friend {
position: fixed;
bottom: 0;
padding: 20rem;
padding-top: 0;
box-sizing: border-box;
width: 100vw;
height: 180rem;
background: black;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
@ -335,9 +354,11 @@ export default {
.comment {
display: flex;
flex: 1;
margin-top: 15rem;
textarea {
flex: 1;
font-size: 14rem;
outline: none;
border: none;
background: transparent;
@ -346,8 +367,10 @@ export default {
.poster {
margin-left: 20rem;
height: 40rem;
width: 40rem;
height: 60rem;
width: 60rem;
object-fit: cover;
border-radius: 4rem;
}
}

View File

@ -1,5 +1,5 @@
<template>
<div id="UserPanel" @scroll="scroll" ref="page">
<div id="UserPanel" @scroll="scroll" @dragstart="(e) => _stopPropagation(e)" ref="page">
<div ref="float" class="float" :class="state.floatFixed ? 'fixed' : ''">
<div class="left">
<Icon @click="emit('back')" class="icon" icon="eva:arrow-ios-back-fill" />
@ -32,7 +32,7 @@
<span>求更新</span>
</div>
</transition>
<Icon class="icon" icon="ion:search" @click.stop="$no()" />
<Icon class="icon" icon="ion:search" @click.stop="_no" />
<Icon class="icon" icon="ri:more-line" @click.stop="emit('showFollowSetting')" />
</div>
</div>
@ -76,7 +76,7 @@
<img
src="@/assets/img/icon/me/copy.png"
alt=""
@click.stop="Utils.copy(_getUserDouyinId(props.currentItem))"
@click.stop="_copy(_getUserDouyinId(props.currentItem))"
/>
</div>
</div>
@ -85,20 +85,16 @@
<div class="info">
<div class="heat">
<div class="text">
<span class="num">{{
Utils.formatNumber(props.currentItem.author.total_favorited)
}}</span>
<span class="num">{{ _formatNumber(props.currentItem.author.total_favorited) }}</span>
<span>获赞</span>
</div>
<div class="text">
<span class="num">{{
Utils.formatNumber(props.currentItem.author.following_count)
}}</span>
<span class="num">{{ _formatNumber(props.currentItem.author.following_count) }}</span>
<span>关注</span>
</div>
<div class="text">
<span class="num">{{
Utils.formatNumber(props.currentItem.author.mplatform_followers_count)
_formatNumber(props.currentItem.author.mplatform_followers_count)
}}</span>
<span>粉丝</span>
</div>
@ -234,7 +230,14 @@
<script setup lang="ts">
import { reactive, ref, watch } from 'vue'
import Utils, { $no, _checkImgUrl, _getUserDouyinId } from '@/utils'
import {
_checkImgUrl,
_copy,
_formatNumber,
_getUserDouyinId,
_no,
_stopPropagation
} from '@/utils'
import { useNav } from '@/utils/hooks/useNav'
import Posters from '@/components/Posters.vue'
import { DefaultUser } from '@/utils/const_var'
@ -405,6 +408,7 @@ function touchEnd() {
}
#UserPanel {
touch-action: pan-y;
position: fixed;
background: var(--color-user);
height: 100%;
@ -423,8 +427,8 @@ function touchEnd() {
justify-content: center;
.resource {
width: 100vw;
max-height: 100vw;
width: 100%;
max-height: 100%;
}
.download {
@ -442,7 +446,7 @@ function touchEnd() {
position: absolute;
top: 0;
left: 0;
width: 100vw;
width: 100%;
height: calc(var(--vh, 1vh) * 100);
z-index: 3;
}
@ -500,7 +504,7 @@ function touchEnd() {
grid-template-columns: 33.33% 33.33% 33.33%;
.item {
height: calc(33.33vw * 1.3);
height: calc(33.33% * 1.3);
padding: 2rem;
overflow: hidden;
position: relative;
@ -572,7 +576,7 @@ function touchEnd() {
.poster {
border-radius: 4rem;
width: 100%;
height: calc((100vw - 34rem) / 3);
height: calc((100% - 34rem) / 3);
display: block;
}
@ -592,7 +596,7 @@ function touchEnd() {
.cover {
height: 220rem;
object-fit: cover;
width: 100vw;
width: 100%;
//transition: height .3s;
}
@ -977,7 +981,7 @@ function touchEnd() {
.float {
position: fixed;
box-sizing: border-box;
width: 100vw;
width: 100%;
z-index: 2;
display: flex;
justify-content: space-between;

View File

@ -1,83 +1,65 @@
<template>
<div class="ConfirmDialog" @click="$emit('dismiss')" v-if="visible">
<div class="content" @click.stop="stop">
<div class="ConfirmDialog" @click="onDismiss" v-if="visible">
<div class="content">
<slot name="header"></slot>
<div class="body">
<div class="title" v-if="title">{{ title }}</div>
<div class="subtitle" :class="subtitleColor" v-if="subtitle">
<div :class="['subtitle', subtitleColor]" v-if="subtitle">
{{ subtitle }}
</div>
<slot></slot>
</div>
<div class="footer">
<div class="cancel" :class="cancelTextColor" @click.stop="cancel">
<div :class="['cancel', cancelTextColor]" @click.stop="onCancel">
{{ cancelText }}
</div>
<div class="ok" @click.stop="ok">{{ okText }}</div>
<div class="ok" @click.stop="onOk">{{ okText }}</div>
</div>
</div>
</div>
</template>
<script lang="ts">
/*TODO 单独使用时没有mark*/
export default {
name: 'ConfirmDialog',
props: {
visible: {
type: Boolean,
default: true
},
title: {
type: String,
default() {
return ''
}
},
subtitle: {
type: String,
default() {
return ''
}
},
subtitleColor: {
type: String,
default() {
return 'gray'
}
},
okText: {
type: String,
default() {
return '确定'
}
},
cancelText: {
type: String,
default() {
return '取消'
}
},
cancelTextColor: {
type: String,
default() {
return 'gray'
}
}
},
data() {
return {}
},
methods: {
stop() {},
ok() {
this.$emit('ok')
this.$emit('update:visible', false)
},
cancel() {
this.$emit('cancel')
this.$emit('update:visible', false)
}
}
<script setup lang="ts">
defineOptions({ name: 'ConfirmDialog' })
interface Props {
title?: string
subtitle?: string
subtitleColor?: string
okText?: string
cancelText?: string
cancelTextColor?: string
}
withDefaults(defineProps<Props>(), {
title: '',
subtitle: '',
subtitleColor: 'gray',
okText: '确定',
cancelText: '取消',
cancelTextColor: 'gray'
})
const emit = defineEmits<{
(ev: 'ok'): void
(ev: 'cancel'): void
(ev: 'dismiss'): void
}>()
const visible = defineModel<boolean>('visible', { type: Boolean, default: true })
const onOk = () => {
visible.value = false
emit('ok')
}
const onCancel = () => {
visible.value = false
emit('cancel')
}
const onDismiss = () => {
emit('dismiss')
}
</script>

View File

@ -3,16 +3,9 @@
<slot></slot>
</div>
</template>
<script>
export default {
name: 'FadeDialog',
data() {
return {}
},
computed: {},
created() {},
methods: {}
}
<script setup lang="ts">
defineOptions({ name: 'FadeDialog' })
</script>
<style scoped lang="less">

View File

@ -1,206 +1,177 @@
<template>
<!-- <transition name="from-bottom"> -->
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
:css="false"
>
<Transition name="test">
<div
ref="dialog"
class="FromBottomDialog"
v-if="modelValue"
:class="[mode, showHengGang ? '' : 'no-heng-gang']"
:style="{ 'max-height': height }"
@touchstart="start"
@touchmove="move"
@touchend="end"
:class="['FromBottomDialog', mode, showHengGang ? '' : 'no-heng-gang']"
@touchstart="onStart"
@touchmove="onMove"
@touchend="onEnd"
>
<slot name="header"></slot>
<div class="heng-gang" :class="mode" v-if="showHengGang">
<div :class="['heng-gang', mode]" v-if="showHengGang">
<div class="content"></div>
</div>
<slot></slot>
<div class="wrapper" ref="wrapper">
<slot></slot>
</div>
</div>
</transition>
</Transition>
</template>
<script>
import Dom from '../../utils/dom'
<script setup lang="ts">
import { ref, watch } from 'vue'
import Dom, { _css } from '../../utils/dom'
import bus, { EVENT_KEY } from '@/utils/bus'
import { _stopPropagation } from '@/utils'
export default {
name: 'FromBottomDialog',
props: {
modelValue: {
type: Boolean,
default: false
},
mode: {
type: String,
default: 'dark'
// default: 'light'
// default: 'white'
},
maskMode: {
type: String,
default: 'dark'
},
height: {
type: String,
default: 'calc(var(--vh, 1vh) * 70)'
},
showHengGang: {
type: Boolean,
default: true
},
pageId: {
type: String,
default: null,
required: true
},
borderRadius: {
type: String,
default: '15rem 15rem 0 0'
},
tag: {
type: String,
default: ''
}
},
watch: {
modelValue(newVal) {
let page = document.getElementById(this.pageId)
if (newVal) {
this.pagePosition = this.$getCss2(page, 'position')
page.style.position = 'absolute'
this.scroll = document.documentElement.scrollTop
document.body.style.position = 'fixed'
document.body.style.top = -this.scroll + 'px'
defineOptions({ name: 'FromBottomDialog' })
let maskTemplate = `<div class="Mask fade-in ${this.maskMode}"></div>`
let mask = new Dom().create(maskTemplate)
mask.on('click', (e) => {
this.$stopPropagation(e)
this.hide(false)
interface Props {
modelValue?: boolean
mode?: 'dark' | 'light' | 'white'
maskMode?: 'dark' | 'light' | 'white'
height?: string
showHengGang?: boolean
pageId: string
borderRadius?: string
tag?: string
}
interface Emits {
(ev: 'update:modelValue', val: boolean): void
(ev: 'cancel'): void
}
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
mode: 'dark',
maskMode: 'dark',
height: 'calc(var(--vh, 1vh) * 70)',
showHengGang: true,
borderRadius: '15rem 15rem 0 0',
tag: ''
})
const emit = defineEmits<Emits>()
const dialog = ref<HTMLElement | null>(null)
const wrapper = ref<HTMLElement | null>(null)
const scroll = ref(0)
const startY = ref(0)
const moveY = ref(0)
const startTime = ref(0)
const pagePosition = ref(null)
watch(
() => props.modelValue,
(newVal: boolean) => {
const page = document.getElementById(props.pageId)
if (!page) return
if (newVal) {
pagePosition.value = _css(page, 'position')
page.style.position = 'absolute'
scroll.value = document.documentElement.scrollTop
document.body.style.position = 'fixed'
document.body.style.top = -scroll.value + 'px'
const maskTemplate = `<div class="Mask fade-in ${props.maskMode}"></div>`
const mask = new Dom().create(maskTemplate)
setTimeout(() => {
mask.on('click', (e: Event) => {
_stopPropagation(e)
onHide()
})
page.appendChild(mask.els[0])
} else {
let page = document.getElementById(this.pageId)
page.style.position = this.pagePosition || 'fixed'
document.body.style.position = 'static'
document.documentElement.scrollTop = this.scroll
}, 200)
page.appendChild(mask.els[0])
} else {
page.style.position = pagePosition.value || 'fixed'
document.body.style.position = 'static'
document.documentElement.scrollTop = scroll.value
let mask = new Dom('.Mask').replaceClass('fade-in', 'fade-out')
setTimeout(() => {
mask.remove()
}, 250)
}
}
},
data() {
return {
scroll: 0,
startLocationY: 0,
moveYDistance: 0,
startTime: 0,
pagePosition: null
}
},
computed: {},
created() {},
methods: {
beforeEnter(el) {
this.$setCss(el, 'transition-duration', `250ms`)
this.$setCss(el, 'transform', `translate3d(0,${this.height},0)`)
},
enter(el, done) {
const mask = new Dom('.Mask').replaceClass('fade-in', 'fade-out')
setTimeout(() => {
this.$setCss(el, 'transform', `translate3d(0,0,0)`)
}, 0)
setTimeout(() => {
// this.$setCss(el, 'transition-duration', `0ms`)
this.$setCss(el, 'transform', `none`)
done()
mask.remove()
}, 250)
},
afterEnter() {},
beforeLeave(el) {
this.$setCss(el, 'transition-duration', `250ms`)
this.$setCss(el, 'transform', `translate3d(0,0,0)`)
},
leave(el, done) {
//ref
let maxHeight = new Dom('.FromBottomDialog').css('max-height')
this.$setCss(el, 'transform', `translate3d(0,${maxHeight},0)`)
setTimeout(done, 250)
},
afterLeave() {},
hide(val = false) {
this.$emit('update:modelValue', val)
this.$emit('cancel')
},
start(e) {
if (this.$refs.dialog.scrollTop !== 0) return
this.startLocationY = e.touches[0].pageY
this.startTime = Date.now()
this.$setCss(this.$refs.dialog, 'transition-duration', `0ms`)
},
move(e) {
if (this.$refs.dialog.scrollTop !== 0) return
this.moveYDistance = e.touches[0].pageY - this.startLocationY
if (this.moveYDistance > 0) {
bus.emit(EVENT_KEY.DIALOG_MOVE, {
tag: this.tag,
e: this.moveYDistance
})
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,${this.moveYDistance}px,0)`)
}
},
end() {
//
if (Date.now() - this.startTime < 150 && Math.abs(this.moveYDistance) < 30) {
return
}
//
if (this.$refs.dialog.scrollTop !== 0) return
let clientHeight = this.$refs.dialog.clientHeight
this.$setCss(this.$refs.dialog, 'transition-duration', `250ms`)
if (Math.abs(this.moveYDistance) > clientHeight / 2) {
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,${clientHeight}px,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: this.tag, isClose: true })
setTimeout(this.hide, 250)
} else {
this.$setCss(this.$refs.dialog, 'transform', `translate3d(0,0,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: this.tag, isClose: false })
setTimeout(() => {
this.$setCss(this.$refs.dialog, 'transform', 'none')
// this.$setCss(this.$refs.dialog, 'transition-duration', `0ms`)
}, 250)
}
this.moveYDistance = 0
}
}
)
const onHide = (val = false) => {
emit('update:modelValue', val)
emit('cancel')
}
const onStart = (e: TouchEvent) => {
if (wrapper.value?.scrollTop !== 0) return
startY.value = e.touches[0].clientY
startTime.value = Date.now()
_css(dialog.value, 'transition-duration', '0ms')
}
const onMove = (e: TouchEvent) => {
if (wrapper.value?.scrollTop !== 0) return
moveY.value = e.touches[0].pageY - startY.value
if (moveY.value > 0) {
bus.emit(EVENT_KEY.DIALOG_MOVE, {
tag: props.tag,
e: moveY.value
})
_css(dialog.value, 'transform', `translate3d(0, ${moveY.value}px, 0)`)
}
}
const onEnd = () => {
// modelValue ref
if (!dialog.value) return
if (Date.now() - startTime.value < 150 && Math.abs(moveY.value) < 30) return
const clientHeight = dialog.value?.clientHeight
_css(dialog.value, 'transition-duration', `250ms`)
if (Math.abs(moveY.value) > clientHeight / 2) {
_css(dialog.value, 'transform', `translate3d(0,100%,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: props.tag, isClose: true })
setTimeout(onHide, 250)
} else {
_css(dialog.value, 'transform', `translate3d(0,0,0)`)
bus.emit(EVENT_KEY.DIALOG_END, { tag: props.tag, isClose: false })
}
}
</script>
<style scoped lang="less">
@import '../../assets/less/index';
.test-enter-active,
.test-leave-active {
transition-duration: 250ms !important;
}
.test-enter-from,
.test-leave-to {
transform: translate3d(0, 101%, 0) !important;
}
.FromBottomDialog {
z-index: 9;
position: fixed;
width: 100%;
overflow-y: auto;
padding-top: 24rem;
bottom: 0;
left: 0;
box-sizing: border-box;
border-radius: v-bind(borderRadius);
transition: all 0.3s;
border-radius: 15rem 15rem 0 0;
transform: translate3d(0, 0, 0);
overflow: hidden;
display: flex;
height: v-bind(height);
max-height: v-bind(height);
flex-direction: column;
&.dark {
background: var(--main-bg);
@ -228,6 +199,7 @@ export default {
transform: translateY(-24rem);
justify-content: center;
align-items: center;
touch-action: pan-y;
&.dark {
background: var(--main-bg);
@ -259,5 +231,10 @@ export default {
width: 30rem;
}
}
.wrapper {
flex: 1;
overflow: auto;
}
}
</style>

View File

@ -1,57 +1,51 @@
<template>
<div class="NoticeDialog" @click="$emit('dismiss')">
<div class="content" @click.stop="stop">
<div class="NoticeDialog" @click="onDismiss">
<div class="content">
<div class="body">
<div class="title">{{ title }}</div>
<div class="subtitle" :class="subtitleColor" v-if="subtitle">
<div :class="['subtitle', subtitleColor]" v-if="subtitle">
{{ subtitle }}
</div>
</div>
<div class="footer">
<div class="cancel" @click.stop="$emit('cancel')">{{ cancelText }}</div>
<div class="cancel" @click.stop="onCancel">{{ cancelText }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'NoticeDialog',
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default() {
return ''
}
},
subtitle: {
type: String,
default() {
return ''
}
},
subtitleColor: {
type: String,
default() {
return 'gray'
}
},
cancelText: {
type: String,
default() {
return '取消'
}
}
},
data() {
return {}
},
methods: {
stop() {}
}
<script setup lang="ts">
defineOptions({ name: 'NoticeDialog' })
interface Props {
title?: string
subtitle?: string
subtitleColor?: string
cancelText?: string
}
withDefaults(defineProps<Props>(), {
title: '',
subtitle: '',
subtitleColor: 'gray',
cancelText: '取消'
})
const emit = defineEmits<{
(ev: 'ok'): void
(ev: 'cancel'): void
(ev: 'dismiss'): void
}>()
const visible = defineModel<boolean>('visible', { type: Boolean, default: true })
const onCancel = () => {
visible.value = false
emit('cancel')
}
const onDismiss = () => {
emit('dismiss')
}
</script>

View File

@ -1,30 +1,39 @@
<template>
<div class="SelectDialog" @click="$emit('cancel')">
<div class="SelectDialog" @click="onCancel">
<div class="content">
<div class="item" :key="i" v-for="(item, i) in list" @click.stop="$emit('ok', item)">
<div class="item" :key="i" v-for="(item, i) in list" @click.stop="onOk(item)">
{{ item.name }}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SelectDialog',
props: {
visible: {
type: Boolean,
default: false
},
list: {
type: Array,
default() {
return []
}
}
},
data() {
return {}
}
<script setup lang="ts" generic="T">
defineOptions({ name: 'SelectDialog' })
type Item = { name: string } & T
interface Props {
visible?: boolean
list?: Item[]
}
withDefaults(defineProps<Props>(), {
visible: false,
list: () => []
})
const emit = defineEmits<{
(ev: 'ok', item: Item): void
(ev: 'cancel'): void
}>()
const onOk = (item: Item) => {
emit('ok', item)
}
const onCancel = () => {
emit('cancel')
}
</script>

View File

@ -1,47 +1,50 @@
<template>
<div class="SimpleConfirmDialog" @click="$emit('dismiss')">
<div class="content" @click.stop="stop">
<div class="SimpleConfirmDialog" @click="onDismiss">
<div class="content">
<div class="item">{{ title }}</div>
<div class="footer">
<div class="cancel" @click.stop="$emit('cancel')">{{ cancelText }}</div>
<div class="ok" @click.stop="$emit('ok')">{{ okText }}</div>
<div class="cancel" @click.stop="onCancel">{{ cancelText }}</div>
<div class="ok" @click.stop="onOk">{{ okText }}</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SimpleConfirmDialog',
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default() {
return ''
}
},
okText: {
type: String,
default() {
return '保存'
}
},
cancelText: {
type: String,
default() {
return '放弃'
}
}
},
data() {
return {}
},
methods: {
stop() {}
}
<script setup lang="ts">
defineOptions({ name: 'SimpleConfirmDialog' })
interface Props {
title?: string
okText?: string
cancelText?: string
}
withDefaults(defineProps<Props>(), {
title: '',
okText: '确定',
cancelText: '取消'
})
const emit = defineEmits<{
(ev: 'ok'): void
(ev: 'cancel'): void
(ev: 'dismiss'): void
}>()
const visible = defineModel<boolean>('visible', { type: Boolean, default: true })
const onOk = () => {
visible.value = false
emit('ok')
}
const onCancel = () => {
visible.value = false
emit('cancel')
}
const onDismiss = () => {
emit('dismiss')
}
</script>

View File

@ -1,738 +0,0 @@
<template>
<div class="video-wrapper" ref="videoWrapper" :class="positionName">
<Loading v-if="loading" style="position: absolute" />
<!-- <video :src="item.video + '?v=123'"-->
<video
:src="item.video.play_addr.url_list[0]"
:poster="poster"
ref="video"
muted
preload
loop
x5-video-player-type="h5-page"
:x5-video-player-fullscreen="false"
:webkit-playsinline="true"
:x5-playsinline="true"
:playsinline="true"
:fullscreen="false"
:autoplay="isPlay"
>
<p>您的浏览器不支持 video 标签</p>
</video>
<Icon icon="fluent:play-28-filled" class="pause-icon" v-if="!isPlaying" />
<div class="float">
<template v-if="isLive">
<div class="living">点击进入直播间</div>
<ItemDesc :is-live="true" v-model:item="localItem" :position="position" />
</template>
<template v-else>
<div :style="{ opacity: isMove ? 0 : 1 }" class="normal">
<template v-if="!commentVisible">
<ItemToolbar v-model:item="localItem" :position="position" v-bind="$attrs" />
<ItemDesc v-model:item="localItem" :position="position" />
</template>
<div v-if="isMy" class="comment-status">
<div class="comment">
<div class="type-comment">
<img src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar" />
<div class="right">
<p>
<span class="name">zzzzz</span>
<span class="time">2020-01-20</span>
</p>
<p class="text">北京</p>
</div>
</div>
<transition-group name="comment-status" tag="div" class="loveds">
<div class="type-loved" :key="i" v-for="i in test">
<img src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar" />
<img src="../../assets/img/icon/love.svg" alt="" class="loved" />
</div>
</transition-group>
</div>
</div>
</div>
<div
class="progress"
:class="progressClass"
ref="progress"
@click="null"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend"
>
<div class="time" v-if="isMove">
<span class="currentTime">{{ LUtils.$duration(currentTime) }}</span>
<span class="duration"> / {{ LUtils.$duration(duration) }}</span>
</div>
<template v-if="duration > 15 || isMove || !isPlaying">
<div class="bg"></div>
<div class="progress-line" :style="durationStyle"></div>
<div class="point"></div>
</template>
</div>
</template>
</div>
</div>
</template>
<script>
import Utils, { _checkImgUrl } from '../../utils'
import Loading from '../Loading'
import ItemToolbar from './ItemToolbar'
import ItemDesc from './ItemDesc'
import bus, { EVENT_KEY } from '../../utils/bus'
import { SlideItemPlayStatus } from '@/utils/const_var'
import { computed } from 'vue'
import { Icon } from '@iconify/vue'
export default {
name: 'BVideo',
components: {
Loading,
ItemToolbar,
ItemDesc,
Icon
},
provide() {
return {
// isPlaying: computed(() => this.status)
isPlaying: computed(() => this.isPlaying)
}
},
props: {
item: {
type: Object,
default: () => {
return {}
}
},
position: {
type: Object,
default: () => {
return {}
}
},
//
isPlay: {
type: Boolean,
default: () => {
return true
}
},
isMy: {
type: Boolean,
default: () => {
return false
}
},
isLive: {
type: Boolean,
default: () => {
return false
}
}
},
computed: {
poster() {
return _checkImgUrl(this.item.video.poster ?? this.item.video.cover.url_list[0])
},
durationStyle() {
return { width: this.playX + 'px' }
},
progressClass() {
if (this.isMove) {
return 'move'
} else {
return this.isPlaying ? '' : 'stop'
}
},
positionName() {
return 'item-' + Object.values(this.position).join('-')
},
isPlaying() {
return this.status === SlideItemPlayStatus.Play
}
},
data() {
return {
loading: false,
paused: false,
status: this.isPlay ? SlideItemPlayStatus.Play : SlideItemPlayStatus.Pause,
duration: 0,
step: 0,
currentTime: -1,
playX: 0,
start: { x: 0 },
last: { x: 0, time: 0 },
height: 0,
width: 0,
isMove: false,
ignoreWaiting: false, //waitingwaiting
test: [1, 2],
localItem: this.item,
progressBarRect: {},
videoScreenHeight: 0,
commentVisible: false,
LUtils: Utils
}
},
mounted() {
// console.log('video', this.localItem.id)
// console.log(this.commentVisible)
this.height = document.body.clientHeight
this.width = document.body.clientWidth
let video = this.$refs.video
video.currentTime = 0
let fun = (e) => {
this.currentTime = Math.ceil(e.target.currentTime)
this.playX = (this.currentTime - 1) * this.step
}
video.addEventListener('loadedmetadata', () => {
this.videoScreenHeight = video.videoHeight / (video.videoWidth / this.width)
this.duration = video.duration
this.progressBarRect = this.$refs.progress.getBoundingClientRect()
this.step = this.progressBarRect.width / Math.floor(this.duration)
video.addEventListener('timeupdate', fun)
})
let eventTester = (e) => {
video.addEventListener(
e,
() => {
// console.log('eventTester', e, this.item.id)
if (e === 'playing') this.loading = false
if (e === 'waiting') {
if (!this.paused && !this.ignoreWaiting) {
this.loading = true
}
}
// console.log(e, t)
},
false
)
}
// eventTester("loadstart", ''); //
// eventTester("abort", ''); //
// eventTester("loadstart", ''); //
// eventTester("progress", ''); //
// // eventTester("suspend", ''); //
// eventTester("abort", ''); //
// eventTester("error", ''); //
// eventTester("stalled", ''); //
// eventTester("play", 'play()autoplay'); //play()autoplay
// eventTester("pause", 'pause()'); //pause()
// eventTester("loadedmetadata", ''); //
// eventTester("loadeddata"); //
eventTester('waiting', '等待数据,并非错误') //
eventTester('playing', '开始回放') //
// eventTester("canplay", '/'); //
// eventTester("canplaythrough", ''); //
// eventTester("seeking", ''); //
// eventTester("seeked", ''); //
// // eventTester("timeupdate",''); //
// eventTester("ended", ''); //
// eventTester("ratechange", ''); //
// eventTester("durationchange", ''); //
// eventTester("volumechange", ''); //
// console.log('mounted')
// bus.off('singleClickBroadcast')
bus.on(EVENT_KEY.SINGLE_CLICK_BROADCAST, this.click)
bus.on(EVENT_KEY.DIALOG_MOVE, this.onDialogMove)
bus.on(EVENT_KEY.DIALOG_END, this.onDialogEnd)
bus.on(EVENT_KEY.OPEN_COMMENTS, this.onOpenComments)
bus.on(EVENT_KEY.CLOSE_COMMENTS, this.onCloseComments)
bus.on(EVENT_KEY.OPEN_SUB_TYPE, this.onOpenSubType)
bus.on(EVENT_KEY.CLOSE_SUB_TYPE, this.onCloseSubType)
},
unmounted() {
// console.log('unmounted')
bus.off(EVENT_KEY.SINGLE_CLICK_BROADCAST, this.click)
bus.off(EVENT_KEY.DIALOG_MOVE, this.onDialogMove)
bus.off(EVENT_KEY.DIALOG_END, this.onDialogEnd)
bus.off(EVENT_KEY.OPEN_COMMENTS, this.onOpenComments)
bus.off(EVENT_KEY.CLOSE_COMMENTS, this.onCloseComments)
bus.off(EVENT_KEY.OPEN_SUB_TYPE, this.onOpenSubType)
bus.off(EVENT_KEY.CLOSE_SUB_TYPE, this.onCloseSubType)
},
methods: {
_checkImgUrl,
onOpenSubType() {
this.commentVisible = true
},
onCloseSubType() {
this.commentVisible = false
},
onDialogMove({ tag, e }) {
if (this.commentVisible && tag === 'comment') {
Utils.$setCss(this.$refs.video, 'transition-duration', `0ms`)
Utils.$setCss(this.$refs.video, 'height', `calc(var(--vh, 1vh) * 30 + ${e}px)`)
}
},
onDialogEnd({ tag, isClose }) {
if (this.commentVisible && tag === 'comment') {
console.log('isClose', isClose)
Utils.$setCss(this.$refs.video, 'transition-duration', `300ms`)
if (isClose) {
this.commentVisible = false
Utils.$setCss(this.$refs.video, 'height', '100%')
} else {
Utils.$setCss(this.$refs.video, 'height', 'calc(var(--vh, 1vh) * 30)')
}
}
},
onOpenComments(id) {
if (id === this.item.id) {
Utils.$setCss(this.$refs.video, 'transition-duration', `300ms`)
Utils.$setCss(this.$refs.video, 'height', 'calc(var(--vh, 1vh) * 30)')
this.commentVisible = true
}
},
onCloseComments() {
if (this.commentVisible) {
Utils.$setCss(this.$refs.video, 'transition-duration', `300ms`)
Utils.$setCss(this.$refs.video, 'height', '100%')
this.commentVisible = false
}
},
click({ uniqueId, index, type }) {
if (this.position.uniqueId === uniqueId && this.position.index === index) {
if (type === EVENT_KEY.ITEM_TOGGLE) {
if (this.isLive) {
if (type === EVENT_KEY.ITEM_TOGGLE) {
this.pause()
bus.emit(EVENT_KEY.NAV, {
path: '/home/live',
query: { id: this.item.id }
})
}
} else {
if (this.status === SlideItemPlayStatus.Play) {
this.pause()
} else {
this.play()
}
}
}
if (type === EVENT_KEY.ITEM_STOP) {
this.$refs.video.currentTime = 0
this.ignoreWaiting = true
this.pause()
setTimeout(() => (this.ignoreWaiting = false), 300)
}
if (type === EVENT_KEY.ITEM_PLAY) {
this.$refs.video.currentTime = 0
this.ignoreWaiting = true
this.play()
setTimeout(() => (this.ignoreWaiting = false), 300)
}
}
},
play() {
this.status = SlideItemPlayStatus.Play
this.$refs.video.volume = 1
this.$refs.video.play()
},
pause() {
this.status = SlideItemPlayStatus.Pause
this.$refs.video.pause()
},
touchstart(e) {
Utils.$stopPropagation(e)
this.start.x = e.touches[0].pageX
this.last.x = this.playX
this.last.time = this.currentTime
},
touchmove(e) {
// console.log('move',e)
Utils.$stopPropagation(e)
this.isMove = true
this.pause()
let dx = e.touches[0].pageX - this.start.x
this.playX = this.last.x + dx
this.currentTime = this.last.time + Math.ceil(Math.ceil(dx) / this.step)
if (this.currentTime <= 0) this.currentTime = 0
if (this.currentTime >= this.duration) this.currentTime = this.duration
},
touchend(e) {
// console.log('end', e)
Utils.$stopPropagation(e)
if (this.isPlaying) return
setTimeout(() => (this.isMove = false), 1000)
this.$refs.video.currentTime = this.currentTime
this.play()
}
}
}
</script>
<style scoped lang="less">
.fade-enter-active,
.fade-leave-active {
transition: transform 0.5s linear;
}
.fade-enter-from,
.fade-leave-to {
transform: scale(0);
}
.video-wrapper {
position: relative;
font-size: 14rem;
width: 100%;
height: 100%;
text-align: center;
video {
max-width: 100vw;
height: 100%;
transition:
height,
margin-top 0.3s;
//background: black;
/*position: absolute;*/
}
.float {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
.normal {
position: absolute;
bottom: 0;
width: 100%;
transition: all 0.3s;
.toolbar {
//width: 40px;
position: absolute;
bottom: 0;
right: 5px;
color: #fff;
.avatar-ctn {
position: relative;
.avatar {
width: 55px;
height: 55px;
border-radius: 50%;
}
.options {
position: absolute;
border-radius: 50%;
margin: auto;
left: 0;
right: 0;
bottom: -5px;
background: red;
//background: black;
width: 18px;
height: 18px;
display: flex;
justify-content: center;
align-items: center;
transition: all 1s;
img {
position: absolute;
width: 12px;
height: 12px;
transition: all 1s;
}
.yes {
opacity: 0;
transform: rotate(-180deg);
}
&.attention {
background: white;
.no {
opacity: 0;
transform: rotate(180deg);
}
.yes {
opacity: 1;
transform: rotate(0deg);
}
}
}
}
.love,
.message,
.share {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
@width: 35rem;
img {
width: @width;
height: @width;
}
span {
font-size: 12rem;
}
}
.loved {
background: red;
}
}
.content {
color: #fff;
position: absolute;
bottom: 0;
width: 75%;
//display: flex;
//flex-direction: column;
.location-wrapper {
display: flex;
.location {
margin-bottom: 10rem;
display: flex;
align-items: center;
font-size: 12rem;
padding: 4rem;
border-radius: 3rem;
background: var(--second-btn-color-tran);
.gang {
height: 8rem;
width: 1.5px;
margin: 0 5rem;
background: gray;
}
img {
margin-right: 7rem;
width: 18rem;
}
}
}
.music {
position: relative;
width: 60%;
display: flex;
align-items: center;
.music-image {
width: 20px;
height: 20px;
margin-top: 3px;
}
}
}
.comment-status {
display: flex;
align-items: center;
.comment {
.type-comment {
display: flex;
background: rgb(130, 21, 44);
border-radius: 50px;
padding: 3px;
margin-bottom: 20px;
.avatar {
width: 36px;
height: 36px;
border-radius: 50%;
}
.right {
margin: 0 10px;
color: var(--second-text-color);
.name {
margin-right: 10px;
}
.text {
color: white;
}
}
}
.loveds {
}
.type-loved {
width: 40px;
height: 40px;
position: relative;
margin-bottom: 20px;
animation: test 1s;
animation-delay: 0.5s;
.avatar {
width: 36px;
height: 36px;
border-radius: 50%;
}
.loved {
position: absolute;
bottom: 0;
left: 20px;
width: 10px;
height: 10px;
background: red;
padding: 3px;
border-radius: 50%;
border: 2px solid white;
}
}
@keyframes test {
from {
display: block;
transform: translate3d(0, 0, 0);
}
to {
display: none;
transform: translate3d(0, -60px, 0);
}
}
}
}
}
.progress {
z-index: 10;
@w: 90%;
position: absolute;
bottom: -1rem;
height: 10rem;
left: calc((100% - @w) / 2);
width: @w;
display: flex;
align-items: flex-end;
margin-bottom: 2rem;
.time {
position: absolute;
z-index: 9;
font-size: 24px;
bottom: 50px;
left: 0;
right: 0;
color: white;
text-align: center;
.duration {
color: darkgray;
}
}
@radius: 10rem;
@h: 2rem;
@tr: height 0.3s;
.bg {
transition: @tr;
position: absolute;
width: 100%;
height: @h;
background: #4f4f4f;
border-radius: @radius;
}
@p: 50px;
.progress-line {
transition: @tr;
height: calc(@h + 0.5rem);
width: @p;
border-radius: @radius 0 0 @radius;
background: #777777;
z-index: 1;
}
.point {
transition: all 0.2s;
width: @h+2;
height: @h+2;
border-radius: 50%;
background: gray;
z-index: 2;
transform: translate(-1rem, 1rem);
}
}
& .move {
@h: 10rem;
.bg {
height: @h;
background: var(--active-main-bg);
}
.progress-line {
height: @h;
background: var(--second-text-color);
}
.point {
width: @h+2;
height: @h+2;
background: white;
}
}
& .stop {
@h: 4rem;
.bg {
height: @h;
}
.progress-line {
height: @h;
background: white;
}
.point {
width: @h+2;
height: @h+2;
background: white;
}
}
}
}
.living {
position: absolute;
left: 50%;
font-size: 18rem;
border-radius: 50rem;
border: 1px solid #e0e0e0;
padding: 15rem 20rem;
line-height: 1;
color: white;
top: 70%;
transform: translate(-50%, -50%);
}
</style>

View File

@ -0,0 +1,621 @@
<template>
<div class="video-wrapper" ref="videoWrapper" :class="positionName">
<Loading v-if="state.loading" style="position: absolute" />
<!-- <video :src="item.video + '?v=123'"-->
<video
:src="item.video.play_addr.url_list[0]"
:poster="poster"
ref="videoEl"
:muted="state.isMuted"
preload="true"
loop
x5-video-player-type="h5-page"
:x5-video-player-fullscreen="false"
:webkit-playsinline="true"
:x5-playsinline="true"
:playsinline="true"
:fullscreen="false"
:autoplay="isPlay"
>
<p>您的浏览器不支持 video 标签</p>
</video>
<Icon icon="fluent:play-28-filled" class="pause-icon" v-if="!isPlaying" />
<div class="float">
<template v-if="isLive">
<div class="living">点击进入直播间</div>
<ItemDesc :is-live="true" v-model:item="state.localItem" :position="position" />
</template>
<template v-else>
<div :style="{ opacity: state.isMove ? 0 : 1 }" class="normal">
<template v-if="!state.commentVisible">
<ItemToolbar v-model:item="state.localItem" />
<ItemDesc v-model:item="state.localItem" />
</template>
<div v-if="isMy" class="comment-status">
<div class="comment">
<div class="type-comment">
<img src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar" />
<div class="right">
<p>
<span class="name">zzzzz</span>
<span class="time">2020-01-20</span>
</p>
<p class="text">北京</p>
</div>
</div>
<transition-group name="comment-status" tag="div" class="loveds">
<div class="type-loved" :key="i" v-for="i in state.test">
<img src="../../assets/img/icon/head-image.jpeg" alt="" class="avatar" />
<img src="../../assets/img/icon/love.svg" alt="" class="loved" />
</div>
</transition-group>
</div>
</div>
</div>
<div
class="progress"
:class="progressClass"
ref="progressEl"
@click="null"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend"
>
<div class="time" v-if="state.isMove">
<span class="currentTime">{{ _duration(state.currentTime) }}</span>
<span class="duration"> / {{ _duration(state.duration) }}</span>
</div>
<template v-if="state.duration > 15 || state.isMove || !isPlaying">
<div class="bg"></div>
<div class="progress-line" :style="durationStyle"></div>
<div class="point"></div>
</template>
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { _checkImgUrl, _duration, _stopPropagation } from '@/utils'
import Loading from '../Loading.vue'
import ItemToolbar from './ItemToolbar.vue'
import ItemDesc from './ItemDesc.vue'
import bus, { EVENT_KEY } from '../../utils/bus'
import { SlideItemPlayStatus } from '@/utils/const_var'
import { computed, onMounted, onUnmounted, provide, reactive } from 'vue'
import { Icon } from '@iconify/vue'
import { _css } from '@/utils/dom'
defineOptions({
name: 'BaseVideo'
})
const props = defineProps({
item: {
type: Object,
default: () => {
return {}
}
},
position: {
type: Object,
default: () => {
return {}
}
},
//
isPlay: {
type: Boolean,
default: () => {
return true
}
},
isMy: {
type: Boolean,
default: () => {
return false
}
},
isLive: {
type: Boolean,
default: () => {
return false
}
}
})
provide(
'isPlaying',
computed(() => isPlaying)
)
provide(
'isMuted',
computed(() => state.isMuted)
)
provide(
'position',
computed(() => props.position)
)
provide(
'item',
computed(() => props.item)
)
const videoEl = $ref<HTMLVideoElement>()
const progressEl = $ref<HTMLDivElement>()
let state = reactive({
loading: false,
paused: false,
isMuted: window.isMuted,
status: props.isPlay ? SlideItemPlayStatus.Play : SlideItemPlayStatus.Pause,
duration: 0,
step: 0,
currentTime: -1,
playX: 0,
start: { x: 0 },
last: { x: 0, time: 0 },
height: 0,
width: 0,
isMove: false,
ignoreWaiting: false, //waitingwaiting
test: [1, 2],
localItem: props.item,
progressBarRect: {
height: 0,
width: 0
},
videoScreenHeight: 0,
commentVisible: false
})
const poster = $computed(() => {
return _checkImgUrl(props.item.video.poster ?? props.item.video.cover.url_list[0])
})
const durationStyle = $computed(() => {
return { width: state.playX + 'px' }
})
const isPlaying = $computed(() => {
return state.status === SlideItemPlayStatus.Play
})
const positionName = $computed(() => {
return 'item-' + Object.values(props.position).join('-')
})
const progressClass = $computed(() => {
if (state.isMove) {
return 'move'
} else {
return isPlaying ? '' : 'stop'
}
})
onMounted(() => {
// console.log('video', this.localItem.aweme_id)
// console.log(this.commentVisible)
state.height = document.body.clientHeight
state.width = document.body.clientWidth
videoEl.currentTime = 0
let fun = (e) => {
state.currentTime = Math.ceil(e.target.currentTime)
state.playX = (state.currentTime - 1) * state.step
}
videoEl.addEventListener('loadedmetadata', () => {
state.videoScreenHeight = videoEl.videoHeight / (videoEl.videoWidth / state.width)
state.duration = videoEl.duration
state.progressBarRect = progressEl.getBoundingClientRect()
state.step = state.progressBarRect.width / Math.floor(state.duration)
videoEl.addEventListener('timeupdate', fun)
})
let eventTester = (e, t: string) => {
videoEl.addEventListener(
e,
() => {
// console.log('eventTester', e, state.item.aweme_id)
if (e === 'playing') state.loading = false
if (e === 'waiting') {
if (!state.paused && !state.ignoreWaiting) {
state.loading = true
}
}
let s = false
if (s) {
console.log(e, t)
}
},
false
)
}
// eventTester("loadstart", ''); //
// eventTester("abort", ''); //
// eventTester("loadstart", ''); //
// eventTester("progress", ''); //
// // eventTester("suspend", ''); //
// eventTester("abort", ''); //
// eventTester("error", ''); //
// eventTester("stalled", ''); //
// eventTester("play", 'play()autoplay'); //play()autoplay
// eventTester("pause", 'pause()'); //pause()
// eventTester("loadedmetadata", ''); //
// eventTester("loadeddata"); //
eventTester('waiting', '等待数据,并非错误') //
eventTester('playing', '开始回放') //
// eventTester("canplay", '/'); //
// eventTester("canplaythrough", ''); //
// eventTester("seeking", ''); //
// eventTester("seeked", ''); //
// // eventTester("timeupdate",''); //
// eventTester("ended", ''); //
// eventTester("ratechange", ''); //
// eventTester("durationchange", ''); //
// eventTester("volumechange", ''); //
// console.log('mounted')
// bus.off('singleClickBroadcast')
bus.on(EVENT_KEY.SINGLE_CLICK_BROADCAST, click)
bus.on(EVENT_KEY.DIALOG_MOVE, onDialogMove)
bus.on(EVENT_KEY.DIALOG_END, onDialogEnd)
bus.on(EVENT_KEY.OPEN_COMMENTS, onOpenComments)
bus.on(EVENT_KEY.CLOSE_COMMENTS, onCloseComments)
bus.on(EVENT_KEY.OPEN_SUB_TYPE, onOpenSubType)
bus.on(EVENT_KEY.CLOSE_SUB_TYPE, onCloseSubType)
bus.on(EVENT_KEY.REMOVE_MUTED, removeMuted)
})
onUnmounted(() => {
// console.log('unmounted')
bus.off(EVENT_KEY.SINGLE_CLICK_BROADCAST, click)
bus.off(EVENT_KEY.DIALOG_MOVE, onDialogMove)
bus.off(EVENT_KEY.DIALOG_END, onDialogEnd)
bus.off(EVENT_KEY.OPEN_COMMENTS, onOpenComments)
bus.off(EVENT_KEY.CLOSE_COMMENTS, onCloseComments)
bus.off(EVENT_KEY.OPEN_SUB_TYPE, onOpenSubType)
bus.off(EVENT_KEY.CLOSE_SUB_TYPE, onCloseSubType)
bus.off(EVENT_KEY.REMOVE_MUTED, removeMuted)
})
function removeMuted() {
state.isMuted = false
}
function onOpenSubType() {
state.commentVisible = true
}
function onCloseSubType() {
state.commentVisible = false
}
function onDialogMove({ tag, e }) {
if (state.commentVisible && tag === 'comment') {
_css(videoEl, 'transition-duration', `0ms`)
_css(videoEl, 'height', `calc(var(--vh, 1vh) * 30 + ${e}px)`)
}
}
function onDialogEnd({ tag, isClose }) {
if (state.commentVisible && tag === 'comment') {
console.log('isClose', isClose)
_css(videoEl, 'transition-duration', `300ms`)
if (isClose) {
state.commentVisible = false
_css(videoEl, 'height', '100%')
} else {
_css(videoEl, 'height', 'calc(var(--vh, 1vh) * 30)')
}
}
}
function onOpenComments(id) {
if (id === props.item.aweme_id) {
_css(videoEl, 'transition-duration', `300ms`)
_css(videoEl, 'height', 'calc(var(--vh, 1vh) * 30)')
state.commentVisible = true
}
}
function onCloseComments() {
if (state.commentVisible) {
_css(videoEl, 'transition-duration', `300ms`)
_css(videoEl, 'height', '100%')
state.commentVisible = false
}
}
function click({ uniqueId, index, type }) {
if (props.position.uniqueId === uniqueId && props.position.index === index) {
if (type === EVENT_KEY.ITEM_TOGGLE) {
if (props.isLive) {
pause()
bus.emit(EVENT_KEY.NAV, {
path: '/home/live',
query: { id: props.item.aweme_id }
})
} else {
if (state.status === SlideItemPlayStatus.Play) {
pause()
} else {
play()
}
}
}
if (type === EVENT_KEY.ITEM_STOP) {
videoEl.currentTime = 0
state.ignoreWaiting = true
pause()
setTimeout(() => (state.ignoreWaiting = false), 300)
}
if (type === EVENT_KEY.ITEM_PLAY) {
videoEl.currentTime = 0
state.ignoreWaiting = true
play()
setTimeout(() => (state.ignoreWaiting = false), 300)
}
}
}
function play() {
state.status = SlideItemPlayStatus.Play
videoEl.volume = 1
videoEl.play()
}
function pause() {
state.status = SlideItemPlayStatus.Pause
videoEl.pause()
}
function touchstart(e) {
_stopPropagation(e)
state.start.x = e.touches[0].pageX
state.last.x = state.playX
state.last.time = state.currentTime
}
function touchmove(e) {
// console.log('move',e)
_stopPropagation(e)
state.isMove = true
pause()
let dx = e.touches[0].pageX - state.start.x
state.playX = state.last.x + dx
state.currentTime = state.last.time + Math.ceil(Math.ceil(dx) / state.step)
if (state.currentTime <= 0) state.currentTime = 0
if (state.currentTime >= state.duration) state.currentTime = state.duration
}
function touchend(e) {
// console.log('end', e)
_stopPropagation(e)
if (isPlaying) return
setTimeout(() => (state.isMove = false), 1000)
videoEl.currentTime = state.currentTime
play()
}
</script>
<style scoped lang="less">
.video-wrapper {
position: relative;
font-size: 14rem;
width: 100%;
height: 100%;
text-align: center;
video {
max-width: 100%;
height: 100%;
transition:
height,
margin-top 0.3s;
//background: black;
/*position: absolute;*/
}
.float {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
.normal {
position: absolute;
bottom: 0;
width: 100%;
transition: all 0.3s;
.comment-status {
display: flex;
align-items: center;
.comment {
.type-comment {
display: flex;
background: rgb(130, 21, 44);
border-radius: 50px;
padding: 3px;
margin-bottom: 20px;
.avatar {
width: 36px;
height: 36px;
border-radius: 50%;
}
.right {
margin: 0 10px;
color: var(--second-text-color);
.name {
margin-right: 10px;
}
.text {
color: white;
}
}
}
.loveds {
}
.type-loved {
width: 40px;
height: 40px;
position: relative;
margin-bottom: 20px;
animation: test 1s;
animation-delay: 0.5s;
.avatar {
width: 36px;
height: 36px;
border-radius: 50%;
}
.loved {
position: absolute;
bottom: 0;
left: 20px;
width: 10px;
height: 10px;
background: red;
padding: 3px;
border-radius: 50%;
border: 2px solid white;
}
}
@keyframes test {
from {
display: block;
transform: translate3d(0, 0, 0);
}
to {
display: none;
transform: translate3d(0, -60px, 0);
}
}
}
}
}
.progress {
z-index: 10;
@w: 90%;
position: absolute;
bottom: -1rem;
height: 10rem;
left: calc((100% - @w) / 2);
width: @w;
display: flex;
align-items: flex-end;
margin-bottom: 2rem;
.time {
position: absolute;
z-index: 9;
font-size: 24px;
bottom: 50px;
left: 0;
right: 0;
color: white;
text-align: center;
.duration {
color: darkgray;
}
}
@radius: 10rem;
@h: 2rem;
@tr: height 0.3s;
.bg {
transition: @tr;
position: absolute;
width: 100%;
height: @h;
background: #4f4f4f;
border-radius: @radius;
}
@p: 50px;
.progress-line {
transition: @tr;
height: calc(@h + 0.5rem);
width: @p;
border-radius: @radius 0 0 @radius;
background: #777777;
z-index: 1;
}
.point {
transition: all 0.2s;
width: @h+2;
height: @h+2;
border-radius: 50%;
background: gray;
z-index: 2;
transform: translate(-1rem, 1rem);
}
}
& .move {
@h: 10rem;
.bg {
height: @h;
background: var(--active-main-bg);
}
.progress-line {
height: @h;
background: var(--second-text-color);
}
.point {
width: @h+2;
height: @h+2;
background: white;
}
}
& .stop {
@h: 4rem;
.bg {
height: @h;
}
.progress-line {
height: @h;
background: white;
}
.point {
width: @h+2;
height: @h+2;
background: white;
}
}
}
}
.living {
position: absolute;
left: 50%;
font-size: 18rem;
border-radius: 50rem;
border: 1px solid #e0e0e0;
padding: 15rem 20rem;
line-height: 1;
color: white;
top: 70%;
transform: translate(-50%, -50%);
}
</style>

View File

@ -1,6 +1,7 @@
<script lang="jsx">
import bus from '../../utils/bus'
import { useBaseStore } from '@/store/pinia'
import { _css } from '@/utils/dom'
export default {
name: 'Indicator',
@ -91,8 +92,8 @@ export default {
this.currentSlideItemIndex = index
this.$attrs['onUpdate:activeIndex'] &&
this.$emit('update:active-index', this.currentSlideItemIndex)
this.$setCss(this.indicatorRef, 'transition-duration', `300ms`)
this.$setCss(
_css(this.indicatorRef, 'transition-duration', `300ms`)
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] + 'px'
@ -103,7 +104,7 @@ export default {
this.indicatorRef = this.$refs.indicator
for (let i = 0; i < tabs.children.length; i++) {
let item = tabs.children[i]
this.tabWidth = this.$getCss(item, 'width')
this.tabWidth = _css(item, 'width')
this.tabIndicatorRelationActiveIndexLefts.push(
item.getBoundingClientRect().x -
tabs.children[0].getBoundingClientRect().x +
@ -112,15 +113,15 @@ export default {
}
this.indicatorSpace =
this.tabIndicatorRelationActiveIndexLefts[1] - this.tabIndicatorRelationActiveIndexLefts[0]
this.$setCss(this.indicatorRef, 'transition-duration', `0ms`)
this.$setCss(
_css(this.indicatorRef, 'transition-duration', `0ms`)
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] + 'px'
)
},
move(e) {
this.$setCss(
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] -
@ -131,14 +132,14 @@ export default {
end(index) {
// console.log(index)
this.currentSlideItemIndex = index
this.$setCss(this.indicatorRef, 'transition-duration', `300ms`)
this.$setCss(
_css(this.indicatorRef, 'transition-duration', `300ms`)
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] + 'px'
)
setTimeout(() => {
this.$setCss(this.indicatorRef, 'transition-duration', `0ms`)
_css(this.indicatorRef, 'transition-duration', `0ms`)
}, 300)
}
}

View File

@ -1,6 +1,7 @@
<script lang="jsx">
import bus from '../../utils/bus'
import { useBaseStore } from '@/store/pinia'
import { _css } from '@/utils/dom'
export default {
name: 'IndicatorLight',
@ -85,8 +86,8 @@ export default {
this.currentSlideItemIndex = index
this.$attrs['onUpdate:activeIndex'] &&
this.$emit('update:active-index', this.currentSlideItemIndex)
this.$setCss(this.indicatorRef, 'transition-duration', `300ms`)
this.$setCss(
_css(this.indicatorRef, 'transition-duration', `300ms`)
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] + 'px'
@ -95,10 +96,10 @@ export default {
initTabs() {
let tabs = this.$refs.tabs
this.indicatorRef = this.$refs.indicator
let indicatorWidth = this.$getCss(this.indicatorRef, 'width')
let indicatorWidth = _css(this.indicatorRef, 'width')
for (let i = 0; i < tabs.children.length; i++) {
let item = tabs.children[i]
this.tabWidth = this.$getCss(item, 'width')
this.tabWidth = _css(item, 'width')
this.tabIndicatorRelationActiveIndexLefts.push(
item.getBoundingClientRect().x -
tabs.children[0].getBoundingClientRect().x +
@ -108,15 +109,15 @@ export default {
this.indicatorSpace =
this.tabIndicatorRelationActiveIndexLefts[1] - this.tabIndicatorRelationActiveIndexLefts[0]
this.$setCss(this.indicatorRef, 'transition-duration', `0ms`)
this.$setCss(
_css(this.indicatorRef, 'transition-duration', `0ms`)
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] + 'px'
)
},
move(e) {
this.$setCss(
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] -
@ -127,14 +128,14 @@ export default {
end(index) {
// console.log(index)
this.currentSlideItemIndex = index
this.$setCss(this.indicatorRef, 'transition-duration', `300ms`)
this.$setCss(
_css(this.indicatorRef, 'transition-duration', `300ms`)
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] + 'px'
)
setTimeout(() => {
this.$setCss(this.indicatorRef, 'transition-duration', `0ms`)
_css(this.indicatorRef, 'transition-duration', `0ms`)
}, 300)
}
}

View File

@ -1,19 +1,7 @@
<script setup>
import { reactive } from 'vue'
<script setup lang="ts">
import { inject, reactive } from 'vue'
const props = defineProps({
item: {
type: Object,
default: () => {
return {}
}
},
position: {
type: Object,
default: () => {
return {}
}
},
isMy: {
type: Boolean,
default: () => {
@ -28,6 +16,8 @@ const props = defineProps({
}
})
const item = inject<any>('item')
const state = reactive({
isAttention: false,
test: [1, 2]
@ -36,26 +26,26 @@ const state = reactive({
<template>
<div class="item-desc ml1r mb1r">
<div class="content" v-if="!props.isMy">
<div class="location-wrapper" v-if="props.item.city || item.address">
<div class="location-wrapper" v-if="item.city || item.address">
<div class="location">
<img src="../../assets/img/icon/location.webp" alt="" />
<span>{{ props.item.city }}</span>
<template v-if="props.item.address">
<span>{{ item.city }}</span>
<template v-if="item.address">
<div class="gang"></div>
</template>
<span>{{ props.item.address }}</span>
<span>{{ item.address }}</span>
</div>
</div>
<div class="live" v-if="props.isLive">直播中</div>
<div class="name mb1r f18 fb" @click.stop="$emit('goUserInfo')">
@{{ props.item.author.nickname }}
@{{ item.author.nickname }}
</div>
<div class="description">
{{ props.item.desc }}
{{ item.desc }}
</div>
<!-- <div class="music" @click.stop="bus.emit('nav','/home/music')">-->
<!-- <img src="../../assets/img/icon/music.svg" alt="" class="music-image">-->
<!-- <span>{{ props.item.music.title }}</span>-->
<!-- <span>{{ item.music.title }}</span>-->
<!-- </div>-->
</div>
<div v-else class="comment-status">
@ -85,10 +75,10 @@ const state = reactive({
.item-desc {
position: absolute;
bottom: 0;
width: 70%;
.content {
color: #fff;
width: 75vw;
text-align: left;
.location-wrapper {

View File

@ -1,46 +1,53 @@
<script setup>
import BaseMusic from '../BaseMusic'
import Utils from '../../utils'
<script setup lang="ts">
import BaseMusic from '../BaseMusic.vue'
import { _formatNumber, cloneDeep } from '@/utils'
import bus, { EVENT_KEY } from '@/utils/bus'
import { Icon } from '@iconify/vue'
import { useClick } from '@/utils/hooks/useClick'
import { inject } from 'vue'
const props = defineProps({
item: {
type: Object,
default: () => {
return {}
}
},
position: {
type: Object,
default: () => {
return {}
}
},
isMy: {
type: Boolean,
default: () => {
return false
}
},
item: {
type: Object,
default: () => {
return {}
}
}
})
const position = inject<any>('position')
const emit = defineEmits(['update:item', 'goUserInfo', 'showComments', 'showShare', 'goMusic'])
function _updateItem(props, key, val) {
const old = cloneDeep(props.item)
old[key] = val
emit('update:item', old)
bus.emit(EVENT_KEY.UPDATE_ITEM, { position: position.value, item: old })
}
function loved() {
Utils.updateItem(props, 'isLoved', !props.item.isLoved, emit)
_updateItem(props, 'isLoved', !props.item.isLoved)
}
function attention(e) {
e.currentTarget.classList.add('attention')
setTimeout(() => {
Utils.updateItem(props, 'isAttention', true, emit)
_updateItem(props, 'isAttention', true)
}, 1000)
}
function showComments() {
// emit('showComments')
bus.emit(EVENT_KEY.OPEN_COMMENTS, props.item.id)
bus.emit(EVENT_KEY.OPEN_COMMENTS, props.item.aweme_id)
}
const vClick = useClick()
</script>
<template>
@ -48,49 +55,51 @@ function showComments() {
<div class="avatar-ctn mb2r">
<img
class="avatar"
:src="props.item.author.avatar_168x168.url_list[0]"
:src="item.author.avatar_168x168.url_list[0]"
alt=""
@click.stop="bus.emit(EVENT_KEY.GO_USERINFO)"
v-click="() => bus.emit(EVENT_KEY.GO_USERINFO)"
/>
<transition name="fade">
<div v-if="!props.item.isAttention" @click.stop="attention" class="options">
<div v-if="!item.isAttention" v-click="attention" class="options">
<img class="no" src="../../assets/img/icon/add-light.png" alt="" />
<img class="yes" src="../../assets/img/icon/ok-red.png" alt="" />
</div>
</transition>
</div>
<div class="love mb2r" @click.stop="loved($event)">
<div class="love mb2r" v-click="loved">
<div>
<img src="../../assets/img/icon/love.svg" class="love-image" v-if="!props.item.isLoved" />
<img src="../../assets/img/icon/loved.svg" class="love-image" v-if="props.item.isLoved" />
<img src="../../assets/img/icon/love.svg" class="love-image" v-if="!item.isLoved" />
<img src="../../assets/img/icon/loved.svg" class="love-image" v-if="item.isLoved" />
</div>
<span>{{ Utils.formatNumber(props.item.statistics.digg_count) }}</span>
<span>{{ _formatNumber(item.statistics.digg_count) }}</span>
</div>
<div class="message mb2r" @click.stop="showComments">
<div class="message mb2r" v-click="showComments">
<Icon icon="mage:message-dots-round-fill" class="icon" style="color: white" />
<span>{{ Utils.formatNumber(props.item.statistics.comment_count) }}</span>
<span>{{ _formatNumber(item.statistics.comment_count) }}</span>
</div>
<!--TODO -->
<div
class="message mb2r"
@click.stop="Utils.updateItem(props, 'isCollect', !props.item.isCollect, emit)"
>
<Icon v-if="props.item.isCollect" icon="ic:round-star" class="icon" style="color: yellow" />
<div class="message mb2r" v-click="() => _updateItem(props, 'isCollect', !item.isCollect)">
<Icon
v-if="item.isCollect"
icon="ic:round-star"
class="icon"
style="color: rgb(252, 179, 3)"
/>
<Icon v-else icon="ic:round-star" class="icon" style="color: white" />
<span>{{ Utils.formatNumber(props.item.statistics.comment_count) }}</span>
<span>{{ _formatNumber(item.statistics.comment_count) }}</span>
</div>
<div v-if="!props.isMy" class="share mb2r" @click.stop="bus.emit(EVENT_KEY.SHOW_SHARE)">
<div v-if="!props.isMy" class="share mb2r" v-click="() => bus.emit(EVENT_KEY.SHOW_SHARE)">
<img src="../../assets/img/icon/share-white-full.png" alt="" class="share-image" />
<span>{{ Utils.formatNumber(props.item.statistics.share_count) }}</span>
<span>{{ _formatNumber(item.statistics.share_count) }}</span>
</div>
<div v-else class="share mb2r" @click.stop="bus.emit(EVENT_KEY.SHOW_SHARE)">
<div v-else class="share mb2r" v-click="() => bus.emit(EVENT_KEY.SHOW_SHARE)">
<img src="../../assets/img/icon/menu-white.png" alt="" class="share-image" />
</div>
<!-- <BaseMusic-->
<!-- :cover="props.item.music.cover"-->
<!-- @click.stop="$nav('/home/music')"-->
<!-- :cover="item.music.cover"-->
<!-- v-click="$router.push('/home/music')"-->
<!-- /> -->
<BaseMusic :item="props.item" />
<BaseMusic />
</div>
</template>
@ -99,8 +108,11 @@ function showComments() {
//width: 40px;
position: absolute;
bottom: 0;
right: 5px;
right: 10rem;
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
.avatar-ctn {
position: relative;
@ -123,8 +135,8 @@ function showComments() {
bottom: -5px;
background: red;
//background: black;
width: 18px;
height: 18px;
width: 18rem;
height: 18rem;
display: flex;
justify-content: center;
align-items: center;
@ -132,8 +144,8 @@ function showComments() {
img {
position: absolute;
width: 12px;
height: 12px;
width: 14rem;
height: 14rem;
transition: all 1s;
}

View File

@ -3,7 +3,7 @@
<div class="img-slide-wrapper">
<div
class="img-slide-list"
ref="wrapperEl"
ref="slideListEl"
@touchstart.passive="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
@ -23,13 +23,13 @@
/>
<template v-if="state.operationStatus === SlideAlbumOperationStatus.Normal">
<ItemToolbar
class="mb3r"
v-model:item="state.localItem"
:position="position"
v-bind="$attrs"
/>
<ItemDesc class="mb3r" v-model:item="state.localItem" :position="position" />
<!-- <ItemToolbar-->
<!-- class="mb3r"-->
<!-- v-model:item="state.localItem"-->
<!-- :position="position"-->
<!-- v-bind="$attrs"-->
<!-- />-->
<!-- <ItemDesc class="mb3r" v-model:item="state.localItem" :position="position" />-->
</template>
<!--不知为啥touch事件在下部20px的空间内不触发加上click事件不好了 -->
<div
@ -70,7 +70,7 @@
/>
</div>
<div class="right">
<Icon icon="heroicons-outline:menu-alt-1" @click="Utils.$no" />
<Icon icon="heroicons-outline:menu-alt-1" @click="Utils._no" />
<Icon
icon="fluent:play-28-filled"
v-if="state.status === SlideItemPlayStatus.Pause"
@ -78,7 +78,7 @@
@click="startPlay"
/>
<Icon icon="bi:pause-fill" v-else class="pause" @click="stopPlay" />
<Icon icon="system-uicons:push-down" @click="$notice('已保存到系统相册')" />
<Icon icon="system-uicons:push-down" @click="_notice('已保存到系统相册')" />
</div>
</div>
</Teleport>
@ -88,7 +88,7 @@
<script setup lang="jsx">
import enums from '../../utils/enums'
import Utils from '../../utils'
import GM, { $notice } from '../../utils'
import GM, { _notice } from '../../utils'
import { mat4 } from 'gl-matrix'
import { Icon } from '@iconify/vue'
import {
@ -106,15 +106,12 @@ import {
slideInit,
slideReset,
slideTouchEnd,
slidePointerMove,
slidePointerDown
slideTouchMove,
slideTouchStart
} from '@/utils/slide'
import { SlideAlbumOperationStatus, SlideItemPlayStatus, SlideType } from '../../utils/const_var'
import ItemToolbar from './ItemToolbar'
import ItemDesc from './ItemDesc'
import { cloneDeep } from '@/utils'
import bus, { EVENT_KEY } from '../../utils/bus'
import $ from 'jquery'
let out = new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
let ov = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
@ -236,8 +233,7 @@ const props = defineProps({
}
}
})
const judgeValue = 20
const wrapperEl = ref(null)
const slideListEl = ref(null)
//用于解决touch事件触发startPlay,然后click事件又触发stopLoop的问题
let lockDatetime = 0
@ -305,7 +301,7 @@ function startLoop() {
onMounted(async () => {
await nextTick()
slideInit(wrapperEl.value, state, SlideType.HORIZONTAL)
slideInit(slideListEl.value, state, SlideType.HORIZONTAL)
startPlay()
// setTimeout(() => {
// state.operationStatus = SlideAlbumOperationStatus.Zooming
@ -352,11 +348,11 @@ onBeforeUpdate(() => {
watch(
() => state.localIndex,
() => {
GM.$setCss(wrapperEl.value, 'transition-duration', `300ms`)
GM.$setCss(slideListEl.value, 'transition-duration', `300ms`)
GM.$setCss(
wrapperEl.value,
slideListEl.value,
'transform',
`translate3d(${getSlideOffset(state, wrapperEl.value)}px, 0, 0)`
`translate3d(${getSlideOffset(state, slideListEl.value)}px, 0, 0)`
)
}
)
@ -410,7 +406,7 @@ function touchStart(e) {
// Utils.$showNoticeDialog('start'+e.touches.length)
console.log('start', e.touches.length)
if (e.touches.length === 1) {
slidePointerDown(e, wrapperEl.value, state)
slideTouchStart(e, slideListEl.value, state)
} else {
if (state.operationStatus === SlideAlbumOperationStatus.Zooming) {
// state.start.center = Utils.getCenter(state.start.point1, state.start.point2)
@ -431,6 +427,9 @@ function touchStart(e) {
}
function touchMove(e) {
const s = true
if (s) return
// Utils.$showNoticeDialog('move'+e.touches.length)
// console.log('move', e.touches.length, state.operationStatus)
let current1 = { x: e.touches[0].pageX, y: e.touches[0].pageY }
@ -453,9 +452,9 @@ function touchMove(e) {
} else {
// console.log('m2')
state.isAutoPlay = false
slidePointerMove(
slideTouchMove(
e,
wrapperEl.value,
slideListEl.value,
state,
canNext,
() => {
@ -482,10 +481,11 @@ function touchMove(e) {
if (rectMap.has(state.localIndex)) {
rect = rectMap.get(state.localIndex)
} else {
//TODO 这里去掉jquery
//getBoundingClientRect在手机上获取不到值
let offset = $(state.itemRefs[state.localIndex]).offset()
rect = { x: offset.left, y: offset.top }
rectMap.set(state.localIndex, rect)
// let offset = $(state.itemRefs[state.localIndex]).offset()
// rect = { x: offset.left, y: offset.top }
// rectMap.set(state.localIndex, rect)
}
let current2 = { x: e.touches[1].pageX, y: e.touches[1].pageY }
@ -580,7 +580,7 @@ function touchEnd(e) {
startLoop()
}
)
slideReset(wrapperEl.value, state)
slideReset(e, slideListEl.value, state)
}
}
}

View File

@ -1,15 +1,15 @@
<script setup lang="ts">
import { onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import GM from '../../utils'
import {
getSlideOffset,
slideInit,
slidePointerDown,
slidePointerMove,
slideReset,
slideTouchEnd
slideTouchEnd,
slideTouchMove,
slideTouchStart
} from '@/utils/slide'
import { SlideType } from '@/utils/const_var'
import { _css } from '@/utils/dom'
const props = defineProps({
index: {
@ -22,6 +22,14 @@ const props = defineProps({
type: String,
default: () => ''
},
autoplay: {
type: Boolean,
default: () => false
},
indicator: {
type: Boolean,
default: () => false
},
//index使
changeActiveIndexUseAnim: {
type: Boolean,
@ -31,16 +39,20 @@ const props = defineProps({
const emit = defineEmits(['update:index'])
let ob = null
const wrapperEl = ref(null)
//slide-listref
const slideListEl = ref(null)
const state = reactive({
judgeValue: 20,
type: SlideType.HORIZONTAL,
judgeValue: 20, //
type: SlideType.HORIZONTAL, //
name: props.name,
localIndex: props.index,
needCheck: true,
next: false,
start: { x: 0, y: 0, time: 0 },
move: { x: 0, y: 0 },
localIndex: props.index, //
needCheck: true, //uptrue
next: false, //
isDown: false, //move
start: { x: 0, y: 0, time: 0 }, //
move: { x: 0, y: 0 }, //
//slide-list
wrapper: {
width: 0,
height: 0,
@ -55,56 +67,100 @@ watch(
if (state.localIndex !== newVal) {
state.localIndex = newVal
if (props.changeActiveIndexUseAnim) {
GM.$setCss(wrapperEl.value, 'transition-duration', `300ms`)
_css(slideListEl.value, 'transition-duration', `300ms`)
}
GM.$setCss(
wrapperEl.value,
_css(
slideListEl.value,
'transform',
`translate3d(${getSlideOffset(state, wrapperEl.value)}px, 0, 0)`
`translate3d(${getSlideOffset(state, slideListEl.value)}px, 0, 0)`
)
}
}
)
onMounted(() => {
slideInit(wrapperEl.value, state)
slideInit(slideListEl.value, state)
if (props.autoplay) {
setInterval(() => {
if (state.localIndex === state.wrapper.childrenLength - 1) {
emit('update:index', 0)
} else {
emit('update:index', state.localIndex + 1)
}
}, 3000)
}
//
//childrenLengthcanNext
ob = new MutationObserver(() => {
state.wrapper.childrenLength = wrapperEl.value.children.length
state.wrapper.childrenLength = slideListEl.value.children.length
})
ob.observe(wrapperEl.value, { childList: true })
ob.observe(slideListEl.value, { childList: true })
})
onUnmounted(() => {
ob.disconnect()
})
function touchStart(e: TouchEvent) {
slidePointerDown(e, wrapperEl.value, state)
function touchStart(e) {
slideTouchStart(e, slideListEl.value, state)
}
function touchMove(e: TouchEvent) {
slidePointerMove(e, wrapperEl.value, state)
function touchMove(e) {
slideTouchMove(e, slideListEl.value, state)
}
function touchEnd(e: TouchEvent) {
function touchEnd(e) {
slideTouchEnd(e, state)
slideReset(wrapperEl.value, state, emit)
slideReset(e, slideListEl.value, state, emit)
}
</script>
<template>
<div class="slide horizontal">
<div class="indicator-bullets" v-if="indicator && state.wrapper.childrenLength">
<div
class="bullet"
:class="{ active: state.localIndex === item - 1 }"
:key="i"
v-for="(item, i) in state.wrapper.childrenLength"
></div>
</div>
<div
class="slide-list"
ref="wrapperEl"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
ref="slideListEl"
@pointerdown.prevent="touchStart"
@pointermove.prevent="touchMove"
@pointerup.prevent="touchEnd"
>
<slot></slot>
</div>
</div>
</template>
<style scoped lang="less">
.indicator-bullets {
position: absolute;
bottom: 10rem;
z-index: 2;
left: 50%;
transform: translateX(-50%);
display: flex;
justify-content: center;
gap: 7rem;
.bullet {
@width: 5rem;
width: @width;
height: @width;
border-radius: 50%;
background: var(--second-btn-color);
&.active {
background: white;
}
}
}
</style>

View File

@ -25,6 +25,8 @@
<script>
import bus from '../../utils/bus'
import { useBaseStore } from '@/store/pinia'
import { _css } from '@/utils/dom'
import { _stopPropagation } from '@/utils'
export default {
name: 'BaseSlideList',
@ -157,14 +159,14 @@ export default {
methods: {
changeIndex(init = false, index = null) {
this.currentSlideItemIndex = index !== null ? index : this.activeIndex
!init && this.$setCss(this.slideList, 'transition-duration', `300ms`)
this.$setCss(
!init && _css(this.slideList, 'transition-duration', `300ms`)
_css(
this.slideList,
'transform',
`translate3d(${-this.getWidth(this.currentSlideItemIndex) + this.moveXDistance}px, 0px, 0px)`
)
if (this.isHome) {
this.$setCss(
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] + 'px'
@ -178,7 +180,7 @@ export default {
this.indicatorRef = this.$refs.indicator
for (let i = 0; i < tabs.children.length; i++) {
let item = tabs.children[i]
this.tabWidth = this.$getCss(item, 'width')
this.tabWidth = _css(item, 'width')
//TODO IndicatorLight.vue
this.tabIndicatorRelationActiveIndexLefts.push(
item.getBoundingClientRect().x -
@ -190,8 +192,8 @@ export default {
this.indicatorSpace =
this.tabIndicatorRelationActiveIndexLefts[1] - this.tabIndicatorRelationActiveIndexLefts[0]
if (this.isHome) {
this.$setCss(this.indicatorRef, 'transition-duration', `300ms`)
this.$setCss(
_css(this.indicatorRef, 'transition-duration', `300ms`)
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] + 'px'
@ -201,16 +203,16 @@ export default {
async checkChildren() {
this.slideList = this.$refs.slideList
this.slideItems = this.slideList.children
this.wrapperWidth = this.$getCss(this.slideList, 'width')
this.wrapperHeight = this.$getCss(this.slideList, 'height')
this.wrapperWidth = _css(this.slideList, 'width')
this.wrapperHeight = _css(this.slideList, 'height')
for (let i = 0; i < this.slideItems.length; i++) {
let el = this.slideItems[i]
this.slideItemsWidths.push(this.$getCss(el, 'width'))
this.slideItemsWidths.push(_css(el, 'width'))
}
},
touchStart(e) {
this.$setCss(this.slideList, 'transition-duration', `0ms`)
this.isHome && this.$setCss(this.indicatorRef, 'transition-duration', `0ms`)
_css(this.slideList, 'transition-duration', `0ms`)
this.isHome && _css(this.indicatorRef, 'transition-duration', `0ms`)
this.toolbarStyleTransitionDuration = 0
this.startLocationX = e.touches[0].pageX
@ -218,7 +220,6 @@ export default {
this.startTime = Date.now()
},
touchMove(e) {
// this.$stopPropagation(e)
if (!this.canMove) return
this.moveXDistance = e.touches[0].pageX - this.startLocationX
this.moveYDistance = e.touches[0].pageY - this.startLocationY
@ -244,8 +245,8 @@ export default {
x: { distance: this.moveXDistance, isDrawRight: this.isDrawRight }
})
this.$stopPropagation(e)
this.$setCss(
_stopPropagation(e)
_css(
this.slideList,
'transform',
`translate3d(${
@ -256,7 +257,7 @@ export default {
)
this.isHome &&
this.$setCss(
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] -
@ -278,8 +279,8 @@ export default {
if (this.currentSlideItemIndex === 0 && !this.isDrawRight) return
if (this.currentSlideItemIndex === this.slideItems.length - 1 && this.isDrawRight) return
this.$setCss(this.slideList, 'transition-duration', `300ms`)
this.isHome && this.$setCss(this.indicatorRef, 'transition-duration', `300ms`)
_css(this.slideList, 'transition-duration', `300ms`)
this.isHome && _css(this.indicatorRef, 'transition-duration', `300ms`)
let endTime = Date.now()
let gapTime = endTime - this.startTime
@ -287,7 +288,7 @@ export default {
// this.$stopPropagation(e)//todo slide,moveYDistance
//this.getWidth(this.currentSlideItemIndex)
if (this.moveXDistance !== 0) {
this.$stopPropagation(e)
_stopPropagation(e)
}
if (Math.abs(this.moveXDistance) < 20) gapTime = 1000
if (Math.abs(this.moveXDistance) > this.wrapperWidth / 3) gapTime = 100
@ -298,13 +299,13 @@ export default {
this.currentSlideItemIndex -= 1
}
}
this.$setCss(
_css(
this.slideList,
'transform',
`translate3d(${-this.getWidth(this.currentSlideItemIndex)}px, 0px, 0px)`
)
if (this.isHome) {
this.$setCss(
_css(
this.indicatorRef,
'left',
this.tabIndicatorRelationActiveIndexLefts[this.currentSlideItemIndex] + 'px'

View File

@ -20,12 +20,12 @@
class="poster-item"
:key="index"
v-for="(i, index) in modelValue.videos.slice(0, 3)"
@click="globalMethods.$no"
@click="_no"
>
<img class="poster" :src="globalMethods.$imgPreview(i.cover)" />
<img class="poster" :src="_checkImgUrl(i.cover)" />
<div class="num">
<img class="love" src="../../assets/img/icon/love.svg" alt="" />
<span>{{ globalMethods.formatNumber(i.digg_count) }}</span>
<span>{{ _formatNumber(i.digg_count) }}</span>
</div>
</div>
</div>
@ -37,7 +37,7 @@
</div>
</template>
<script>
import globalMethods from '../../utils'
import { _checkImgUrl, _formatNumber, _no } from '../../utils'
import BaseButton from '../BaseButton'
export default {
@ -240,17 +240,12 @@ export default {
}
}
},
data() {
return {
globalMethods
}
},
computed: {},
watch: {},
created() {
console.log('modelValue', this.modelValue)
},
methods: {}
methods: { _formatNumber, _checkImgUrl, _no }
}
</script>

View File

@ -1,15 +1,15 @@
<script setup>
import { onMounted, reactive, ref, watch } from 'vue'
import GM from '../../utils'
import {
getSlideOffset,
slideInit,
slideReset,
slideTouchEnd,
slidePointerMove,
slidePointerDown
slideTouchMove,
slideTouchStart
} from '@/utils/slide'
import { SlideType } from '@/utils/const_var'
import { _css } from '@/utils/dom'
const props = defineProps({
index: {
@ -22,21 +22,34 @@ const props = defineProps({
changeActiveIndexUseAnim: {
type: Boolean,
default: true
},
name: {
type: String,
default: () => 'SlideVertical'
}
})
const emit = defineEmits(['update:index'])
const wrapperEl = ref(null)
//slide-listref
const slideListEl = ref(null)
const state = reactive({
judgeValue: 20,
type: SlideType.HORIZONTAL,
name: 'SlideVertical',
localIndex: props.index,
needCheck: true,
next: false,
start: { x: 0, y: 0, time: 0 },
move: { x: 0, y: 0 },
wrapper: { width: 0, height: 0, childrenLength: 0 }
judgeValue: 20, //
type: SlideType.VERTICAL, //
name: props.name,
localIndex: props.index, //
needCheck: true, //uptrue
next: false, //
isDown: false, //move
start: { x: 0, y: 0, time: 0 }, //
move: { x: 0, y: 0 }, //
//slide-list
wrapper: {
width: 0,
height: 0,
//childrenLengthcanNext
childrenLength: 0
}
})
watch(
@ -45,32 +58,32 @@ watch(
if (state.localIndex !== newVal) {
state.localIndex = newVal
if (props.changeActiveIndexUseAnim) {
GM.$setCss(wrapperEl.value, 'transition-duration', `300ms`)
_css(slideListEl.value, 'transition-duration', `300ms`)
}
GM.$setCss(
wrapperEl.value,
_css(
slideListEl.value,
'transform',
`translate3d(0,${getSlideOffset(state, wrapperEl.value)}px, 0)`
`translate3d(0,${getSlideOffset(state, slideListEl.value)}px, 0)`
)
}
}
)
onMounted(() => {
slideInit(wrapperEl.value, state)
slideInit(slideListEl.value, state)
})
function touchStart(e) {
slidePointerDown(e, wrapperEl.value, state)
slideTouchStart(e, slideListEl.value, state)
}
function touchMove(e) {
slidePointerMove(e, wrapperEl.value, state)
slideTouchMove(e, slideListEl.value, state)
}
function touchEnd(e) {
slideTouchEnd(e, state)
slideReset(wrapperEl.value, state, emit)
slideReset(e, slideListEl.value, state, emit)
}
</script>
@ -78,10 +91,10 @@ function touchEnd(e) {
<div class="slide v">
<div
class="slide-list flex-direction-column"
ref="wrapperEl"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
ref="slideListEl"
@pointerdown.prevent="touchStart"
@pointermove.prevent="touchMove"
@pointerup.prevent="touchEnd"
>
<slot></slot>
</div>

View File

@ -1,20 +1,19 @@
<script setup lang="jsx">
<script setup lang="tsx">
import { createApp, onMounted, reactive, ref, render as vueRender, watch } from 'vue'
import GM from '../../utils'
import {
getSlideOffset,
slideInit,
slideReset,
slideTouchEnd,
slidePointerMove,
slidePointerDown
slideTouchMove,
slideTouchStart
} from '@/utils/slide'
import { SlideType } from '@/utils/const_var'
import SlideItem from '@/components/slide/SlideItem.vue'
import bus, { EVENT_KEY } from '../../utils/bus'
import Loading from '@/components/Loading.vue'
import { useBaseStore } from '@/store/pinia'
import $ from 'jquery'
import { _css } from '@/utils/dom'
const props = defineProps({
index: {
@ -35,6 +34,7 @@ const props = defineProps({
return []
}
},
//SlideItem
virtualTotal: {
type: Number,
default: () => 5
@ -60,14 +60,15 @@ const emit = defineEmits(['update:index', 'loadMore', 'refresh'])
const appInsMap = new Map()
const itemClassName = 'slide-item'
const wrapperEl = ref(null)
const slideListEl = ref<HTMLDivElement>(null)
const state = reactive({
judgeValue: 20,
type: SlideType.VERTICAL,
type: SlideType.VERTICAL_INFINITE,
name: props.name,
localIndex: props.index,
needCheck: true,
next: false,
isDown: false,
start: { x: 0, y: 0, time: 0 },
move: { x: 0, y: 0 },
wrapper: { width: 0, height: 0, childrenLength: 0 }
@ -78,27 +79,32 @@ watch(
() => props.list,
(newVal, oldVal) => {
// console.log('watch-list', newVal.length, oldVal.length, newVal)
//
if (newVal.length < oldVal.length) {
//
if (newVal.length <= oldVal.length) {
insertContent()
} else {
//
if (oldVal.length === 0) {
insertContent()
} else {
let lastSlideItem = $(wrapperEl.value).find(`.${itemClassName}:last`)
let top = lastSlideItem.css('top')
let lastIndex = Number(lastSlideItem.attr('data-index')) + 1
console.log('lastIndex', lastIndex)
newVal.slice(lastIndex, lastIndex + 3).map((item, index) => {
let el = getInsEl(item, lastIndex + index)
//top
//2022-3-27slide-itemtop
//top
// top
$(el).css('top', top)
wrapperEl.value.appendChild(el)
state.wrapper.childrenLength++
})
//
// 使
// 3html538dom
// domvirtualTotal/2+1
// let lastSlideItem = slideListEl.value.querySelector(`.${itemClassName}:last-child`)
// let top = _css(lastSlideItem, 'top')
// let newListStartIndex = Number(lastSlideItem.getAttribute('data-index')) + 1
// // console.log('newListStartIndex', newListStartIndex)
// newVal.slice(newListStartIndex, newListStartIndex + 3).map((item, index) => {
// let el = getInsEl(item, newListStartIndex + index)
// //top
// //2022-3-27slide-itemtop
// //top
// // top
// _css(el, 'top', top)
// slideListEl.value.appendChild(el)
// state.wrapper.childrenLength++
// })
}
}
}
@ -106,10 +112,45 @@ watch(
watch(
() => props.index,
(newVal, oldVal) => {
(newVal) => {
state.localIndex = newVal
// console.log('watch-index', newVal, oldVal)
if (!props.list.length) return
if (!props?.list?.length) return
//
if (
slideListEl.value &&
slideListEl.value?.innerHTML &&
state.localIndex < props?.list?.length
) {
let startIndex = slideListEl.value
.querySelector(`.${itemClassName}:first-child`)
.getAttribute('data-index')
let endIndex = slideListEl.value
.querySelector(`.${itemClassName}:last-child`)
.getAttribute('data-index')
if (
state.localIndex >= (startIndex as any) * 1 &&
state.localIndex <= (endIndex as any) * 1
) {
//
touchEnd({})
} else {
//
insertContent()
}
}
}
)
/**
* 滑动
*/
watch(
() => state.localIndex,
(newVal, oldVal) => {
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[newVal])
bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
uniqueId: props.uniqueId,
@ -125,78 +166,98 @@ watch(
}, 200)
}
)
watch(
() => props.active,
(newVal) => {
//list
if (newVal && !props.list.length) {
return emit('refresh')
}
let t = newVal ? 0 : 200
// console.log('active', 'newVal', newVal, 'oldVal', oldVal)
if (newVal) {
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[state.localIndex])
}
bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
uniqueId: props.uniqueId,
index: state.localIndex,
type: newVal === false ? EVENT_KEY.ITEM_STOP : EVENT_KEY.ITEM_PLAY
})
setTimeout(() => {
bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
uniqueId: props.uniqueId,
index: state.localIndex,
type: newVal === false ? EVENT_KEY.ITEM_STOP : EVENT_KEY.ITEM_PLAY
})
}, t)
},
{ immediate: true }
)
onMounted(() => {
slideInit(wrapperEl.value, state, SlideType.VERTICAL)
insertContent()
slideInit(slideListEl.value, state)
})
function insertContent(list = props.list) {
if (!list.length) return
$(wrapperEl.value).empty()
let half = (props.virtualTotal - 1) / 2
/**
* 插入SlideItem
*/
function insertContent() {
if (!props.list.length) return
//SlideList
slideListEl.value.innerHTML = ''
let half = parseInt((props.virtualTotal / 2).toString()) //
// props.virtualTotal domindex0
let start = 0
if (state.localIndex >= half) {
if (state.localIndex > half) {
start = state.localIndex - half
}
let end = start + props.virtualTotal
if (end >= list.length) {
end = list.length
if (end >= props.list.length) {
end = props.list.length
start = end - props.virtualTotal
}
if (start < 0) start = 0
// console.log('start', start, end)
list.slice(start, end).map((item, index) => {
//0jqtrigger play
let el = getInsEl(item, start + index, start + index === state.localIndex)
wrapperEl.value.appendChild(el)
})
GM.$setCss(wrapperEl.value, 'transform', `translate3d(0px,${getSlideOffset(state)}px, 0px)`)
if (state.localIndex > 2 && list.length > 5) {
$(wrapperEl.value)
.find(`.${itemClassName}`)
.each(function () {
if (list.length - state.localIndex > 2) {
$(this).css('top', (state.localIndex - 2) * state.wrapper.height)
} else {
$(this).css('top', start * state.wrapper.height)
}
})
// console.log('start', start, end)
//startenddom
props.list.slice(start, end).map((item, index) => {
let el = getInsEl(item, start + index, start + index === state.localIndex)
slideListEl.value.appendChild(el)
})
//SlideList
_css(
slideListEl.value,
'transform',
`translate3d(0px,${getSlideOffset(state, slideListEl.value)}px, 0px)`
)
//index0Itemtop
if (state.localIndex > 2 && props.list.length > 5) {
let list = slideListEl.value.querySelectorAll(`.${itemClassName}`)
list.forEach((item) => {
if (list.length - state.localIndex > 2) {
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
} else {
_css(item, 'top', start * state.wrapper.height)
}
})
}
state.wrapper.childrenLength = wrapperEl.value.children.length
state.wrapper.childrenLength = slideListEl.value.children.length
// console.log('list[state.localIndex]',list[state.localIndex])
bus.emit(EVENT_KEY.CURRENT_ITEM, list[state.localIndex])
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[state.localIndex])
}
function dislike(item) {
let currentItem = $(wrapperEl.value).find(`.${itemClassName}[data-index=${state.localIndex}]`)
let replaceItem = getInsEl(item, state.localIndex, true)
$(replaceItem).css('top', currentItem.css('top'))
currentItem.replaceWith(replaceItem)
function dislike() {
// let currentItem = $(slideListEl.value).find(`.${itemClassName}[data-index=${state.localIndex}]`)
// let replaceItem = getInsEl(item, state.localIndex, true)
// $(replaceItem).css('top', currentItem.css('top'))
// currentItem.replaceWith(replaceItem)
}
defineExpose({ dislike })
/**
* 获取Vue组件渲染之后的dom元素
* @param item
* @param index
* @param play
*/
function getInsEl(item, index, play = false) {
// console.log('index', cloneDeep(item), index, play)
let slideVNode = props.render(item, index, play, props.uniqueId)
@ -205,6 +266,7 @@ function getInsEl(item, index, play = false) {
if (import.meta.env.PROD) {
parent.classList.add('slide-item')
parent.setAttribute('data-index', index)
//Vuediv
vueRender(slideVNode, parent)
appInsMap.set(index, {
unmount: () => {
@ -214,6 +276,7 @@ function getInsEl(item, index, play = false) {
})
return parent
} else {
//Vuediv
const app = createApp({
render() {
return <SlideItem data-index={index}>{slideVNode}</SlideItem>
@ -226,12 +289,11 @@ function getInsEl(item, index, play = false) {
}
function touchStart(e) {
slidePointerDown(e, wrapperEl.value, state)
slideTouchStart(e, slideListEl.value, state)
}
//TODO 2022-3-28:
function touchMove(e) {
slidePointerMove(e, wrapperEl.value, state, canNext)
slideTouchMove(e, slideListEl.value, state, canNext)
}
function touchEnd(e) {
@ -244,76 +306,61 @@ function touchEnd(e) {
emit('refresh')
}
slideTouchEnd(e, state, canNext, (isNext) => {
let half = (props.virtualTotal + 1) / 2
let half = parseInt((props.virtualTotal / 2).toString()) //
if (props.list.length > props.virtualTotal) {
//()
if (isNext) {
if (state.localIndex > props.list.length - props.virtualTotal && state.localIndex >= half) {
// `dom` `dom`
if (state.localIndex > props.list.length - props.virtualTotal && state.localIndex > half) {
emit('loadMore')
}
let addItemIndex = state.localIndex + 2
let res = $(wrapperEl.value).find(`.${itemClassName}[data-index=${addItemIndex}]`)
if (state.wrapper.childrenLength < props.virtualTotal) {
if (res.length === 0) {
wrapperEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
// console.log('props.list.length', props.list.length, state.localIndex)
if (state.localIndex > half && state.localIndex < props.list.length - half) {
let addItemIndex = state.localIndex + half
let res = slideListEl.value.querySelector(
`.${itemClassName}[data-index='${addItemIndex}']`
)
if (!res) {
slideListEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
}
}
if (
state.wrapper.childrenLength === props.virtualTotal &&
state.localIndex >= (props.virtualTotal + 1) / 2 &&
state.localIndex <= props.list.length - 3
) {
if (res.length === 0) {
wrapperEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
appInsMap
.get($(wrapperEl.value).find(`.${itemClassName}:first`).data('index'))
.unmount()
// $(wrapperEl.value).find(".base-slide-item:first").remove()
$(wrapperEl.value)
.find(`.${itemClassName}`)
.each(function () {
$(this).css('top', (state.localIndex - 2) * state.wrapper.height)
})
}
}
if (state.wrapper.childrenLength > props.virtualTotal) {
$(wrapperEl.value)
.find(`.${itemClassName}`)
.each(function () {
let index = $(this).data('index')
if (index < state.localIndex - 2) {
appInsMap.get(index).unmount()
}
$(this).css('top', (state.localIndex - 2) * state.wrapper.height)
})
let index = slideListEl.value
.querySelector(`.${itemClassName}:first-child`)
.getAttribute('data-index')
appInsMap.get(Number(index)).unmount()
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
_css(item, 'top', (state.localIndex - half) * state.wrapper.height)
})
}
} else {
let addItemIndex = state.localIndex - 2
let res = $(wrapperEl.value).find(`.${itemClassName}[data-index=${addItemIndex}]`)
if (state.localIndex > 1 && state.localIndex <= props.list.length - 4) {
if (res.length === 0) {
wrapperEl.value.prepend(getInsEl(props.list[addItemIndex], addItemIndex))
appInsMap.get($(wrapperEl.value).find(`.${itemClassName}:last`).data('index')).unmount()
// $(wrapperEl.value).find(".base-slide-item:last").remove()
$(wrapperEl.value)
.find(`.${itemClassName}`)
.each(function () {
$(this).css('top', (state.localIndex - 2) * state.wrapper.height)
})
// `dom` `dom`
if (state.localIndex >= half && state.localIndex < props.list.length - (half + 1)) {
let addIndex = state.localIndex - half
if (addIndex >= 0) {
let res = slideListEl.value.querySelector(`.${itemClassName}[data-index='${addIndex}']`)
if (!res) {
slideListEl.value.prepend(getInsEl(props.list[addIndex], addIndex))
}
}
}
let index = slideListEl.value
.querySelector(`.${itemClassName}:last-child`)
.getAttribute('data-index')
appInsMap.get(Number(index)).unmount()
if (state.wrapper.childrenLength > props.virtualTotal) {
appInsMap.get($(wrapperEl.value).find(`.${itemClassName}:last`).data('index')).unmount()
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
_css(item, 'top', (state.localIndex - half) * state.wrapper.height)
})
}
}
state.wrapper.childrenLength = wrapperEl.value.children.length
state.wrapper.childrenLength = slideListEl.value.children.length
}
})
slideReset(wrapperEl.value, state, emit)
slideReset(e, slideListEl.value, state, emit)
}
function canNext(state, isNext) {
function canNext(state, isNext: boolean) {
return !(
(state.localIndex === 0 && !isNext) ||
(state.localIndex === props.list.length - 1 && isNext)
@ -326,11 +373,10 @@ function canNext(state, isNext) {
<Loading v-if="props.loading && props.list.length === 0" />
<div
class="slide-list flex-direction-column"
ref="wrapperEl"
@click="null"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
ref="slideListEl"
@pointerdown.prevent="touchStart"
@pointermove.prevent="touchMove"
@pointerup.prevent="touchEnd"
>
<slot></slot>
</div>

View File

@ -0,0 +1,365 @@
<script setup lang="tsx">
import { createApp, onMounted, reactive, ref, render as vueRender, watch } from 'vue'
import {
getSlideOffset,
slideInit,
slideReset,
slideTouchEnd,
slideTouchMove,
slideTouchStart
} from '@/utils/slide'
import { SlideType } from '@/utils/const_var'
import SlideItem from '@/components/slide/SlideItem.vue'
import bus, { EVENT_KEY } from '../../utils/bus'
import Loading from '@/components/Loading.vue'
import { useBaseStore } from '@/store/pinia'
import { _css } from '@/utils/dom'
const props = defineProps({
index: {
type: Number,
default: () => {
return -1
}
},
render: {
type: Function,
default: () => {
return null
}
},
list: {
type: Array,
default: () => {
return []
}
},
//页面中同时存在多少个SlideItem
virtualTotal: {
type: Number,
default: () => 5
},
name: {
type: String,
default: () => ''
},
uniqueId: {
type: String,
default: () => ''
},
loading: {
type: Boolean,
default: () => false
},
active: {
type: Boolean,
default: () => false
}
})
const emit = defineEmits(['update:index', 'loadMore', 'refresh'])
const appInsMap = new Map()
const itemClassName = 'slide-item'
const slideListEl = ref<HTMLDivElement>(null)
const state = reactive({
judgeValue: 20,
type: SlideType.VERTICAL_INFINITE,
name: props.name,
localIndex: props.index,
needCheck: true,
next: false,
isDown: false,
start: { x: 0, y: 0, time: 0 },
move: { x: 0, y: 0 },
wrapper: { width: 0, height: 0, childrenLength: 0 }
})
const baseStore = useBaseStore()
watch(
() => props.list,
(newVal, oldVal) => {
// console.log('watch-list', newVal.length, oldVal.length, newVal)
//新数据长度比老数据长度小,说明是刷新
if (newVal.length < oldVal.length) {
insertContent()
} else {
//没数据就直接插入
if (oldVal.length === 0) {
insertContent()
} else {
// 走到这里,说明是通过接口加载了下一页的数据,
// 为了在用户快速滑动时,无需频繁等待请求接口加载数据,给用户更好的使用体验
// 这里额外加载3条数据。所以此刻html里面有原本的5个加新增的3个一共8个dom
// 用户往下滑动时只删除前面多余的dom等滑动到临界值virtualTotal/2+1再去执行新增逻辑
// let lastSlideItem = slideListEl.value.querySelector(`.${itemClassName}:last-child`)
// let top = _css(lastSlideItem, 'top')
// let newListStartIndex = Number(lastSlideItem.getAttribute('data-index')) + 1
// // console.log('newListStartIndex', newListStartIndex)
// newVal.slice(newListStartIndex, newListStartIndex + 3).map((item, index) => {
// let el = getInsEl(item, newListStartIndex + index)
// //这里必须要设置个top值不然会把前面的条目给覆盖掉
// //2022-3-27这里不用计算直接用已用slide-item最后一条的top值
// //因为有一条情况当滑动最后一条和二条的时候top值不会继续加。此时新增的数据如果还
// // 计算top值的会和前面的对不上
// _css(el, 'top', top)
// slideListEl.value.appendChild(el)
// state.wrapper.childrenLength++
// })
}
}
}
)
watch(
() => props.index,
(newVal, oldVal) => {
state.localIndex = newVal
// console.log('watch-index', newVal, oldVal)
if (!props.list.length) return
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[newVal])
bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
uniqueId: props.uniqueId,
index: newVal,
type: EVENT_KEY.ITEM_PLAY
})
setTimeout(() => {
bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
uniqueId: props.uniqueId,
index: oldVal,
type: EVENT_KEY.ITEM_STOP
})
}, 200)
}
)
watch(
() => props.active,
(newVal) => {
//当激活此页时如果list为空那么向上发射事件通知父组件请求数据
if (newVal && !props.list.length) {
return emit('refresh')
}
// console.log('active', 'newVal', newVal, 'oldVal', oldVal)
if (newVal) {
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[state.localIndex])
}
bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
uniqueId: props.uniqueId,
index: state.localIndex,
type: newVal === false ? EVENT_KEY.ITEM_STOP : EVENT_KEY.ITEM_PLAY
})
},
{ immediate: true }
)
onMounted(() => {
slideInit(slideListEl.value, state)
})
/**
* 插入SlideItem
*/
function insertContent() {
if (!props.list.length) return
//清空SlideList
slideListEl.value.innerHTML = ''
let half = (props.virtualTotal - 1) / 2
//因为我们只渲染 props.virtualTotal 条数据到dom中并且当前index有可能不是0所以需要计算出起始下标和结束下标
let start = 0
if (state.localIndex > half) {
start = state.localIndex - half
}
let end = start + props.virtualTotal
if (end >= props.list.length) {
end = props.list.length
start = end - props.virtualTotal
}
if (start < 0) start = 0
// console.log('start', start, end)
//插入start到end范围内的数据到dom中
props.list.slice(start, end).map((item, index) => {
let el = getInsEl(item, start + index, start + index === state.localIndex)
slideListEl.value.appendChild(el)
})
//设置SlideList的偏移量
_css(
slideListEl.value,
'transform',
`translate3d(0px,${getSlideOffset(state, slideListEl.value)}px, 0px)`
)
//因为index有可能不是0所以要设置Item的top偏移量
if (state.localIndex > 2 && props.list.length > 5) {
let list = slideListEl.value.querySelectorAll(`.${itemClassName}`)
list.forEach((item) => {
if (list.length - state.localIndex > 2) {
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
} else {
_css(item, 'top', start * state.wrapper.height)
}
})
}
state.wrapper.childrenLength = slideListEl.value.children.length
// console.log('list[state.localIndex]',list[state.localIndex])
bus.emit(EVENT_KEY.CURRENT_ITEM, props.list[state.localIndex])
}
function dislike() {
// let currentItem = $(slideListEl.value).find(`.${itemClassName}[data-index=${state.localIndex}]`)
// let replaceItem = getInsEl(item, state.localIndex, true)
// $(replaceItem).css('top', currentItem.css('top'))
// currentItem.replaceWith(replaceItem)
}
defineExpose({ dislike })
/**
* 获取Vue组件渲染之后的dom元素
* @param item
* @param index
* @param play
*/
function getInsEl(item, index, play = false) {
// console.log('index', cloneDeep(item), index, play)
let slideVNode = props.render(item, index, play, props.uniqueId)
const parent = document.createElement('div')
//TODO 打包到线上时用这个,这个在开发时任何修改都会刷新页面
if (import.meta.env.PROD) {
parent.classList.add('slide-item')
parent.setAttribute('data-index', index)
//将Vue组件渲染到一个div上
vueRender(slideVNode, parent)
appInsMap.set(index, {
unmount: () => {
vueRender(null, parent)
parent.remove()
}
})
return parent
} else {
//创建一个新的Vue实例并挂载到一个div上
const app = createApp({
render() {
return <SlideItem data-index={index}>{slideVNode}</SlideItem>
}
})
const ins = app.mount(parent)
appInsMap.set(index, app)
return ins.$el
}
}
function touchStart(e) {
slideTouchStart(e, slideListEl.value, state)
}
function touchMove(e) {
slideTouchMove(e, slideListEl.value, state, canNext)
}
function touchEnd(e) {
let isNext = state.move.y < 0
if (
state.localIndex === 0 &&
!isNext &&
state.move.y > baseStore.homeRefresh + baseStore.judgeValue
) {
emit('refresh')
}
slideTouchEnd(e, state, canNext, (isNext) => {
let half = (props.virtualTotal - 1) / 2
if (props.list.length > props.virtualTotal) {
//往下滑
if (isNext) {
if (state.localIndex > props.list.length - props.virtualTotal && state.localIndex > half) {
emit('loadMore')
}
let addItemIndex = state.localIndex + 2
console.log('addItemIndex', addItemIndex)
let res = slideListEl.value.querySelector(`.${itemClassName}[data-index='${addItemIndex}']`)
if (state.wrapper.childrenLength < props.virtualTotal) {
if (!res) {
slideListEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
}
}
if (
state.wrapper.childrenLength === props.virtualTotal &&
state.localIndex > half &&
state.localIndex <= props.list.length - 3
) {
if (!res) {
slideListEl.value.appendChild(getInsEl(props.list[addItemIndex], addItemIndex))
let index = slideListEl.value
.querySelector(`.${itemClassName}:first-child`)
.getAttribute('data-index')
appInsMap.get(Number(index)).unmount()
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
})
}
}
if (state.wrapper.childrenLength > props.virtualTotal) {
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
let index = Number(item.getAttribute('data-index'))
if (index < state.localIndex - 2) {
appInsMap.get(index).unmount()
}
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
})
}
} else {
let addItemIndex = state.localIndex - 2
let res = slideListEl.value.querySelector(`.${itemClassName}[data-index='${addItemIndex}']`)
if (state.localIndex > 1 && state.localIndex <= props.list.length - 4) {
if (!res) {
slideListEl.value.prepend(getInsEl(props.list[addItemIndex], addItemIndex))
let index = slideListEl.value
.querySelector(`.${itemClassName}:last-child`)
.getAttribute('data-index')
appInsMap.get(Number(index)).unmount()
// $(slideListEl.value).find(".base-slide-item:last").remove()
slideListEl.value.querySelectorAll(`.${itemClassName}`).forEach((item) => {
_css(item, 'top', (state.localIndex - 2) * state.wrapper.height)
})
}
}
if (state.wrapper.childrenLength > props.virtualTotal) {
let index = slideListEl.value
.querySelector(`.${itemClassName}:last-child`)
.getAttribute('data-index')
appInsMap.get(Number(index)).unmount()
}
}
state.wrapper.childrenLength = slideListEl.value.children.length
}
})
slideReset(e, slideListEl.value, state, emit)
}
function canNext(state, isNext) {
return !(
(state.localIndex === 0 && !isNext) ||
(state.localIndex === props.list.length - 1 && isNext)
)
}
</script>
<template>
<div class="slide slide-infinite">
<Loading v-if="props.loading && props.list.length === 0" />
<div
class="slide-list flex-direction-column"
ref="slideListEl"
@click="null"
@pointerdown="touchStart"
@pointermove="touchMove"
@pointerup="touchEnd"
>
<slot></slot>
</div>
</div>
</template>

View File

@ -7,12 +7,14 @@ const BASE_URL_MAP = {
DEV: '',
PROD: '',
// GP_PAGES: '/dist',
GP_PAGES: '/douyin',
GITEE_PAGES: '/gitee-dy',
GP_PAGES: '',
GITEE_PAGES: '/douyin',
UNI: 'https://dy.ttentau.top'
}
export const IS_SUB_DOMAIN = ['GITEE_PAGES', 'GP_PAGES'].includes(import.meta.env.VITE_ENV)
export const IS_GITEE_PAGES = ['GITEE_PAGES'].includes(import.meta.env.VITE_ENV)
export const BASE_URL = BASE_URL_MAP[import.meta.env.VITE_ENV]
export const IMG_URL = BASE_URL + '/images/'
export const FILE_URL = BASE_URL + '/data/'
export const IS_DEV = process.env.NODE_ENV !== 'production'

View File

@ -1,18 +1,42 @@
import { createApp } from 'vue'
import App from './App.vue'
import mitt from 'mitt'
import './assets/less/index.less'
import { startMock } from '@/mock'
import router from './router'
import mixin from './utils/mixin'
import VueLazyload from '@jambonn/vue-lazyload'
import { createPinia } from 'pinia'
import { useClick } from '@/utils/hooks/useClick'
import bus, { EVENT_KEY } from '@/utils/bus'
window.isMoved = false
window.isMuted = true
window.showMutedNotice = true
HTMLElement.prototype.addEventListener = new Proxy(HTMLElement.prototype.addEventListener, {
apply(target, ctx, args) {
const eventName = args[0]
const listener = args[1]
if (listener instanceof Function && eventName === 'click') {
args[1] = new Proxy(listener, {
apply(target1, ctx1, args1) {
// console.log('e', args1)
// console.log('click点击', window.isMoved)
if (window.isMoved) return
try {
return target1.apply(ctx1, args1)
} catch (e) {
console.error(`[proxyPlayerEvent][${eventName}]`, listener, e)
}
}
})
}
return target.apply(ctx, args)
}
})
const vClick = useClick()
const pinia = createPinia()
const emitter = mitt()
const app = createApp(App)
app.config.globalProperties.emitter = emitter
app.provide('mitt', emitter)
app.mixin(mixin)
const loadImage = new URL('./assets/img/icon/img-loading.png', import.meta.url).href
app.use(VueLazyload, {
@ -23,6 +47,14 @@ app.use(VueLazyload, {
app.use(pinia)
app.use(router)
app.mount('#app')
app.directive('click', vClick)
//放到最后才可以使用pinia
startMock()
setTimeout(() => {
bus.emit(EVENT_KEY.HIDE_MUTED_NOTICE)
window.showMutedNotice = false
}, 2000)
bus.on(EVENT_KEY.REMOVE_MUTED, () => {
window.isMuted = false
})

View File

@ -3,10 +3,10 @@ import posts6 from '@/assets/data/posts6.json'
import { _fetch, cloneDeep, random } from '@/utils'
import { BASE_URL, FILE_URL } from '@/config'
import { useBaseStore } from '@/store/pinia'
import axiosInstance from '@/utils/request'
import { axiosInstance } from '@/utils/request'
import MockAdapter from 'axios-mock-adapter'
const mock = new MockAdapter(axiosInstance, { delayResponse: 300 })
const mock = new MockAdapter(axiosInstance)
function getPage2(params: any): { limit: number; offset: number; pageNo: number } {
const offset = params.pageNo * params.pageSize
@ -28,23 +28,71 @@ const t = [
type: 'imgs',
src: `https://imgapi.cn/bing.php`,
author: {
unique_id: 1
}
},
{
type: 'user',
src: `https://imgapi.cn/bing.php`,
author: {
unique_id: 2
}
},
{
type: 'img',
src: `https://imgapi.cn/bing.php`,
author: {
unique_id: 3
unique_id: 1,
avatar_168x168: {
url_list: []
},
avatar_300x300: {
url_list: []
},
cover_url: [
{
url_list: []
}
],
white_cover_url: [
{
url_list: []
}
]
}
}
// {
// type: 'user',
// src: `https://imgapi.cn/bing.php`,
// author: {
// unique_id: 2,
// avatar_168x168: {
// url_list: []
// },
// avatar_300x300: {
// url_list: []
// },
// cover_url: [
// {
// url_list: []
// }
// ],
// white_cover_url: [
// {
// url_list: []
// }
// ]
// }
// },
// {
// type: 'img',
// src: `https://imgapi.cn/bing.php`,
// author: {
// unique_id: 3,
// avatar_168x168: {
// url_list: []
// },
// avatar_300x300: {
// url_list: []
// },
// cover_url: [
// {
// url_list: []
// }
// ],
// white_cover_url: [
// {
// url_list: []
// }
// ]
// }
// }
]
// allRecommendVideos.unshift(...t)
@ -86,14 +134,28 @@ async function fetchData() {
//TODO 有个bug一开始只返回了6条数据但第二次前端传过来的pageNo是2了就是会从第10条数据开始返回导致中间漏了4条
export async function startMock() {
mock.onGet(/video\/recommended/).reply(async (config) => {
const page = getPage2(config.params)
console.log('allRecommendVideos', cloneDeep(allRecommendVideos.length), page)
const { start, pageSize } = config.params
// console.log('allRecommendVideos', cloneDeep(allRecommendVideos.length), config.params)
return [
200,
{
data: {
total: 844,
list: allRecommendVideos.slice(page.offset, page.limit) // list: allRecommendVideos.slice(0, 6),
list: allRecommendVideos.slice(start, start + pageSize) // list: allRecommendVideos.slice(0, 6),
},
code: 200,
msg: ''
}
]
})
mock.onGet(/video\/long\/recommended/).reply(async (config) => {
const page = getPage2(config.params)
return [
200,
{
data: {
total: 844,
list: allRecommendVideos.slice(page.offset, page.limit)
},
code: 200,
msg: ''

View File

@ -1,17 +0,0 @@
export default {
data() {
return {
mainScrollTop: 0
}
},
activated() {
if (this.$refs.mainScroll && this.$refs.mainScroll.wrapper) {
this.$refs.mainScroll.wrapper.scrollTop = this.mainScrollTop
}
},
deactivated() {
if (this.$refs.mainScroll && this.$refs.mainScroll.wrapper) {
this.mainScrollTop = this.$refs.mainScroll.wrapper.scrollTop
}
}
}

View File

@ -49,7 +49,7 @@
<img src="../../assets/img/icon/avatar/2.png" alt="" class="round" />
<img src="../../assets/img/icon/avatar/3.png" alt="" class="round" />
<div class="round count">107</div>
<dy-back class="round close" img="close" mode="light" @click="$back" />
<dy-back class="round close" img="close" mode="light" @click="$router.back()" />
</div>
<div class="more">
<div class="wrapper">
@ -117,7 +117,7 @@ import Dom from '../../utils/dom'
import { nextTick } from 'vue'
import { mapState } from 'pinia'
import { useBaseStore } from '@/store/pinia'
import { _checkImgUrl, random } from '@/utils'
import { _checkImgUrl, _sleep, random } from '@/utils'
import Mock from 'mockjs'
export default {
@ -205,7 +205,7 @@ export default {
this.page = this.$refs.page
this.timer1 = setInterval(async () => {
this.sendGift()
await this.$sleep(300)
await _sleep(300)
this.sendGift()
this.joinUser()
}, 1000)
@ -376,7 +376,7 @@ export default {
.barrage {
position: fixed;
top: 50%;
transform: translateX(100vw);
transform: translateX(100%);
display: flex;
align-items: center;
font-size: 12rem;
@ -384,7 +384,7 @@ export default {
@keyframes anim {
from {
transform: translateX(100vw);
transform: translateX(100%);
}
to {
transform: translateX(-100%);
@ -467,14 +467,14 @@ export default {
@import '../../assets/less/index';
.LivePage {
width: 100vw;
width: 100%;
height: calc(var(--vh, 1vh) * 100);
color: white;
font-size: 14rem;
position: relative;
.live-wrapper {
width: 100vw;
width: 100%;
height: calc(var(--vh, 1vh) * 100);
background: black;
display: flex;
@ -487,7 +487,7 @@ export default {
}
img {
width: 100vw;
width: 100%;
height: calc(var(--vh, 1vh) * 100);
color: rgb(229, 229, 229);
}
@ -496,7 +496,7 @@ export default {
.float {
position: absolute;
top: 0;
width: 100vw;
width: 100%;
height: calc(var(--vh, 1vh) * 100);
@tag-bg: rgba(58, 58, 70, 0.3);
@ -634,7 +634,7 @@ export default {
.bottom {
position: absolute;
bottom: 0;
width: 100vw;
width: 100%;
box-sizing: border-box;
padding: var(--page-padding);
padding-bottom: 10rem;

View File

@ -75,11 +75,11 @@
</Scroll>
</div>
<div class="options">
<div class="l-button white" @click="$no">
<div class="l-button white" @click="_no">
<img src="../../assets/img/icon/home/music3.png" alt="" />
<span>分享到日常</span>
</div>
<div class="l-button primary" @click="$no">
<div class="l-button primary" @click="_no">
<img src="../../assets/img/icon/home/record.png" alt="" />
<span>拍同款</span>
</div>
@ -130,7 +130,7 @@ import { myVideo } from '@/api/videos'
import { onDeactivated, onMounted, onUnmounted, reactive, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useNav } from '@/utils/hooks/useNav'
import { $no, $notice, _checkImgUrl, _formatNumber } from '@/utils'
import { _checkImgUrl, _formatNumber, _no, _notice } from '@/utils'
const route = useRoute()
const router = useRouter()
@ -203,7 +203,7 @@ async function loadData(init = false) {
if (data.loading) return
if (!init) {
if (data.total <= data.videos.length) {
return $notice('暂时没有更多了')
_notice('暂时没有更多了')
}
data.pageNo++
}
@ -376,7 +376,7 @@ function stopPlay() {
.options {
font-size: 14rem;
width: 100vw;
width: 100%;
position: fixed;
bottom: 20rem;
display: flex;

View File

@ -305,7 +305,7 @@
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { computed, onDeactivated, onMounted, onUnmounted, reactive } from 'vue'
import { $notice, _checkImgUrl, _dateFormat, _duration, _formatNumber } from '@/utils/index.jsx'
import { _checkImgUrl, _dateFormat, _duration, _formatNumber, _notice } from '@/utils/index.jsx'
import { useNav } from '@/utils/hooks/useNav'
defineOptions({
@ -523,9 +523,9 @@ function scroll(e) {
function toggleCollect(item) {
item.is_collect = !item.is_collect
if (item.is_collect) {
$notice('收藏成功')
_notice('收藏成功')
} else {
$notice('取消收藏')
_notice('取消收藏')
}
}

View File

@ -57,7 +57,7 @@
品牌榜
</div>
</div>
<!-- TODO 滚动到下面的时候应该禁止slide-move因为个slideitem的高度不一样高的切到矮的会闪屏-->
<!-- TODO 滚动到下面的时候应该禁止slide-move因为个slideitem的高度不一样高的切到矮的会闪屏-->
<SlideHorizontal v-model:index="data.slideIndex" :style="slideListHeight">
<SlideItem>
<div class="slide0" ref="slide0">
@ -179,7 +179,7 @@
<div class="brands">
<div
class="brand"
@click="toggleKey(key)"
@click="toggleKey(key, i)"
:key="i"
:class="{ active: key === data.selectBrandKey }"
v-for="(key, i) in Object.keys(data.brandRankList)"
@ -198,7 +198,6 @@
<div class="avatar-wrapper" :class="item.living ? 'living' : ''">
<div class="avatar-out-line"></div>
<img v-lazy="_checkImgUrl(item.logo)" alt="" class="avatar" />
<!-- <img :src="item.logo" class="avatar">-->
</div>
<div class="desc">{{ item.name }}</div>
</div>
@ -211,7 +210,7 @@
<div class="more" @click="_no">查看完整品牌榜 ></div>
</div>
<SlideRowList :autoplay="true" indicatorType="bullets">
<SlideHorizontal v-model:index="data.adIndex" :autoplay="true" indicator>
<SlideItem>
<div class="ad">AD1</div>
</SlideItem>
@ -236,7 +235,7 @@
<SlideItem>
<div class="ad">AD8</div>
</SlideItem>
</SlideRowList>
</SlideHorizontal>
</div>
</SlideItem>
</SlideHorizontal>
@ -247,11 +246,8 @@
<script setup lang="ts">
import Search from '../../components/Search.vue'
import Dom from '../../utils/dom'
import { computed, nextTick, watch } from 'vue'
import { computed, nextTick, onMounted, reactive, watch } from 'vue'
import { _checkImgUrl, _formatNumber, _no, _showSimpleConfirmDialog, sampleSize } from '@/utils'
import { useBaseStore } from '@/store/pinia'
import { onMounted, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { useNav } from '@/utils/hooks/useNav'
@ -261,9 +257,9 @@ defineOptions({
const router = useRouter()
const nav = useNav()
const store = useBaseStore()
const data = reactive({
isExpand: false,
adIndex: 0,
history: [
'历史记录1',
'历史记录2',
@ -696,7 +692,7 @@ watch(
} else {
data.selectBrandKeyIndex++
}
data.selectBrandKey = brandListKeys[data.selectBrandKeyIndex]
data.selectBrandKey = brandListKeys.value[data.selectBrandKeyIndex]
}, 3000)
} else {
clearInterval(data.timer)
@ -710,8 +706,9 @@ onMounted(() => {
refresh()
})
function toggleKey(key) {
function toggleKey(key: string, i: number) {
data.selectBrandKey = key
data.selectBrandKeyIndex = i
clearInterval(data.timer)
}
@ -779,7 +776,7 @@ function toggle() {
align-items: center;
border-bottom: 1px solid var(--line-color);
position: fixed;
width: 100vw;
width: 100%;
box-sizing: border-box;
top: 0;
@ -913,9 +910,10 @@ function toggle() {
display: flex;
align-items: center;
justify-content: space-between;
min-width: 0;
.center {
width: calc(100vw - 140rem);
width: calc(100% - 140rem);
box-sizing: border-box;
//padding: 0 1rem;
//flex: 1;
@ -983,7 +981,7 @@ function toggle() {
justify-content: space-between;
.center {
width: calc(100vw - 160rem);
width: calc(100% - 160rem);
box-sizing: border-box;
//padding: 0 1rem;
//flex: 1;
@ -1025,6 +1023,7 @@ function toggle() {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 10rem;
color: var(--second-text-color);
margin-left: 5rem;
@ -1095,7 +1094,7 @@ function toggle() {
justify-content: space-between;
.center {
width: calc(100vw - 150rem);
width: calc(100% - 150rem);
box-sizing: border-box;
//padding: 0 1rem;
//flex: 1;
@ -1200,7 +1199,7 @@ function toggle() {
justify-content: space-between;
.center {
width: calc(100vw - 150rem);
width: calc(100% - 150rem);
box-sizing: border-box;
//padding: 0 1rem;
//flex: 1;

View File

@ -22,11 +22,11 @@
<img src="../../../assets/img/icon/components/follow/share.png" alt="" />
<span>分享主页</span>
</div>
<div class="option" @click="cancel((e) => $nav('/message/chat'))">
<div class="option" @click="cancel((e) => $router.push('/message/chat'))">
<img src="../../../assets/img/icon/components/follow/private-chat.png" alt="" />
<span>发私信</span>
</div>
<div class="option" @click="cancel((e) => $nav('/home/report', { mode: 'chat' }))">
<div class="option" @click="cancel((e) => $router.push('/home/report', { mode: 'chat' }))">
<img src="../../../assets/img/icon/components/follow/report.png" alt="" />
<span>举报</span>
</div>

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