├── public └── favicon.ico ├── src ├── assets │ ├── logo.png │ └── docs │ │ └── log.json ├── main.scss ├── views │ ├── fatherContainer.vue │ ├── webData.vue │ ├── faq.vue │ ├── index.vue │ ├── about.vue │ ├── admin │ │ └── hideVideo.vue │ ├── log.vue │ ├── user │ │ ├── rank.vue │ │ ├── signin.vue │ │ ├── setting.vue │ │ └── signup.vue │ ├── space │ │ └── index.vue │ ├── video │ │ └── detail.vue │ └── member │ │ └── upload.vue ├── vite-env.d.ts ├── store │ ├── urlStore.ts │ └── userStore.ts ├── components │ ├── rzm │ │ ├── mmvCard.vue │ │ └── mmCard.vue │ ├── user │ │ └── avatar.vue │ ├── dataCount.vue │ ├── video │ │ ├── VideoList.vue │ │ ├── VideoGrid.vue │ │ └── player.vue │ ├── common │ │ └── comment.vue │ └── navBar.vue ├── composables │ └── useFormat.ts ├── main.ts ├── router │ └── index.ts └── App.vue ├── .prettierrc ├── tailwind.config.js ├── tsconfig.node.json ├── auto-imports.d.ts ├── .gitignore ├── README.md ├── vite.config.ts ├── tsconfig.json ├── .eslintrc.cjs ├── package.json ├── components.d.ts └── index.html /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rzmaoo/maomao-frontend/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rzmaoo/maomao-frontend/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/main.scss: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss/base'; 2 | @import 'tailwindcss/components'; 3 | @import 'tailwindcss/utilities'; 4 | -------------------------------------------------------------------------------- /src/views/fatherContainer.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "es5", 5 | "endOfLine": "crlf" 6 | } 7 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | } 9 | -------------------------------------------------------------------------------- /src/views/webData.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-types */ 2 | /* eslint-disable @typescript-eslint/no-explicit-any */ 3 | /// 4 | 5 | declare module '*.vue' { 6 | import { DefineComponent } from 'vue' 7 | const component: DefineComponent<{}, {}, any> 8 | export default component 9 | } 10 | -------------------------------------------------------------------------------- /auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // noinspection JSUnusedGlobalSymbols 5 | // Generated by unplugin-auto-import 6 | export {} 7 | declare global { 8 | const ElMessage: typeof import('element-plus/es')['ElMessage'] 9 | const ElNotification: typeof import('element-plus/es')['ElNotification'] 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | /yarn.lock 26 | /package-lock.json -------------------------------------------------------------------------------- /src/store/urlStore.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export const useUrlStore = defineStore({ 4 | id: 'urlStore', 5 | 6 | state: () => ({ 7 | // apiUrl: 'http://localhost:5000', 8 | // cosUrl: 'https://cos.bilirz.com', 9 | apiUrl: `${window.location.protocol}//${window.location.host}`, 10 | // apiUrl: 'https://v.bilirz.com', 11 | cosUrl: `${window.location.protocol}//${window.location.host}/api/public/cos`, 12 | faceUrl: `https://cos.bilirz.com`, 13 | }), 14 | }) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 猫猫站 2 | 3 | ### 写在前面 4 | 5 | 本项目是B站UP主[认真猫](https://space.bilibili.com/256195234)使用Vue3开发的视频网站,仅供[认真猫](https://space.bilibili.com/256195234)学习编程使用。 6 | 7 | 网站功能还非常简单,期待大家的丰富。 8 | 9 | 欢迎加入猫猫站用户与技术QQ群:`542174643` 10 | 11 | ### TODO 12 | 13 | 目标 14 | 15 | - [ ] 用户 16 | 17 | - [x] 注册 18 | - [x] 登录 19 | - [ ] 个人中心 20 | 21 | - [ ] 视频 22 | 23 | - [x] m3u8 24 | - [x] 播放量 25 | - [x] 点赞 26 | - [ ] 投币 27 | - [ ] 收藏 28 | - [x] 弹幕 29 | - [x] 评论 30 | - [ ] 转发 31 | 32 | - [ ] 首页推荐 33 | - [ ] 热门 34 | - [ ] 热搜 35 | -------------------------------------------------------------------------------- /src/views/faq.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 28 | -------------------------------------------------------------------------------- /src/components/rzm/mmvCard.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 31 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import AutoImport from 'unplugin-auto-import/vite' 4 | import Components from 'unplugin-vue-components/vite' 5 | import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' 6 | import vuetify from 'vite-plugin-vuetify' 7 | 8 | export default defineConfig({ 9 | plugins: [ 10 | vue(), 11 | vuetify(), 12 | AutoImport({ 13 | resolvers: [ElementPlusResolver()], 14 | }), 15 | Components({ 16 | resolvers: [ElementPlusResolver()], 17 | }), 18 | ], 19 | css: { 20 | preprocessorOptions: { 21 | scss: { 22 | additionalData: `@import "./src/main.scss";`, 23 | }, 24 | }, 25 | }, 26 | resolve: { 27 | alias: { 28 | '@': '/src', 29 | }, 30 | }, 31 | }) 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | "allowJs": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "preserve", 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "noFallthroughCasesInSwitch": true, 23 | 24 | "types": ["element-plus/global"] 25 | }, 26 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 27 | "references": [{ "path": "./tsconfig.node.json" }] 28 | } 29 | -------------------------------------------------------------------------------- /src/store/userStore.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import { defineStore } from 'pinia' 3 | import axios from 'axios' 4 | import { useUrlStore } from './urlStore' 5 | 6 | export const useUserStore = defineStore({ 7 | id: 'userStore', 8 | 9 | state: () => ({ 10 | sessionData: { 11 | signin: null, 12 | status: null, 13 | isload: false, 14 | }, 15 | }), 16 | 17 | actions: { 18 | async fetchSessionData() { 19 | const urlStore = useUrlStore() 20 | 21 | try { 22 | const response = await axios.get( 23 | `${urlStore.apiUrl}/api/user/session/get` 24 | ) 25 | 26 | this.setSessionData(response.data) 27 | } catch (error) { 28 | console.error('获取session数据出错:', error) 29 | } 30 | }, 31 | 32 | setSessionData(data: any) { 33 | this.sessionData = data 34 | }, 35 | }, 36 | }) 37 | -------------------------------------------------------------------------------- /src/views/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 32 | -------------------------------------------------------------------------------- /src/components/rzm/mmCard.vue: -------------------------------------------------------------------------------- 1 | 12 | 22 | 46 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | }, 6 | extends: [ 7 | 'eslint:recommended', 8 | 'plugin:vue/recommended', 9 | 'plugin:@typescript-eslint/recommended', 10 | 'plugin:prettier/recommended', 11 | ], 12 | overrides: [ 13 | { 14 | env: { 15 | node: true, 16 | }, 17 | files: ['.eslintrc.{js,cjs}'], 18 | parserOptions: { 19 | sourceType: 'script', 20 | }, 21 | }, 22 | ], 23 | parser: 'vue-eslint-parser', 24 | parserOptions: { 25 | parser: '@typescript-eslint/parser', // TypeScript 解析器 26 | ecmaVersion: 2020, 27 | sourceType: 'module', 28 | ecmaFeatures: { 29 | jsx: true, 30 | }, 31 | }, 32 | plugins: [ 33 | 'vue', 34 | '@typescript-eslint', // TypeScript 35 | 'prettier', // 启用 Prettier 插件 36 | ], 37 | rules: { 38 | 'vue/multi-word-component-names': 'off', 39 | 'prettier/prettier': 'error', // 确保代码风格一致性 40 | }, 41 | globals: { 42 | ElNotification: 'readonly', 43 | ElMessage: 'readonly', 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /src/composables/useFormat.ts: -------------------------------------------------------------------------------- 1 | type Categories = { 2 | [key: number]: string 3 | } 4 | 5 | // 将时间戳改为 2023-01-01 12:00:00 格式 6 | export default function useFormat() { 7 | const formatTimestamp = (timestamp: number): string => { 8 | const date = new Date(timestamp * 1000) 9 | const year = date.getFullYear() 10 | const month = (date.getMonth() + 1).toString().padStart(2, '0') 11 | const day = date.getDate().toString().padStart(2, '0') 12 | const hours = date.getHours().toString().padStart(2, '0') 13 | const minutes = date.getMinutes().toString().padStart(2, '0') 14 | const seconds = date.getSeconds().toString().padStart(2, '0') 15 | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` 16 | } 17 | 18 | // 将分区ID展示成分区 19 | const getCategoryByValue = (value: number): string => { 20 | const categories: Categories = { 21 | 100: '游戏', 22 | 200: '生活', 23 | 300: '知识', 24 | 400: '科技', 25 | 500: '音乐', 26 | 600: '鬼畜', 27 | 700: '动画', 28 | 800: '时尚', 29 | 900: '舞蹈', 30 | 1000: '娱乐', 31 | 1100: '美食', 32 | 1200: '动物', 33 | } 34 | 35 | return categories[value] || '未知' 36 | } 37 | 38 | return { formatTimestamp, getCategoryByValue } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/user/avatar.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 51 | 52 | 59 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import { createPinia } from 'pinia' 5 | import { useUserStore } from './store/userStore' 6 | import ElementPlus from 'element-plus' 7 | import 'element-plus/dist/index.css' 8 | import * as ElementPlusIconsVue from '@element-plus/icons-vue' 9 | import 'element-plus/theme-chalk/display.css' 10 | import axios from 'axios' 11 | 12 | import 'vuetify/styles' 13 | import '@mdi/font/css/materialdesignicons.css' 14 | import { createVuetify } from 'vuetify' 15 | import * as components from 'vuetify/components' 16 | import * as directives from 'vuetify/directives' 17 | 18 | const app = createApp(App) 19 | const pinia = createPinia() 20 | const vuetify = createVuetify({ 21 | components, 22 | directives, 23 | icons: { 24 | defaultSet: 'mdi', 25 | }, 26 | defaults: { 27 | VBtn: { 28 | color: '#61aefb', 29 | variant: 'outlined', 30 | }, 31 | }, 32 | }) 33 | 34 | app.use(pinia) 35 | app.use(router) 36 | app.use(ElementPlus) 37 | app.use(vuetify) 38 | 39 | // 全局注册element-plus icon 40 | for (const [key, component] of Object.entries(ElementPlusIconsVue)) { 41 | app.component(key, component) 42 | } 43 | 44 | app.mount('#app') 45 | 46 | const userStore = useUserStore() 47 | 48 | userStore.fetchSessionData().then(() => { 49 | // 移除加载器 50 | const loader = document.getElementById('loader') 51 | if (loader) { 52 | loader.style.display = 'none' 53 | } 54 | 55 | // 全局设置 withCredentials 56 | axios.defaults.withCredentials = true 57 | }) 58 | -------------------------------------------------------------------------------- /src/views/about.vue: -------------------------------------------------------------------------------- 1 | 40 | 43 | 44 | 62 | -------------------------------------------------------------------------------- /src/views/admin/hideVideo.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "private": true, 4 | "version": "beta.1.2.1", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@element-plus/icons-vue": "^2.1.0", 13 | "autoprefixer": "^10.4.16", 14 | "axios": "^1.5.1", 15 | "cropperjs": "^1.6.1", 16 | "echarts": "^5.4.3", 17 | "element-plus": "^2.4.1", 18 | "hls.js": "^1.4.12", 19 | "pinia": "^2.1.7", 20 | "postcss": "^8.4.31", 21 | "sass": "^1.69.4", 22 | "socket.io-client": "^4.7.2", 23 | "tailwindcss": "^3.3.4", 24 | "uuid": "^9.0.1", 25 | "video.js": "^8.6.1", 26 | "videojs-hlsjs-plugin": "^1.0.5", 27 | "vite-plugin-vuetify": "^1.0.2", 28 | "vue": "^3.3.4", 29 | "vue-cropperjs": "^5.0.0", 30 | "vue-router": "^4.0.13", 31 | "vuetify": "^3.3.23" 32 | }, 33 | "devDependencies": { 34 | "@mdi/font": "^7.3.67", 35 | "@typescript-eslint/eslint-plugin": "^6.10.0", 36 | "@typescript-eslint/parser": "^6.10.0", 37 | "@vitejs/plugin-vue": "^4.2.3", 38 | "eslint": "^8.53.0", 39 | "eslint-config-prettier": "^9.0.0", 40 | "eslint-config-standard-with-typescript": "^39.1.1", 41 | "eslint-plugin-import": "^2.29.0", 42 | "eslint-plugin-n": "^16.3.1", 43 | "eslint-plugin-prettier": "^5.0.1", 44 | "eslint-plugin-promise": "^6.1.1", 45 | "eslint-plugin-vue": "^9.18.1", 46 | "prettier": "^3.0.3", 47 | "typescript": "^5.2.2", 48 | "unplugin-auto-import": "^0.16.6", 49 | "unplugin-vue-components": "^0.25.2", 50 | "vite": "^4.4.5", 51 | "vue-eslint-parser": "^9.3.2", 52 | "vue-tsc": "^1.8.5" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/dataCount.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 82 | -------------------------------------------------------------------------------- /components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by unplugin-vue-components 5 | // Read more: https://github.com/vuejs/core/pull/3399 6 | export {} 7 | 8 | declare module 'vue' { 9 | export interface GlobalComponents { 10 | Avatar: typeof import('./src/components/user/avatar.vue')['default'] 11 | Comment: typeof import('./src/components/common/comment.vue')['default'] 12 | DataCount: typeof import('./src/components/dataCount.vue')['default'] 13 | ElButton: typeof import('element-plus/es')['ElButton'] 14 | ElCol: typeof import('element-plus/es')['ElCol'] 15 | ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] 16 | ElDialog: typeof import('element-plus/es')['ElDialog'] 17 | ElForm: typeof import('element-plus/es')['ElForm'] 18 | ElFormItem: typeof import('element-plus/es')['ElFormItem'] 19 | ElIcon: typeof import('element-plus/es')['ElIcon'] 20 | ElInput: typeof import('element-plus/es')['ElInput'] 21 | ElLink: typeof import('element-plus/es')['ElLink'] 22 | ElOption: typeof import('element-plus/es')['ElOption'] 23 | ElPopover: typeof import('element-plus/es')['ElPopover'] 24 | ElProgress: typeof import('element-plus/es')['ElProgress'] 25 | ElRadio: typeof import('element-plus/es')['ElRadio'] 26 | ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] 27 | ElResult: typeof import('element-plus/es')['ElResult'] 28 | ElSelect: typeof import('element-plus/es')['ElSelect'] 29 | ElTag: typeof import('element-plus/es')['ElTag'] 30 | ElUpload: typeof import('element-plus/es')['ElUpload'] 31 | MmCard: typeof import('./src/components/rzm/mmCard.vue')['default'] 32 | MmvCard: typeof import('./src/components/rzm/mmvCard.vue')['default'] 33 | NavBar: typeof import('./src/components/navBar.vue')['default'] 34 | Player: typeof import('./src/components/video/player.vue')['default'] 35 | RouterLink: typeof import('vue-router')['RouterLink'] 36 | RouterView: typeof import('vue-router')['RouterView'] 37 | VideoGrid: typeof import('./src/components/video/VideoGrid.vue')['default'] 38 | VideoList: typeof import('./src/components/video/VideoList.vue')['default'] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/views/log.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 78 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 猫猫站 8 | 98 | 99 | 100 |
101 |
102 |
103 |
104 |
正在加载
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/assets/docs/log.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "stage": "alpha", 4 | "version": "0.0.0", 5 | "dotColor": "red", 6 | "size": "large", 7 | "date": "2023-10-08", 8 | "changes": { 9 | "milestone": ["萌发了想制作视频网站的念头"] 10 | } 11 | }, 12 | { 13 | "stage": "alpha", 14 | "version": "0.1.0", 15 | "dotColor": "blue", 16 | "date": "2023-10-14", 17 | "size": "small", 18 | "changes": { 19 | "feature": ["加入基本的用户注册与登录", "加入基本的发布与查看视频"], 20 | "fix": ["修复了已知bug"] 21 | } 22 | }, 23 | { 24 | "stage": "alpha", 25 | "version": "0.2.0", 26 | "dotColor": "blue", 27 | "date": "2023-10-19", 28 | "size": "small", 29 | "changes": { 30 | "feature": ["加入了签到功能", "现在可以AI审核视频了"], 31 | "architecture": ["使用腾讯云对象存储"] 32 | } 33 | }, 34 | { 35 | "stage": "alpha", 36 | "version": "0.2.1", 37 | "dotColor": "green", 38 | "date": "2023-10-21", 39 | "size": "small", 40 | "changes": { 41 | "feature": ["使用上传视频分片技术,可以上传更大的视频了"], 42 | "fix": ["修复了已知bug"] 43 | } 44 | }, 45 | { 46 | "stage": "beta", 47 | "version": "1.0.0", 48 | "dotColor": "red", 49 | "date": "2023-10-28", 50 | "size": "large", 51 | "changes": { 52 | "feature": [ 53 | "加入了更好看的导航栏与侧边栏", 54 | "加入了主页留言板", 55 | "加入了开发日志" 56 | ], 57 | "beautify": ["使用vuetify代替element-plus,全局美化"], 58 | "architecture": [ 59 | "使用pinia代替vuex", 60 | "使用TypeScript代替JavaScript", 61 | "使用vite代替webpack" 62 | ] 63 | } 64 | }, 65 | { 66 | "stage": "beta", 67 | "version": "1.0.1", 68 | "dotColor": "green", 69 | "date": "2023-10-29", 70 | "size": "small", 71 | "changes": { 72 | "feature": ["加入了关于本网站和FAQ页面"], 73 | "other": ["优化经验系统", "优化了session持续时间"] 74 | } 75 | }, 76 | { 77 | "stage": "beta", 78 | "version": "1.1.0", 79 | "dotColor": "blue", 80 | "date": "2023-11-01", 81 | "size": "small", 82 | "changes": { 83 | "feature": [ 84 | "加入了喜闻乐见的排行榜功能", 85 | "添加了网站访问量统计公共页面", 86 | "首页视频选择新增随机、入站必刷页面" 87 | ], 88 | "beautify": [ 89 | "美化了首页视频卡片与导航栏", 90 | "美化了加载页面", 91 | "美化了广告内容" 92 | ], 93 | "fix": [ 94 | "修复了部分页面不能评论的bug", 95 | "修复了经验精度错误", 96 | "修复了竖屏竖屏电脑端显示问题" 97 | ] 98 | } 99 | }, 100 | { 101 | "stage": "beta", 102 | "version": "1.2.0", 103 | "dotColor": "blue", 104 | "date": "2023-11-05", 105 | "size": "small", 106 | "changes": { 107 | "feature": ["通宵12小时完成了弹幕功能", "编辑资料页面回归"], 108 | "beautify": ["我会逐渐美化这个网站的"], 109 | "fix": ["修复了不能上传视频的bug", "修复了不能修改头像的bug"] 110 | } 111 | },{ 112 | "stage": "beta", 113 | "version": "1.2.1", 114 | "dotColor": "green", 115 | "date": "2023-11-11", 116 | "size": "small", 117 | "changes": { 118 | "beautify": ["加了一些细节"], 119 | "fix": ["修复了一些BUG"], 120 | "architecture": ["重构前后端代码,更符合标准"] 121 | } 122 | } 123 | ] 124 | -------------------------------------------------------------------------------- /src/views/user/rank.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 54 | 55 | 130 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' 2 | 3 | const routes: RouteRecordRaw[] = [ 4 | { 5 | path: '/', 6 | name: '主页', 7 | component: () => import('@/views/index.vue'), 8 | }, 9 | { 10 | path: '/log', 11 | name: '开发日志', 12 | component: () => import('@/views/log.vue'), 13 | }, 14 | { 15 | path: '/about', 16 | name: '关于本网站', 17 | component: () => import('@/views/about.vue'), 18 | }, 19 | { 20 | path: '/faq', 21 | name: 'FAQ', 22 | component: () => import('@/views/faq.vue'), 23 | }, 24 | { 25 | path: '/data', 26 | name: '网站可公开数据', 27 | component: () => import('@/views/webData.vue'), 28 | }, 29 | { 30 | path: '/member', 31 | name: '创作中心', 32 | component: () => import('@/views/fatherContainer.vue'), 33 | children: [ 34 | { 35 | path: 'upload', 36 | name: '上传', 37 | meta: { layout: 'form' }, 38 | component: () => import('@/views/member/upload.vue'), 39 | }, 40 | ], 41 | }, 42 | { 43 | path: '/user', 44 | name: '我的信息', 45 | component: () => import('@/views/fatherContainer.vue'), 46 | children: [ 47 | { 48 | path: 'signin', 49 | name: '登录', 50 | meta: { layout: 'form' }, 51 | component: () => import('@/views/user/signin.vue'), 52 | }, 53 | { 54 | path: 'signup', 55 | name: '注册', 56 | meta: { layout: 'form' }, 57 | component: () => import('@/views/user/signup.vue'), 58 | }, 59 | { 60 | path: 'setting', 61 | name: '修改个人信息', 62 | meta: { layout: 'form' }, 63 | component: () => import('@/views/user/setting.vue'), 64 | }, 65 | { 66 | path: 'rank', 67 | name: '喵绘者排行', 68 | component: () => import('@/views/user/rank.vue'), 69 | }, 70 | ], 71 | }, 72 | { 73 | path: '/video/:aid', 74 | component: () => import('@/views/video/detail.vue'), 75 | }, 76 | { 77 | path: '/space/:uid', 78 | component: () => import('@/views/space/index.vue'), 79 | }, 80 | { 81 | path: '/admin', 82 | name: '管理员', 83 | component: () => import('@/views/fatherContainer.vue'), 84 | children: [ 85 | { 86 | path: 'hidevideo', 87 | name: '隐藏视频', 88 | meta: { layout: 'form' }, 89 | component: () => import('@/views/admin/hideVideo.vue'), 90 | }, 91 | ], 92 | }, 93 | ] 94 | 95 | const router = createRouter({ 96 | history: createWebHistory(), 97 | routes, 98 | }) 99 | 100 | import { useUserStore } from '../store/userStore' 101 | 102 | router.beforeEach(async (to, _from, next) => { 103 | document.title = `${String( 104 | to.name || to.params.aid || to.params.uid 105 | )} - 猫猫站` 106 | 107 | const userStore = useUserStore() 108 | 109 | if (userStore.sessionData.isload == false) { 110 | await userStore.fetchSessionData() 111 | } 112 | 113 | const { signin, status } = userStore.sessionData 114 | 115 | if (signin && (to.path === '/user/signup' || to.path === '/user/signin')) { 116 | next('/') 117 | return 118 | } 119 | 120 | if ( 121 | !signin && 122 | (to.path === '/member/upload' || to.path === '/user/setting') 123 | ) { 124 | next('/user/signin') 125 | return 126 | } 127 | // TODO: 这只是一个应急方式,后续需要改成动态路由 128 | if (status !== 1 && to.path === '/admin/hidevideo') { 129 | next('/') 130 | return 131 | } 132 | 133 | next() 134 | }) 135 | 136 | export default router 137 | -------------------------------------------------------------------------------- /src/views/user/signin.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 139 | -------------------------------------------------------------------------------- /src/components/video/VideoList.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 123 | 124 | 131 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 119 | -------------------------------------------------------------------------------- /src/components/video/VideoGrid.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 92 | 93 | 191 | -------------------------------------------------------------------------------- /src/views/space/index.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 149 | 150 | 180 | -------------------------------------------------------------------------------- /src/views/video/detail.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 178 | 179 | 205 | -------------------------------------------------------------------------------- /src/components/common/comment.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 209 | -------------------------------------------------------------------------------- /src/components/navBar.vue: -------------------------------------------------------------------------------- 1 | 98 | 99 | 202 | 203 | 237 | -------------------------------------------------------------------------------- /src/views/user/setting.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 212 | 213 | 252 | -------------------------------------------------------------------------------- /src/views/user/signup.vue: -------------------------------------------------------------------------------- 1 | 139 | 140 | 278 | -------------------------------------------------------------------------------- /src/views/member/upload.vue: -------------------------------------------------------------------------------- 1 | 194 | 195 | 457 | 458 | 503 | -------------------------------------------------------------------------------- /src/components/video/player.vue: -------------------------------------------------------------------------------- 1 | 99 | 100 | 383 | 384 | 543 | --------------------------------------------------------------------------------