0%

执行ssh-keygen命令,生成id_rsa和id_rsa.pub两个文件,id_rsa是私钥(重要,需安全保管),id_rsa.pub是公钥,密钥生成过程中可根据提示对密钥设置密码,也可留空直接回车。

1
ssh-keygen -t rsa

创建authorized_keys文件并设置权限

1
2
[root@server1 ~]# touch ~/.ssh/authorized_keys
[root@server1 ~]# chmod 600 ~/.ssh/authorized_keys

将公钥内容追加到authorized_keys文件中

1
2
[root@server1 ~]# cd ~/.ssh
[root@server1 .ssh]# cat id_rsa.pub >> authorized_keys

修改 /etc/ssh/sshd_config文件,添加以下参数

注意 PermitRootLogin yes 这句会拦截root用户,可以先不写,等证书登录测试成功了加上

1
2
3
4
RSAAuthentication yes
PubkeyAuthentication yes
#PermitRootLogin yes # 注意禁止root登录
AuthorizedKeysFile .ssh/authorized_keys

修改完配置文件,重启sshd服务

1
systemctl restart sshd

在Linux主机上登录验证

1
ssh root@localhost -i id_rsa

禁用密码登录,修改 /etc/ssh/sshd_config文件

1
PasswordAuthentication no

重启sshd服务

1
systemctl restart sshd

1.App.vue

将获取菜单的方法放在全局中,以便每次刷新页面时,能够加载出。this.$store.state.userInfo是登入后存放在Vuex的用户信息

TODO:把数据放到本地存储,没有的时候再加载,这只是demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div id="app">
<router-view />
</div>
</template>

<script>
import { generaMenu } from '@/assets/js/menu'
export default {
created() {
if (this.$store.state.userInfo != null) {
generaMenu()
}
}
}
</script>

2.menu.js(@/assets/js路径下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import Layout from '@/layout/index.vue'
import router from '@/router'
import store from '@/store'
import axios from 'axios'
import Vue from 'vue'

export function generaMenu() {
axios.get('/api/admin/user/menus').then(({ data }) => {
if (data.flag) {
let userMenus = data.data;
userMenus.forEach((item) => {
if (item.icon != null) {
item.icon = 'iconfont ' + item.icon
}
if (item.component == 'Layout') {
item.component = Layout
}
if (item.children && item.children.length > 0) {
item.children.forEach((route) => {
route.icon = 'iconfont ' + route.icon;
route.component = loadView(route.component)
})
}
})
store.commit('saveUserMenus', userMenus);
userMenus.forEach((item) => {
router.addRoute(item)
})
} else {
Vue.prototype.$message.error(data.message);
router.push({ path: '/login' })
}
})
}

export const loadView = (view) => {
return (resolve) => require([`@/views${view}`], resolve)
}

3.SideBar.vue(侧边栏)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<template>
<div>
<el-menu
class="side-nav-bar"
router
:collapse="this.$store.state.collapse"
:default-active="this.$route.path"
background-color="#304156"
text-color="#BFCBD9"
active-text-color="#409EFF">
<template v-for="route of this.$store.state.userMenus">
<template v-if="route.name && route.children && !route.hidden">
<el-submenu :key="route.path" :index="route.path">
<template slot="title">
<i :class="route.icon" />
<span>{{ route.name }}</span>
</template>
<template v-for="(item, index) of route.children">
<el-menu-item v-if="!item.hidden" :key="index" :index="item.path">
<i :class="item.icon" />
<span slot="title">{{ item.name }}</span>
</el-menu-item>
</template>
</el-submenu>
</template>
<template v-else-if="!route.hidden">
<el-menu-item :index="route.path" :key="route.path">
<i :class="route.children[0].icon" />
<span slot="title">{{ route.children[0].name }}</span>
</el-menu-item>
</template>
</template>
</el-menu>
</div>
</template>

<style scoped>
.side-nav-bar:not(.el-menu--collapse) {
width: 210px;
}
.side-nav-bar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
overflow-x: hidden;
overflow-y: auto;
}
.side-nav-bar i {
margin-right: 1rem;
}
*::-webkit-scrollbar {
width: 0.5rem;
height: 1px;
}
*::-webkit-scrollbar-thumb {
border-radius: 0.5rem;
background-color: rgba(144, 147, 153, 0.3);
}
</style>

html版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>弹性盒容器-媒体查询-百分比布局--实现响应式布局</title>
<style>
body {
font: 24px Helvetica;
background-color: #fff;
}
/* 弹性盒容器 */

.main {
display: flex;
min-height: 500px;
margin: 0;
padding: 0;
flex-flow: row;
}

.main>* {
margin: 4px;
padding: 5px;
border-radius: 7px;
}

article {
background-color: #719dca;
order: 2;
flex-grow: 3;
}

nav {
background-color: #ffba41;
order: 1;
flex-grow: 1;
/* width: 20%; */
}

aside {
background-color: aquamarine;
order: 3;
flex-grow: 1;
/* width: 20%; */
}

header,
footer {
display: block;
margin: 4px;
padding: 5px;
min-height: 100px;
border: 2px solid #7e7234;
border-radius: 7px;
background-color: rgb(43, 144, 226);
}

footer {
background-color: chocolate;
}
/* 媒体查询-当屏幕宽度小于XX,弹性盒容器内子元素按照纵轴方向排列 */

@media screen and (max-width:640px) {
.main {
flex-flow: column;
}
article,
nav,
aside {
order: 0;
}
nav,
aside,
header,
footer {
min-height: 50px;
max-height: 50px;
}
}
</style>
</head>

<body>
<header>弹性盒容器-媒体查询-百分比布局--实现响应式布局</header>
<!-- 弹性盒容器 -->
<div class="main">
<!-- 弹性盒容器子元素 -->
<article>文章</article>
<nav>导航栏</nav>
<aside>侧边栏</aside>
</div>
<footer>date:2023-12-19</footer>
</body>

</html>

vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<template>
<el-container>
<el-header>头部</el-header>
<el-container>
<!-- 准备两份aside侧边栏 -->
<!-- 利用isCollapse动态控制侧边栏的宽度 -->
<el-aside :width="isCollapse ? '64px' : '200px'" class="show">
<!-- 切换侧边栏的显示与隐藏效果 -->
<div class="toggle" @click="toggleCollapse">|||</div>
<!-- collapse: 是否水平折叠收起菜单 -->
<!-- collapse-transition:开启折叠动画 -->
<el-menu :collapse="isCollapse" :collapse-transition="false" default-active="2" class="el-menu-vertical-demo" @open="handleOpen"
@close="handleClose" background-color="#333744" text-color="#fff" active-text-color="#ffd04b">
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航一</span>
</template>
<el-menu-item index="1-4">选项1</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<el-main>主体</el-main>
</el-container>
</el-container>
</template>

<script>
export default {
name: 'App',
data () {
return {
// 控制侧边栏的显示与隐藏
isCollapse: false // 默认显示侧边栏
}
},
methods: {
// 控制侧边栏的显示与隐藏
toggleCollapse () {
this.isCollapse = !this.isCollapse
}
}
}
</script>

<style lang="less">
html,
body,
.el-container {
margin: 0;
height: 100%;
}

.el-container {
.el-header {
padding: 0;
background-color: #373d41;
color: #fff;
}

.el-aside {
background-color: #333744;

// 默认只显示一份aside侧边栏
&.show {
display: block;
}

&.hide {
display: none;
}

// 媒体查询,实现响应式
@media (max-width: 992px) {
// 取值与上面相反即可
&.show {
display: none;
}

// 取值与上面相反即可
&.hide {
display: block;
}
}

.toggle {
letter-spacing: 0.2em;
color: #fff;
text-align: center;
line-height: 24px;
background-color: #4a5064;
cursor: pointer;
}

.el-menu {
border-right: none;
}
}

.el-main {
background-color: #eaedf1;
}
}
</style>

gitee地址 https://gitee.com/newly-released_0/express-mysql-jwt

jwt的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
const express = require('express')
const app = express()
const compression = require('compression');
const cors = require('cors')
// 导入 jsonwebtoken 和express-jwt 两个包
const jwt = require('jsonwebtoken')
const {expressjwt:jwt2}= require('express-jwt')

var usersRouter = require("./controllers/UserController");
// 创建secret对象
const secretKey = 'tokenKey123 num.1 ^_^'

require('./utils/db.js')

app.use(express.urlencoded({ extended: false }));
app.use(express.json())

app.use(cors())
// 压缩
app.use(compression());

// 注册将JWT字符串解析还原成JSON对象的中间件
// .unless()指定不需要访问权限的接口
app.use(jwt2({ secret: secretKey,algorithms:["HS256"] }).unless({ path: [/^\/user\//,'/api/login'] }))

app.use('/user', usersRouter);
// 编写登陆接口
app.post('/api/login',(req,res)=>{
const userinfo = req.body

// 登陆成功 => 将用户信息转换成token,并设置有效期
const token = jwt.sign({username:userinfo.username},secretKey,{expiresIn:'300s'})
res.send({
staus:200,
msg:'登陆成功!',
token:token
})
})

app.get('/admin/getinfo',(req,res)=>{
console.log(req.auth);
res.send({
staus:200,
message:'获取用户信息成功!',
datas :req.auth.username
})
})

// 全局处理404
app.use('*', (req,res) => {
res.send({
status : 200,
msg : "404 , not found",
data : ''
})
})

//最后使用错误中间件
app.use((err, req, res, next) => {
console.error(err.stack);
// 这次失败是由token解析失败导致的
if(err.name==='UnauthorizedError'){
return res.send({
staus: 401,
message:'无效的token'
})
}
res.send({
status:500,
message:'未知的错误'
})
});

app.listen("3333",()=>{
console.log("run as 3333")
})

使用pm2多进程部署express

1
npm install -g pm2

启动

1
2
3
4
5
6
7
pm2 start app.js 
pm2 start app.js -i max 根据有效CPU数目启动最大进程数目
pm2 start app.js -i 3 启动3个进程
pm2 start app.js -x 用fork模式启动 app.js 而不是使用 cluster
pm2 start app.js -x -- -a 23 用fork模式启动 app.js 并且传递参数 (-a 23)
pm2 start app.js --name my-app 启动一个进程并把它命名为 my-app
pm2 stop my-app 根据名字停止进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
var express = require('express');
var expressWs = require('express-ws');
var app = express();
expressWs(app);

var PORT = 7777;
var clientObject = {};

app.ws('/', (client, req) => {
var key = req.socket.remoteAddress + "_" + req.socket.remotePort;
clientObject[key] = {
cli: client,
lastActiveTime: Date.now(), // 记录最后活跃时间
};

client.on('message', (message) => {
client.send("收到你的消息了" + message);
clientObject[key].lastActiveTime = Date.now(); // 更新最后活跃时间
});

client.on('close', (code, reason) => {
console.log(`WebSocket close event code: ${code}, reason: ${reason}`);
delete clientObject[key];
});

client.on('error', (err) => {
console.error("WebSocket error observed:", err);
delete clientObject[key];
});
});

app.get('/', (req, res) => {
res.send("hello 2023");
});

// 封装心跳逻辑
function setupHeartbeat() {
setInterval(() => {
var time = Date.now();
for (var key in clientObject) {
var client = clientObject[key].cli;
var lastActiveTime = clientObject[key].lastActiveTime;

// 检查最后一次活跃时间是否超过了1分钟(60000毫秒)
if (time - lastActiveTime > 60000) {
console.log('Client timed out due to inactivity:', key);
// 关闭连接,删除
client.close();
delete clientObject[key];
continue;
}

try {
var sData = {
rspdata: {
heartBeat: time,
time: time,
},
};
client.send(JSON.stringify(sData));
} catch (e) {
console.error("Heartbeat error:", e);
// 如果发送心跳失败,也关闭连接
client.close();
delete clientObject[key];
}
}
}, 3000); // 每3秒发送一次心跳
}

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
setupHeartbeat(); // 设置心跳检测
});

这里只是做个笔记,js,css那些都没写

子组件 MenuItem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<template v-if="item.children">
<el-sub-menu :index="item.value">
<template #title>{{ item.label }}</template>
<MenuItem v-for="childItem in item.children" :key="childItem.value" :item="childItem" />
</el-sub-menu>
</template>
<template v-else>
<el-menu-item :index="item.value">{{ item.label }}</el-menu-item>
</template>
</template>
<script setup>
import { defineProps } from 'vue';
defineProps(['item']);
</script>
<style scoped>
</style>

父组件,调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-vertical-demo"
@select="handleSelect"
background-color="#f8f8f9"
style="margin-top: 20px;margin-left: 1px;width: 20%"
>
<MenuItem v-for="menuItem in menuItems" :key="menuItem.value" :item="menuItem" />
</el-menu>
</template>
<script setup>
import MenuItem from "./components/MenuItem.vue";
import {ref} from "vue";
const menuItems = [
{
value: '1',
label: '菜单1',
children: [
{
value: '1-1',
label: '子菜单1-1',
children: [
{ value: '1-1-1', label: '子菜单1-1-1' },
{ value: '1-1-2', label: '子菜单1-1-2' },
],
},
{ value: '1-2', label: '子菜单1-2' },
],
},
{
value: '2',
label: '菜单2',
children: [
{ value: '2-1', label: '子菜单2-1' },
{
value: '2-2',
label: '子菜单2-2',
children: [
{ value: '2-2-1', label: '子菜单2-2-1' },
{ value: '2-2-2', label: '子菜单2-2-2' },
],
},
],
},
{
value: '3',
label: '菜单3',
children: [
{
value: '3-1',
label: '子菜单3-1',
children: [
{
value: '3-1-1',
label: '子菜单3-1-1',
children: [
{ value: '3-1-1-1', label: '子菜单3-1-1-1' },
{ value: '3-1-1-2', label: '子菜单3-1-1-2' },
],
},
],
},
],
},
];
const activeIndex = ref(1)
const handleSelect = async (key, keyPath) => {
console.log(key, keyPath)
this.activeIndex = key
}
</script>

vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<template>
<div class="container">
<el-row>
<el-col :span="4">
<!-- 左边列表项 -->
<div class="scrollable-menu">
<el-menu
class="el-menu-vertical-demo"
background-color="#304156"
text-color="#fff"
active-text-color="#ffd04b"
>
<el-menu-item
v-for="item in items"
:key="item.id"
:index="item.id.toString()"
@click="selectItem(item)"
>
<i class="el-icon-menu"></i>
{{ item.name }}
</el-menu-item>
</el-menu>
</div>
</el-col>
<el-col :span="12">
<!-- 右边选中的数据和数量 -->
<div
v-for="(selectedItem, index) in selectedItems"
:key="index"
class="item-container"
>
<div class="flex-container">
<span class="item-name">{{ selectedItem.name }}</span>
<el-input
type="number"
:min="1"
v-model.number="selectedItem.quantity"
class="quantity-input"
@change="updateQuantity(selectedItem.id, selectedItem.quantity)"
></el-input>
<el-button type="danger" @click="removeItem(index)">移除</el-button>
</div>
</div>
<el-button type="primary" @click="addSelectedItem">提交</el-button>
</el-col>
</el-row>
</div>
</template>

<script>
export default {
data() {
return {
items: [
{id: 1, name: '数据1'},
{id: 2, name: '数据2'},
{id: 3, name: '数据3'},
{id: 4, name: '数据4'},
{id: 5, name: '数据5'},
{id: 6, name: '数据6'},
{id: 7, name: '数据7'},
{id: 8, name: '数据8'},
{id: 9, name: '数据9'},
{id: 10, name: '数据10'},
// 假设这里有很多数据项...
],
selectedItems: [],
};
},
methods: {
selectItem(item) {
const existingItem = this.selectedItems.find((x) => x.id === item.id);
if (existingItem) {
existingItem.quantity += 1;
} else {
this.selectedItems.push({...item, quantity: 1});
}
},
removeItem(index) {
this.selectedItems.splice(index, 1);
},
updateQuantity(id, quantity) {
const item = this.selectedItems.find((x) => x.id === id);
if (item) {
item.quantity = quantity;
}
},
addSelectedItem() {
console.log(this.selectedItems)
},
},
};
</script>

<style scoped>
.container {
//margin: 30px;
}

.scrollable-menu {
max-height: 400px; /* 可调整为适合的高度 */
overflow-y: auto;
}

.item-container {
margin-bottom: 10px;
}

.flex-container {
display: flex;
align-items: center;
width: 100%;
}

.quantity-input {
margin-right: 10px;
}

.item-name {
margin-right: 10px;
}

.el-input {
width: 50%;
}

.el-button {
text-align: center;
}
</style>

html版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bootstrap 美化的数据列表示例</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>

<div class="container">
<div class="row">
<div class="col-md-6" style=" height: 500px; overflow-y: scroll;">
<!-- 左边列表项 -->
<ul class="list-group" id="list-container">
<li class="list-group-item" data-id="1">数据1</li>
<li class="list-group-item" data-id="2">数据2</li>
<li class="list-group-item" data-id="3">数据3</li>
</ul>
</div>
<div class="col-md-6">
<!-- 右边选中的数据和数量 -->
<div class="input-group mb-3">
</div>
<ul class="list-group" id="selected-list">
<!-- 右边选中的数据和数量将显示在这里 -->
</ul>
</div>
</div>
</div>

<!-- 引入 Bootstrap 的 JavaScript 组件 -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

<script>
document.addEventListener('DOMContentLoaded', function() {
var listContainer = document.getElementById('list-container');
var selectedList = document.getElementById('selected-list');
// 监听左边列表项的点击事件
listContainer.addEventListener('click', function(e) {
var item = e.target.closest('.list-group-item');
if (item) {
var itemId = item.getAttribute('data-id');
updateRightSide(itemId); // 更新右边的数据
}
});

// 更新或增加右边的数据和数量
function updateRightSide(itemId) {
var itemText = '数据' + itemId;
var selectedItem = selectedList.querySelector('li[data-id="' + itemId + '"]');
var quantity = 1;

if (selectedItem) {
// 如果右边已经有这个数据项,增加数量
quantity = parseInt(selectedItem.querySelector('input[type="number"]').value, 10) + 1;
selectedItem.querySelector('input[type="number"]').value = quantity;
} else {
// 如果右边没有这个数据项,创建新的列表项
selectedItem = document.createElement('li');
selectedItem.className = 'list-group-item d-flex justify-content-between align-items-center';
selectedItem.setAttribute('data-id', itemId);
selectedItem.innerHTML = '<span>' + itemText + '</span>' +
'<input type="number" class="quantity form-control form-control-sm" value="' + quantity + '" min="1" style="max-width: 80px;">' +
'<button type="button" class="close" aria-label="Remove"><span aria-hidden="true">&times;</span></button>';
selectedItem.querySelector('.close').addEventListener('click', function() {
// 删除右边列表中的项
selectedList.removeChild(selectedItem);
});
selectedList.appendChild(selectedItem);
}
}

// 监听添加按钮的点击事件
addButton.addEventListener('click', function() {
// 这里可以根据需要添加逻辑,比如更新数据库或执行其他操作
// ...
});
});
</script>

</body>
</html>

安装

1
npm install vue-quill-editor --save

全局引入

1
2
3
4
5
6
7
import Vue from 'vue'
import VueQuillEditor from 'vue-quill-editor'
// 引入样式
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
Vue.use(VueQuillEditor)

指定vue文件中引入

1
2
3
4
5
6
7
8
9
10
11
12
// 引入样式
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'

import { quillEditor } from 'vue-quill-editor'

export default {
components: {
quillEditor
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<quill-editor v-model="content"
ref="myQuillEditor"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)">
</quill-editor>
</template>

<script>
export default {
data () {
return {
content: '',
editorOption: {}, //编辑器配置项
};
},
methods: {
onEditorBlur () { }, // 失去焦点触发事件
onEditorFocus () { }, // 获得焦点触发事件
onEditorChange () { }, // 内容改变触发事件
}
}
</script>

1
2
3
4
5
6
7
8
9
.container{
height: 300px;
width: 300px;
background: red;
position: absolute;
top:50%;
left: 50%;
translate:-50%-50%;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
<video
id="videoID"
src="video.mp4"
poster="loadbg.jpg" 视频封面
preload="auto"
x-webkit-airplay="allow"
x5-video-player-type="h5"  启用H5播放器,是wechat安卓版特性
x5-video-player-fullscreen="true" 全屏设置,设置为 true 是防止横屏
x5-video-orientation="portraint"  播放器支付的方向,landscape横屏,portraint竖屏,默认值为竖屏
webkit-playsinline="true"  这个属性是iOS 10中设置可以让视频在小窗内播放,也就是不是全屏播放
playsinline="true"  ios微信浏览器支持小窗内播放
style="object-fit:fill">
</video>