闭包
深(新的全部)浅(地址第一层)拷贝
git 命令
样式 老项目 ~@ 现在脚手架项目 @
背景图问题 设置高度
AAA大事件项目
思路:
1.配置vue脚手架
2.创建项目,npm操作上传初始化项目
AAA001注册功能
引入配置路由 router
配置子组件
elementUI 使用方法
主体登录模块 from表单 input ico图标
匹配密码是否一致 from表单验证
axios 根据文档来设置请求和 判断 响应
注册成功—>登录跳转 登录模块
1.相同的设置主体结构
2.校验数据输入
3.校验数据和数据库是否一致
4.实现登录成功功能 判断
5.登录成功跳转
6.将获取的token存起来## 登陆成功---->跳转首页
首页退出功能
根据elementui构建侧边栏,点击退出功能
//1.判断是否要退出
//2.销毁token
//3.跳转到登录页面
vuex修改值的mutations
1.先在vuex获取数据并传给 userinfo
1.1获取到数据
1.2把值调用修改数据
1.3把获取的值赋值给空值(来接收修改的数据)
在 Main.vue
组件的 created
生命周期函数中,调用 Vuex 中的 initUserInfo 函数,获取用户的基本信息:
在 Main.vue
组件中,基于 mapState
辅助函数,把 Vuex 中的 userInfo
数据映射到当前组件中使用:
首页模块
头部
侧边栏 获取数据渲染到页面
<!-- 侧边栏区域 -->
<el-aside width="200px">
<div class="user-box">
<img :src="userinfo.user_pic" alt="" v-if="userinfo.user_pic" />
<img src="../../assets/logo.png" alt="" v-else/>
<span>欢迎:{
{
userinfo.nickname||userinfo.username}}</span>
</div>
<el-menu
default-active="/home"
class="el-menu-vertical-demo"
background-color="aqua"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<template v-for="item in menus">
<!-- 不包含子菜单的“一级菜单” -->
<el-menu-item :index="item.indexPath" :key="item.indexPath" v-if="!item.children"><i class="el-icon-s-tools"></i>{
{
item.title}}</el-menu-item>
<!-- 包含子菜单的“一级菜单” -->
<el-submenu :index="item.indexPath" :key="item.indexPath" v-else>
<template slot="title">
<i :class="item.icon"></i>
<span>{
{
item.title}}</span>
</template>
<el-menu-item :index="subItem.indexPath" v-for="subItem in item.children" :key="subItem.indexPath"><i :class="subItem.icon"></i>{
{
subItem.title}}</el-menu-item>
</el-submenu>
</template>
</el-menu>
</el-aside>
<el-container>
多次axios获取token 解决方法axios请求拦截
**1.删除请求头
优化身份认证的过程 2.在main.js里面添加拦截**
目标:掌握使用拦截器简化 Token 认证的能力
-
在调用获取用户基本信息、获取左侧菜单接口时,不再单独提供
headers
下的Authorization
请求头。 -
在项目入口文件
main.js
中,配置 axios 的请求拦截器:
3.(没有token)判断是否在有token的状态下,才能进入登录成功的状态.
4.(有token) 跳转到登录页面
不合法的token 根据合法的token 才能发送ajax
2.通过响应拦截器判断token是否合法
设置axios响应拦截器
获取用户数据(并修改不会造成直接修改state数据)
点击重置 ----->点击修改
1.考虑到重置是获取上一次的数据
修改数据put 后需要更新vuex组件最新的数据
图片的同步
<template>
<el-card class="box-card">
<template v-slot:header>
<div class="clearfix">
<span>更换头像</span>
</div>
</template>
<div>
<!-- 图片,用来展示用户选择的头像 -->
<img :src="avatar" alt="" v-if="avatar"/>
<img src="@/assets/images/avatar.jpg" v-else/>
<!-- 按钮区域 -->
<div class="btn-box">
<!-- 1.书写input:file的表单项,并且让他隐藏 -->
<input type="file" accrpt="image/*" style="display:none" ref="iptRef" @change="onIptChange">
<el-button type="primary" icon="el-icon-plus" @click="chooseImg" >选择图片</el-button>
<el-button type="success" icon="el-icon-upload" :disabled="avatar===''" @click="uploadImg" >上传头像</el-button>
</div>
</div>
</el-card>
</template>
<script>
export default {
data () {
return {
avatar: ''
}
},
name: 'UserAvtatar',
methods: {
chooseImg () {
this.$refs.iptRef.click()
},
onIptChange (e) {
// console.dir(e.target)
if (e.target.files.length === 0) {
// 没有图片
this.avatar = ''
} else {
// 选择了一张图片 图片对象--> base64 格式的字符串
// e.target.files[0]
// 1. new dileReader() 文件读取器(浏览器自带的)
const reader = new FileReader()
// 2.读取图片对象
reader.readAsDataURL(e.target.files[0])
// 3. 监听onload文件读取完毕事件
reader.onload = () => {
// console.log(reader.result)
// 头像预览赋值
this.avatar = reader.result
}
}
},
async uploadImg () {
// 4.图片上传
const {
data: res } = await this.$http.patch('/my/update/avatar', {
avatar: this.avatar })
// console.log(res)
if (res.code !== 0) return this.$message.error('上传头像失败!')
this.$message.success('上传成功')
// 再次通知vuex获取用户信息 // 上传头像成功
this.$store.dispatch('initUserInfo')
}
}
}
</script>
<style>
</style>
重置密码
1.配置动态rules 校验表单
2.验证密码框是否一致
3.手动校验 patch请求传送新密码 自定义检测新旧密码是否相同和检测两次新密码是否一致的校验规则
4.重置密码
文章分类
<template>
<div>
<el-card class="box-card">
<div slot="header" class="clearfix header-box">
<span>文章分类</span>
<el-button type="primary" size="mini" @click="dialogVisible = true">添加分类</el-button>
</div>
<el-table :data="cateList" style="width: 100%" borde stripe>
<el-table-column type="index" width="50">
</el-table-column>
<el-table-column label="序号" width="180"></el-table-column>
<el-table-column prop="cate_name" label="姓名" width="180">
</el-table-column>
<el-table-column prop="cate_alias" label="分类别名">
</el-table-column>
<el-table-column label="操作">
<!-- 设置插槽 {
row}解构对象 -->
<template v-slot="{row}">
<!-- v-slot="scope" v-slot="{row}" -->
<!-- {
{
scope.column}} {
{
row}} -->
<el-button size="mini" type="primary" @click="editCate(row.id)" >修改</el-button>
<el-button size="mini" type="danger" @click="remove(row.id) ">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 添加文章分类 -->
<el-dialog @closed="headerAddClose" title="添加文章分类" :visible.sync="dialogVisible" width="40%">
<span>
<el-form ref="addCloseRef" :model="form" :rules="CateRules" label-width="80px">
<el-form-item label="分类名称" prop="cate_name">
<el-input v-model="form.cate_name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="分类别名" prop="cate_alias" >
<el-input v-model="form.cate_alias" autocomplete="off"></el-input>
</el-form-item>
</el-form>
</span>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addCate">确 定</el-button>
</span>
</el-dialog>
<!-- 修改文章分类的对话框 -->
<el-dialog title="修改文章分类" :visible.sync="editVisible" width="35%" @closed="handleEditClose">
<span>
<!-- 修改的表单 -->
<el-form :model="editCateForm" :rules="CateRules" ref="editFormRef" label-width="100px">
<el-form-item label="分类名称" prop="cate_name">
<el-input v-model="editCateForm.cate_name"></el-input>
</el-form-item>
<el-form-item label="分类别名" prop="cate_alias">
<el-input v-model="editCateForm.cate_alias"></el-input>
</el-form-item>
</el-form>
</span>
<span slot="footer" class="dialog-footer">
<el-button size="mini" @click="editVisible = false">取 消</el-button>
<el-button size="mini" type="primary" @click="alterCate">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'ArtCate',
data () {
return {
editCateForm: [],
cateList: [],
// 文章新增分类隐藏
dialogVisible: false,
editVisible: false,
form: {
cate_name: '', cate_alias: '' },
CateRules: {
cate_name: [
{
required: true, message: '输入名字', trigger: 'blur' },
{
pattern: /^\S{
1,10}$/, message: '输入名字1~10位非空字符', trigger: 'blur' }],
cate_alias: [
{
required: true, message: '输入分类', trigger: 'blur' },
{
pattern: /^[a-zA-Z0-9]{
1,15}$/, message: '输入名字1~15位字母数字', trigger: 'blur' }
]
}
}
},
methods: {
async initCateList () {
const {
data: res } = await this.$http.get('/my/cate/list')
// console.log(res)
if (res.code === 0) {
this.cateList = res.data
}
},
// 处理关闭重置
headerAddClose () {
this.$refs.addCloseRef.resetFields()
}, // 添加分类
addCate () {
this.$refs.addCloseRef.validate(async (valid) => {
// 手动校验
if (!valid) return
// 发送axios
const {
data: res } = await this.$http.post('/my/cate/add', this.form)
// 可以判断不等于0 添加错误提示
// if (res.code !== 0) return this.$message.error(res.message)
if (res.code === 0) {
// 1.关闭对话框
this.dialogVisible = false
// 2.重新获取分类数据
this.initCateList()
}
})
},
// 修改数据思路 1.点击根据id来修改数据2.插槽
// 修改数据
async editCate (id) {
// id为1或2时 数据不能修改
if (id === 1 || id === 2) return this.$message.warning('该数据不能不修改')
// console.log(id)
// axios获取数据 axios.get('地址',{params:参数}) get或delete
// axios.post(地址,{参数})
/* axios({
url:'请求路径',
method:'get',
data: { 'post请求参数'},
params: { 'get请求参数'}
}).then(res=>{
//成功回调
console.log(res)
}) */
const {
data: res } = await this.$http.get('/my/cate/info', {
params: {
id: id } })
// console.log(res)
// 2.赋值
if (res.code === 0) this.editCateForm = res.data
console.log(this.editCateForm)
// 3. 展示对话框
this.editVisible = true
},
// 处理编辑弹出框关闭事件
handleEditClose () {
this.$refs.editFormRef.resetFields()
},
// 修改数据成功
alterCate () {
this.$refs.editFormRef.validate(async (valid) => {
// 1.手动校验
if (!valid) return
// 2. 发送axios更新数据
const {
data: res } = await this.$http.put('/my/cate/info', this.editCateForm)
// console.log(res)
// 3.判断修改数据
// 失败 res.code !== 0 this.$message.error(res.message)
if (res.code !== 0) return this.$message.error(res.message)
// 成功
this.$message.success(res.message)
// this.editCateForm = res.data ???错误操作
// 4..重新获取分类数据
this.initCateList()
})
this.editVisible = false
},
// 删除数据 根据id删除
async remove (id) {
if (id === 1 || id === 2) return this.$message.warning('不允许删除')
// 方法1
/* // 询问用户是否删除
const res = await this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).catch(err => err)
// 取消
if (res !== 'confirm') return
// 确定后
const { data: result } = await this.$http.delete('/my/cate/del', { params: { id } })
// 判断后弹框
if (result.code !== 0) return this.$message.error(result.message)
// 成功提示
this.$message.success(result.message)
// 更新数据
this.initCateList() */
// 2.方法2
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$message({
type: 'success',
message: '删除成功!'
})
this.$http.delete('/my/cate/del', {
params: {
id } })
this.initCateList()
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
})
})
}
},
// 钩子函数 调用
created () {
this.initCateList()
}
}
</script>
<style lang="less" scoped>
.header-box {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
发表文章
<template>
<div>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>文章列表</span>
</div>
<!-- 搜索区域 -->
<div class="search-box">
<el-form :inline="true" :model="q" ref="searchRef">
<el-form-item label="文章分类">
<el-select v-model="q.cate_id" placeholder="请选择分类" size="small">
<el-option :label="item.cate_name" :value="item.id" v-for="item in cateList" :key="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="发布状态" style="margin-left: 15px;">
<el-select v-model="q.state" placeholder="请选择状态" size="small">
<el-option label="已发布" value="已发布"></el-option>
<el-option label="草稿" value="草稿"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" size="small" @click="search">筛选</el-button>
<el-button type="info" size="small" @click="reset">重置</el-button>
</el-form-item>
</el-form>
<!-- 发表文章的按钮 @click="pubDialogVisible=true" 控制span信息显示-->
<el-button type="primary" size="small" class="btn-pub" @click="pubDialogVisible=true">发表文章</el-button>
</div>
<!-- 文章表格区域 -->
<el-table :data="artList" border stripe >
<el-table-column label="文章标题" >
<template v-slot="{row}">
<el-link type="primary" @click="getArtDetail(row.id)">{
{
row.title}}</el-link>
</template>
</el-table-column>
<el-table-column label="文章分类" prop="cate_name"></el-table-column>
<!-- 格式化时间 作用域插槽 获取时间并渲染 -->
<el-table-column label="文章时间" ><!-- prop="pub_date" -->
<template v-slot="{row}">
{
{
row.pub_date|dateFormat}}
</template>
</el-table-column>
<el-table-column label="状态" prop="state"></el-table-column>
<el-table-column label="操作" >
<template v-slot="{row}">
<el-button type="danger" size="mini" @click="delArt(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页区域 -->
<!--
current-page 当前页码值
page-sizes 每一显示条数列表
page-size 当前每页显示的条数
layout 分页显示 布局
total 数据总条数
size-change 每页显示的条数发生变化时
current-change页码值改变触发
-->
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="q.pagenum"
:page-sizes="[2, 3, 5, 10]"
:page-size="q.pagesize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</el-card>
<!-- 发表文章弹出框发表文章的 Dialog 对话框 -->
<el-dialog @close="onDialogClosed" title="发表文章" :visible.sync="pubDialogVisible" fullscreen :before-close="handleBeforeClose">
<!-- :before-close="handleClose"点击×触发这个事件(elementui封装的) -->
<!-- 发布文章的对话框 -->
<el-form :model="pubForm" :rules="pubFormRules" ref="pubFormRef" label-width="100px">
<el-form-item label="文章标题" prop="title">
<el-input v-model="pubForm.title" placeholder="请输入标题"></el-input>
</el-form-item>
<el-form-item label="文章分类" prop="cate_id">
<el-select v-model="pubForm.cate_id" placeholder="请选择分类" style="width: 100%;">
<el-option :label="item.cate_name" :value="item.id" v-for="item in cateList" :key="item.id"></el-option>
</el-select>
</el-form-item>
<!-- 富文本编辑器 -->
<el-form-item label="文章内容" prop="content" >
<!-- 使用 v-model 进行双向的数据绑定 -->
<quill-editor v-model="pubForm.content" ></quill-editor>
</el-form-item>
<el-form-item label="文章封面">
<!-- 用来显示封面的图片 -->
<img src="@/assets/images/cover.jpg" alt="" class="cover-img" ref="imgRef" />
<br />
<!-- 文件选择框,默认被隐藏 -->
<input @change="onFIleeChange" type="file" style="display: none;" accept="image/*" ref="iptFile" />
<!-- 选择封面的按钮 -->
<el-button type="text" @click="choseImg">+ 选择封面</el-button>
</el-form-item>
<!-- <el-form-item> 包一个就位置同步了 -->
<el-button type="primary" style="margin-left: 100px;" @click="pubArticle('已发布')">发布</el-button>
<el-button type="info" @click="pubArticle('草稿')">存为草稿</el-button>
<!-- </el-form-item> -->
</el-form>
</el-dialog>
<!-- 文章详情弹出框 -->
<!-- 查看文章详情的对话框 -->
<!-- 查看文章详情的对话框 -->
<el-dialog title="文章预览" :visible.sync="detailVisible" width="80%">
<h1 class="title">{
{
artDetail.title }}</h1>
<div class="info">
<span>作者:{
{
artDetail.nickname || artDetail.username }}</span>
<span>发布时间:{
{
artDetail.pub_date | dateFormat }}</span>
<span>所属分类:{
{
artDetail.cate_name }}</span>
<span>状态:{
{
artDetail.state }}</span>
</div>
<!-- 分割线 -->
<el-divider></el-divider>
<img :src="'http://www.liulongbin.top:3008' + artDetail.cover_img" alt="" />
<div v-html="artDetail.content"></div>
</el-dialog>
</div>
</template>
<script>
import defaultImg from '@/assets/images/cover.jpg'
export default {
name: 'ArtList',
data () {
return {
// 发表文章弹出框
pubDialogVisible: false,
// 查询参数对象
q: {
pagenum: 1,
pagesize: 2,
cate_id: '',
state: ''
},
// 发表表单数据
pubForm: {
title: '',
cate_id: '',
content: '', // 发布文章 草稿 或者 收集数据 发布
state: '',
// 文档初始图片
cover_img: null
},
// 发表表单数据规则
pubFormRules: {
title: [
{
required: true, message: '请输入文章标题', trigger: 'blur' },
{
min: 1, max: 30, message: '文章标题的长度为1-30个字符', trigger: 'blur' }
],
cate_id: [{
required: true, message: '请选择文章标题', trigger: 'blur' }],
content: [{
required: true, message: '请选择文章内容', trigger: 'blur' }]
},
// 文章分类
cateList: [],
// 文章列表
artList: [],
// 文章条数
total: 0,
// 控制文章详情对话框的显示与隐藏
detailVisible: false,
// 文章的详情信息对象
artDetail: {
}
}
},
methods: {
// 对话框dialog
async handleBeforeClose (done) {
const res = await this.$confirm('你确定要关闭发表文章对话框吗?').catch(err => err)
// console.log(res)
if (res === 'cancel') return
done()
},
async initCateList () {
const {
data: res } = await this.$http.get('/my/cate/list')
// console.log(res)
if (res.code === 0) {
this.cateList = res.data
}
},
// 点击button触发input上传框
choseImg () {
this.$refs.iptFile.click()
},
// cheange 触发内置事件 图片 看文档
onFIleeChange (e) {
console.log(e.target.files)
const files = e.target.files
if (files.length === 0) {
// 1. 没有图片
this.pubForm.cover_img = null
// 没有选择
// this.$refs.imgRef.src = '../../../assets/images/cover.jpg' //路径赋值无效后
// 路径赋值无效后 图片模块导入 defaultImg(先导入图片 在赋值 )
this.$refs.imgRef.src = defaultImg // 图片模块
} else {
// 2. 获取图片
this.pubForm.cover_img = files[0]
// // 图片预览 2种解决方案
// 方案一: files[0]图片对象 =>img展示(1.base64)(阅览器 new FileReader())
/* onIptChange (e) {
// console.dir(e.target)
if (e.target.files.length === 0) {
// 没有图片
this.avatar = ''
} else {
// 选择了一张图片 图片对象--> base64 格式的字符串
// e.target.files[0]
// 1. new dileReader() 文件读取器(浏览器自带的)
const reader = new FileReader()
// 2.读取图片对象
reader.readAsDataURL(e.target.files[0])
// 3. 监听onload文件读取完毕事件
reader.onload = () => {
// console.log(reader.result)
// 头像预览赋值
this.avatar = reader.result
}
}
}, */
// 方案二 : 图片地址 (阅览器)
// 实现 图片预览
const url = URL.createObjectURL(files[0])
console.log(url)
// 将url赋值给img的src属性
this.$refs.imgRef.src = url
// this.$refs.imgRef.setAttribute('src', url)
}
},
// 上传文章
pubArticle (state) {
// console.log(state)
this.pubForm.state = state// 接收传值
// 手动校验
this.$refs.pubFormRef.validate(async (valid) => {
// 判断文章输入信息是否完全
if (!valid) return this.$message.warning('输入完全信息')
// 判断图片上传
if (!this.pubForm.cover_img) return this.$message.warning('请上传图片')
// axios 发送请求
// 看文档 数据转换
const fd = new FormData()
// Object.keys() 对象 =>数组 对象中所有的键
// console.log(Object.keys(this.pubForm))//['title', 'cate_id', 'content', 'state', 'cover_img']
/* for (const k in this.pubForm) {
fd.append(k, this.pubForm[k])
} */
// 数据格式转换
Object.keys(this.pubForm).forEach(item => {
fd.append(item, this.pubForm[item])
})
// 看fd的值
/* console.log(fd)// 出不来
fd.forEach((value, key) => {
console.log(key, value)
}) */
const {
data: res } = await this.$http.post('/my/article/add', fd)
console.log(res)
if (res.code !== 0) return this.$message.error(res.message)
this.$message.success(res.message)
// 关闭弹出框
this.pubDialogVisible = false
// 刷新数据
this.initArtList()
})
}, // 获取文章列表
async initArtList () {
const {
data: res } = await this.$http.get('/my/article/list', {
params: this.q })
// console.log(res)
// 失败return this.$message.error('获取文章列表失败')
if (res.code === 0) {
// 成功
this.artList = res.data
// console.log(this.artList)
// 文章的条数
this.total = res.total
}
},
// 关闭弹出框重置表单数据
onDialogClosed () {
this.$refs.pubFormRef.resetFields()
// this.pubForm.cover_img = null // 1. 没有图片
this.$refs.imgRef.src = defaultImg // 2.清除视图
// this.$refs.imgRef.resetFields() 现不了提交清除图片
},
// 每页val条 pagesize 发生变化
handleSizeChange (val) {
// console.log(val)
// 每页条数赋值 只显示第一条
this.q.pagesize = val
this.pagesize = 1
// 重新发请求 刷新数据
this.initArtList()
},
// 当前页val条
handleCurrentChange (val) {
// console.log(val)
// 页码赋值
this.q.pagenum = val
// 重新发请求 刷新数据
this.initArtList()
}, // 筛选
search () {
// 解决点击筛选时获取数据 筛选出现bug 所以重置为1
this.initArtList()
// 页码赋值
this.q.pagenum = 1
},
// 重置筛选
reset () {
// 清空表单
this.$refs.searchRef.resetFields()
// q初始化
this.q = {
pagenum: 1,
pagesize: 2,
cate_id: '',
state: ''
}
// 重新获取数据
this.initArtList()
},
// 根据id获取文章详情
async getArtDetail (id) {
// axios请求
const {
data: res } = await this.$http.get('/my/article/info', {
params: {
id: id } })
// console.log(res)
this.artDetail = res.data
this.detailVisible = true
},
/* try{
}catch{
}
*/
// 根据id删除文章
async delArt (id) {
try {
await this.$confirm('你确定要删除吗?')// promise
// 这里的代码是点击的确定按钮
const {
data: res } = await this.$http.delete('/my/article/info', {
params: {
id } })
// 失败
if (res.code !== 0) return this.$message.error(res.message)
// 成功
this.$message.success(res.message)
// 如果在刷新数据之前,当前页的数据只有 1 条,
// 而且,当前的页码值 > 1,
// 则说明当前页已没有数据可显示,需要让页码值 -1
// 删除优化 当页面中只有一条数据时 展示上一页的数据
if (this.artList.length === 1 && this.q.pagenum > 1) {
this.q.pagenum--
}
// 刷新数据
this.initArtList()
} catch (err) {
// 打印 点击效果
return err
}
}
},
created () {
this.initCateList()
this.initArtList()
}
}
</script>
<style lang="less" scoped>
.title {
font-size: 24px;
text-align: center;
font-weight: normal;
color: #000;
margin: 0 0 10px 0;
}
.info {
font-size: 12px;
span {
margin-right: 20px;
}
}
.el-pagination {
margin-top: 15px;
}
.search-box {
display: flex;
justify-content: space-between;
align-items: flex-start;
.btn-pub {
margin-top: 5px;
}
}
// 设置富文本编辑器的默认最小高度
//vue组件中,不能修改子组件的样式 vue中 css样式穿透 深度选择器(作用域?)
//语法: /deep/ ::v-deep
/deep/ .ql-editor {
min-height: 300px;
}// 设置图片封面的宽高
.cover-img {
width: 400px;
height: 280px;
object-fit: cover;
}.putbtn{
margin-left: 100px;
}
</style>