登录注册功能页面完成,视频列表使用选项式失败,视频播放未设置,全局的请求拦截未设置

This commit is contained in:
junleea 2024-01-07 18:14:35 +08:00
commit c864054c59
22 changed files with 2251 additions and 0 deletions

30
.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
}

29
README.md Normal file
View File

@ -0,0 +1,29 @@
# video_ca
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Compile and Minify for Production
```sh
npm run build
```

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

8
jsconfig.json Normal file
View File

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

1536
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "video_ca",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.6.5",
"element-plus": "^2.4.4",
"video.js": "^8.9.0",
"vue": "^3.3.11",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.6.2",
"vite": "^5.0.10"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

11
src/App.vue Normal file
View File

@ -0,0 +1,11 @@
<script setup>
// import Login from "@/views/Login.vue";
</script>
<template>
<!-- <Login /> -->
<router-view></router-view>
</template>
<style>
</style>

17
src/api/user.js Normal file
View File

@ -0,0 +1,17 @@
import request from '@/utils/request.js'
export const loginService = (loginData) => {
const params = new URLSearchParams();
for (let key in loginData) {
params.append(key, loginData[key])
}
return request.post('/user/login', params)
}
export const registerService = (registerData) => {
const params = new URLSearchParams();
for (let key in loginData) {
params.append(key, registerData[key])
}
return request.post('/user/register', params)
}

24
src/api/video.js Normal file
View File

@ -0,0 +1,24 @@
import request from '@/utils/request.js'
export const playVideoService = (data) => {
const params = new URLSearchParams();
for (let key in data) {
params.append(key, data[key])
}
return request.post('/video/m3u8', params, Headers = { 'Accept': 'application/vnd.apple.mpegurl', Authorization: data.Authorization });
}
export const getVideoListService = (data) => {
const params = new URLSearchParams();
for (let key in data) {
params.append(key, data[key])
}
// request.headers["Content-Type"] = "application/json";
request.defaults.headers["token"] = data.token.value;
return request.post('/video/get_video_list', params, {
headers: {
'token': data.token, // 将 token 替换为您的令牌值
}
}
);
}

86
src/assets/base.css Normal file
View File

@ -0,0 +1,86 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

1
src/assets/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

35
src/assets/main.css Normal file
View File

@ -0,0 +1,35 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}

11
src/main.js Normal file
View File

@ -0,0 +1,11 @@
import './assets/main.css'
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from '@/router'
createApp(App).use(router).use(ElementPlus).mount('#app')

30
src/router/index.js Normal file
View File

@ -0,0 +1,30 @@
import {createWebHistory, createRouter} from "vue-router";
import LoginVue from "@/views/Login.vue";
import VideoVue from "@/views/Video.vue";
import VideoListVue from "@/views/VideoList.vue";
const routes = [
{
path: '/login',
name: 'Login',
component: LoginVue
},
{
path: '/video',
name: 'Video',
component: VideoVue
},
{
path: '/videoList',
name: 'VideoList',
component: VideoListVue
},
{
path: '/',
redirect: '/login'
}
]
const router = createRouter({routes: routes, history: createWebHistory()} );
export default router;

7
src/utils/global.js Normal file
View File

@ -0,0 +1,7 @@
import { ref } from "vue";
const token = ref(""); // 定义一个响应式全局变量
const userId=ref(1);
const videoId=ref(1);
const videoName=ref("");
export default token; // 导出全局变量

28
src/utils/request.js Normal file
View File

@ -0,0 +1,28 @@
import axios from "axios";
import router from "@/router";
const baseURL = "/api";
const request = axios.create({
baseURL: baseURL,
});
request.interceptors.response.use(
result => {
if(result.status!==200){
router.push("/login")
}
if(result.data.code===0){
return result.data;
}else{
alert(result.data.message ? result.data.message : "请求失败,请稍后重试!");
return Promise.reject(result.data.message);
}
},
error => {
alert("请求失败,请稍后重试!");
return Promise.reject(error);
}
)
request.interceptors.request.use(
)
export default request;

167
src/views/Login.vue Normal file
View File

@ -0,0 +1,167 @@
<template>
<el-row class="loginPage">
<!-- element-plus login form -->
<el-form
ref="form"
size="large"
autocomplete="false"
:model="loginData"
:rules="rules"
v-if="isLogin === true"
>
<el-form-item>
<h1>登录</h1>
</el-form-item>
<el-form-item prop="username">
<el-input
placeholder="请输入用户名或邮箱"
v-model="loginData.username"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
placeholder="请输入密码"
type="password"
v-model="loginData.password"
></el-input>
</el-form-item>
<el-form-item>
<el-button
class="button"
type="primary"
auto-insert-space
@click="login"
>登录</el-button
>
</el-form-item>
<el-form-item class="flex">
<el-link @click="isLogin = false"> 注册 </el-link>
</el-form-item>
</el-form>
<!-- element-plus register form -->
<el-form
ref="form"
size="large"
autocomplete="false"
:model="registerData"
:rules="rules"
v-if="isLogin === false"
>
<el-form-item>
<h1>注册</h1>
</el-form-item>
<el-form-item prop="username">
<el-input
placeholder="请输入用户名"
v-model="registerData.username"
></el-input>
</el-form-item>
<el-form-item prop="email">
<el-input
placeholder="请输入邮箱"
v-model="registerData.email"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
placeholder="请输入密码"
type="password"
v-model="registerData.password"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
placeholder="请再次输入密码"
type="password"
v-model="registerData.repassword"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" auto-insert-space>注册</el-button>
</el-form-item>
<el-form-item class="flex">
<el-link @click="isLogin = true"> 返回 </el-link>
</el-form-item>
</el-form>
</el-row>
</template>
<script setup>
import { ref } from "vue";
import axios from "axios";
import { loginService, registerService } from "@/api/user.js";
import token from "@/utils//global.js";
import router from "@/router/index.js";
const isLogin = ref(true);
const loginData = ref({
username: "",
email: "lijun.lj@foxmail.com",
password: "",
});
const registerData = ref({
username: "",
email: "",
password: "",
repassword: "",
});
//
const rules = {
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{
min: 6,
max: 20,
message: "密码长度在 6 到 20 个字符",
trigger: "blur",
},
],
username: [
{ required: true, message: "请输入用户名", trigger: "blur" },
{
min: 5,
max: 20,
message: "用户名长度在 6 到 20 个字符",
trigger: "blur",
},
],
email: [{}],
};
//
const login = async () => {
// let req = axios.create({
// baseURL: "/api",
// timeout: 5000,
// });
// await req
// .post("/user/login", {
// email: loginData.value.username,
// password: loginData.value.password,
// })
// .then((response) => {
// alert(response.data.message);
// resp = response.data;
// jwt = resp.data;
// console.log(jwt);
// })
// .catch((error) => {
// console.error(error);
// });
let result = await loginService(loginData.value);
alert(result.message);
token.value= result.data;
router.push("/videoList", {token:result.data});
};
const register = async () => {
let result = registerService(registerData.value);
if (result.code === 0) {
alert(result.message);
} else {
alert(result.message);
}
};
</script>

35
src/views/Video.vue Normal file
View File

@ -0,0 +1,35 @@
<script setup>
import token from "@/utils/global.js";
import axios from "axios";
import videojs from "video.js";
const player = videojs(this.$refs.videoPlayer, this.options); //
axios
.post(
"/video/m3u8",
{
// POST
id: "",
userId: "",
filename: "",
},
{
headers: {
Authorization: token.value,
},
}
)
.then((response) => {
// response.data m3u8
player.src({ type: "application/x-mpegURL", src: response.data }); // m3u8
player.play(); //
})
.catch((error) => {
//
});
</script>
<template>
<div>
<video ref="videoPlayer" class="video-js"></video>
</div>
</template>

133
src/views/VideoList.vue Normal file
View File

@ -0,0 +1,133 @@
<script>
import axios from "axios";
import { getVideoListService } from "@/api/video.js";
import { onMounted, ref } from "vue";
import token from "@/utils//global.js";
import router from "@/router/index.js";
export default {
// data()
// `this`
data() {
return {
tableData: {},
tokenData: { token: "" },
};
},
// methods
//
methods: {
async getVideoList() {
this.tokenData.token = token.value;
console.log(this.tokenData.token);
let result = {};
try {
result = await getVideoListService(this.tokenData);
} catch (e) {
console.log(e);
}
this.tableData = JSON.parse(result.data);
console.log(this.tableData[0]);
},
onSubmit() {
getVideoList({ token: token });
},
handleSizeChange() {
alert("每页记录数变化" + val);
},
handleCurrentChange() {
alert("页码发生变化" + val);
},
},
//
//
mounted() {
getVideoList();
},
};
</script>
<template>
<div>
<el-container style="height: 700px; border: 1px solid #eee">
<el-header style="font-size: 40px; background-color: rgb(238, 241, 246)"
>监控视频列表</el-header
>
<el-container>
<el-main>
<!-- 表单 -->
<!-- <el-form :inline="true" :model="searchForm" class="demo-form-inline">
<el-form-item label="姓名">
<el-input v-model="searchForm.name" placeholder="姓名"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="searchForm.gender" placeholder="性别">
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="视频日期">
<el-date-picker
v-model="searchForm.entrydate"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
>
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">查询</el-button>
</el-form-item>
</el-form> -->
<!-- 表格 -->
<el-table :data="tableData" border>
<el-table-column prop="id" label="id" width="250"></el-table-column>
<el-table-column
prop="video_name"
label="视频名称"
width="250"
></el-table-column>
<el-table-column label="是否有人" width="250">
<template human="human">
{{ human.row.human == 1 ? "有" : "无" }}
</template>
</el-table-column>
<el-table-column
prop="create_time"
label="创建时间"
width="250"
></el-table-column>
<el-table-column
prop="delete_time"
label="预期删除时间"
width="250"
></el-table-column>
<el-table-column label="操作">
<el-button type="primary" size="mini">播放</el-button>
<el-button type="danger" size="mini">删除</el-button>
</el-table-column>
</el-table>
<br />
<!-- 分页条 -->
<!-- Pagination 分页 -->
<el-pagination
background
layout="total,sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:total="1000"
></el-pagination>
</el-main>
</el-container>
</el-container>
</div>
</template>

25
vite.config.js Normal file
View File

@ -0,0 +1,25 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server : {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
})