提交后台前端基础框架
This commit is contained in:
246
Backend/src/components/note/NoteAnnexBox.vue
Executable file
246
Backend/src/components/note/NoteAnnexBox.vue
Executable file
@@ -0,0 +1,246 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>笔记附件列表({{ files.length }})</p>
|
||||
<div class="annex-box lum-scrollbar">
|
||||
<input ref="uploads" type="file" @change="uploadAnnex" />
|
||||
<div class="annex-main">
|
||||
<p v-show="files.length == 0" class="empty-text">
|
||||
暂无附件
|
||||
</p>
|
||||
|
||||
<div v-for="(file, i) in files" :key="file.id" class="file-item">
|
||||
<div class="suffix">{{ file.file_suffix }}</div>
|
||||
<div class="content">
|
||||
<div class="filename">{{ file.original_name }}</div>
|
||||
<div class="filetool">
|
||||
<span>{{ formateTime(file.created_at) }}</span>
|
||||
<span class="size">
|
||||
{{ formateSize(file.file_size) }}
|
||||
</span>
|
||||
<div class="tools">
|
||||
<i class="el-icon-download" @click="downloadAnnex(file.id)" />
|
||||
<i class="el-icon-delete" @click="deleteAnnex(file.id, i)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="annex-footer">
|
||||
<p class="notice-text">最多可支持上传{{ maxNum }}个附件</p>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="el-icon-upload"
|
||||
:disabled="files.length >= maxNum"
|
||||
:loading="loadStatus"
|
||||
@click="$refs.uploads.click()"
|
||||
>{{ loadStatus ? '上传中...' : '上传附件' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
ServeDeleteArticleAnnex,
|
||||
ServeDownloadAnnex as downloadAnnex,
|
||||
ServeUploadArticleAnnex,
|
||||
} from '@/api/article'
|
||||
import { formateSize, formateTime, parseTime } from '@/utils/functions'
|
||||
|
||||
export default {
|
||||
name: 'NoteAnnexBox',
|
||||
props: {
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
files: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadStatus: false,
|
||||
disabled: false,
|
||||
maxNum: 10,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 格式化文件大小
|
||||
formateSize,
|
||||
|
||||
// 格式化时间显示格式
|
||||
formateTime,
|
||||
|
||||
// 下载笔记附件
|
||||
downloadAnnex,
|
||||
|
||||
// 删除笔记附件
|
||||
deleteAnnex(annex_id, index) {
|
||||
ServeDeleteArticleAnnex({
|
||||
annex_id,
|
||||
}).then(({ code }) => {
|
||||
if (code == 200) {
|
||||
this.$delete(this.files, index)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 上传笔记附件文件
|
||||
uploadAnnex(e) {
|
||||
if (e.target.files.length == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
let file = e.target.files[0]
|
||||
if (file.size / (1024 * 1024) > 5) {
|
||||
this.$message('笔记附件不能大于5M!')
|
||||
return false
|
||||
}
|
||||
|
||||
let fileData = new FormData()
|
||||
fileData.append('annex', file)
|
||||
fileData.append('article_id', this.id)
|
||||
|
||||
this.loadStatus = true
|
||||
ServeUploadArticleAnnex(fileData)
|
||||
.then(({ code, data }) => {
|
||||
if (code == 200) {
|
||||
this.files.push({
|
||||
id: data.id,
|
||||
original_name: data.original_name,
|
||||
created_at: parseTime(new Date()),
|
||||
file_size: data.file_size,
|
||||
file_suffix: data.file_suffix,
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadStatus = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/* 文件管理弹出层 */
|
||||
.annex-box {
|
||||
width: 300px;
|
||||
min-height: 50px;
|
||||
max-height: 675px;
|
||||
background-color: white;
|
||||
overflow-y: auto;
|
||||
|
||||
.annex-main {
|
||||
min-height: 30px;
|
||||
border-bottom: 1px solid rgb(239, 233, 233);
|
||||
margin-bottom: 8px;
|
||||
padding: 5px 0;
|
||||
|
||||
.empty-text {
|
||||
color: #969292;
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
height: 50px;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 10px;
|
||||
|
||||
.suffix {
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
background-color: #ffcc80;
|
||||
border-radius: 3px;
|
||||
float: left;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.content {
|
||||
float: left;
|
||||
width: 247px;
|
||||
height: 100%;
|
||||
|
||||
.filename {
|
||||
padding-left: 5px;
|
||||
color: #172b4d;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.filetool {
|
||||
color: #505f79;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
padding-left: 5px;
|
||||
margin-top: 9px;
|
||||
position: relative;
|
||||
|
||||
span {
|
||||
margin: 0 3px;
|
||||
&.size {
|
||||
color: #3a8ee6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tools {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: 5px;
|
||||
width: 55px;
|
||||
height: 24px;
|
||||
text-align: right;
|
||||
line-height: 28px;
|
||||
|
||||
i {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.el-icon-download {
|
||||
color: #66b1ff;
|
||||
}
|
||||
|
||||
.el-icon-delete {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.annex-footer {
|
||||
.notice-text {
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
text-align: left;
|
||||
float: left;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
206
Backend/src/components/note/NoteAnnexRecycle.vue
Executable file
206
Backend/src/components/note/NoteAnnexRecycle.vue
Executable file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<div class="lum-dialog-mask">
|
||||
<el-container
|
||||
class="lum-dialog-box animated bounceInDown"
|
||||
v-outside="close"
|
||||
>
|
||||
<el-header height="60px" class="header no-select">
|
||||
<p>近 30 天删除的附件({{ tableData.length }})</p>
|
||||
<p class="tools">
|
||||
<i class="el-icon-close" @click="close" />
|
||||
</p>
|
||||
</el-header>
|
||||
<el-main class="main lum-scrollbar">
|
||||
<el-table :data="tableData" size="mini">
|
||||
<div slot="empty">暂无相关数据</div>
|
||||
<el-table-column
|
||||
prop="original_name"
|
||||
label="附件名称"
|
||||
width="180"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" @click="downloadAnnex(scope.row.id)">{{
|
||||
scope.row.original_name
|
||||
}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="title"
|
||||
label="所属笔记"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="day"
|
||||
label="剩余天数"
|
||||
align="center"
|
||||
width="80"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
fixed="right"
|
||||
label="操作"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="recoverAnnex(scope.row)"
|
||||
>恢复</el-button
|
||||
>
|
||||
<el-popover
|
||||
placement="top"
|
||||
@hide="lock(false)"
|
||||
@show="lock(true)"
|
||||
:ref="`popover-${scope.$index}`"
|
||||
>
|
||||
<p style="margin-bottom: 10px">
|
||||
【{{
|
||||
scope.row.original_name
|
||||
}}】附件您确定要永久删除吗?<br />
|
||||
</p>
|
||||
<div style="text-align: right; margin: 0">
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="
|
||||
scope._self.$refs[`popover-${scope.$index}`].doClose()
|
||||
"
|
||||
>
|
||||
取消</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
@click="deleteAnnex(scope.row, scope.$index)"
|
||||
>确定</el-button
|
||||
>
|
||||
</div>
|
||||
<el-button
|
||||
slot="reference"
|
||||
type="text"
|
||||
size="small"
|
||||
style="color: #ec5252; margin-left: 5px"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-main>
|
||||
<el-footer class="footer" height="30px">
|
||||
<p class="footer-tip">移动至回收站的附件和笔记,将在 30 天后自动清除</p>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import { Table, TableColumn } from 'element-ui'
|
||||
Vue.use(Table)
|
||||
Vue.use(TableColumn)
|
||||
|
||||
import {
|
||||
ServeGetRecoverAnnexList,
|
||||
ServeRecoverArticleAnnex,
|
||||
ServeDownloadAnnex,
|
||||
ServeForeverDeleteAnnex,
|
||||
} from '@/api/article'
|
||||
|
||||
export default {
|
||||
name: 'NoteAnnexRecycle',
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
closeLock: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadList()
|
||||
},
|
||||
methods: {
|
||||
loadList() {
|
||||
ServeGetRecoverAnnexList().then(res => {
|
||||
if (res.code == 200) {
|
||||
this.tableData = res.data.rows
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 关闭当前窗口
|
||||
close() {
|
||||
if (!this.closeLock) this.$emit('close')
|
||||
},
|
||||
|
||||
// 给遮罩层加锁
|
||||
lock(value) {
|
||||
this.closeLock = value
|
||||
},
|
||||
|
||||
// 恢复附件
|
||||
recoverAnnex(data, index) {
|
||||
ServeRecoverArticleAnnex({
|
||||
annex_id: data.id,
|
||||
}).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.tableData.splice(index, 1)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
//永久删除附件
|
||||
deleteAnnex(data, index) {
|
||||
ServeForeverDeleteAnnex({
|
||||
annex_id: data.id,
|
||||
})
|
||||
.then(res => {
|
||||
this.$refs[`popover-${index}`].doClose()
|
||||
this.lock(false)
|
||||
if (res.code == 200) {
|
||||
this.tableData.splice(index, 1)
|
||||
} else {
|
||||
this.$notify({
|
||||
message: '附件删除失败...',
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.$refs[`popover-${index}`].doClose()
|
||||
this.lock(false)
|
||||
})
|
||||
},
|
||||
|
||||
//下载笔记附件
|
||||
downloadAnnex: ServeDownloadAnnex,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.lum-dialog-box {
|
||||
width: 700px;
|
||||
height: 80%;
|
||||
max-width: 700px;
|
||||
|
||||
.main {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
.footer-tip {
|
||||
color: #a7afbc;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .tab-header-row .cell {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgb(172, 167, 167);
|
||||
}
|
||||
</style>
|
||||
220
Backend/src/components/note/NoteTagBox.vue
Executable file
220
Backend/src/components/note/NoteTagBox.vue
Executable file
@@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<div class="tag-manage">
|
||||
<div class="title">
|
||||
<span>已选择</span>
|
||||
</div>
|
||||
|
||||
<div class="tag-groups">
|
||||
<p
|
||||
v-for="(tag, i) in tags"
|
||||
v-show="tag.isSelectd"
|
||||
:key="i"
|
||||
class="tag-item"
|
||||
>
|
||||
<span>{{ tag.name }}</span>
|
||||
<i class="el-icon-close" @click="active(tag.id, tag.isSelectd)" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="title">
|
||||
<span>标签栏</span>
|
||||
</div>
|
||||
|
||||
<div class="tag-groups">
|
||||
<p
|
||||
v-for="(tag, i) in tags"
|
||||
:key="i"
|
||||
class="tag-item"
|
||||
:class="{ active: tag.isSelectd }"
|
||||
@click="active(tag.id, tag.isSelectd)"
|
||||
>
|
||||
<span>{{ tag.name }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<el-button
|
||||
v-show="!isInput"
|
||||
type="primary"
|
||||
class="addbtn"
|
||||
@click="isInput = !isInput"
|
||||
>+ 添加标签
|
||||
</el-button>
|
||||
|
||||
<div class="tag-input" v-show="isInput">
|
||||
<input
|
||||
ref="editTaginput"
|
||||
type="text"
|
||||
placeholder="回车保存..."
|
||||
v-model.trim="tagText"
|
||||
@keyup.enter="save"
|
||||
/>
|
||||
|
||||
<el-button type="primary" size="small" @click="isInput = false"
|
||||
>取消编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ServeUpdateArticleTag, ServeEditArticleTag } from '@/api/article'
|
||||
export default {
|
||||
name: 'NoteTagBox',
|
||||
props: {
|
||||
note_id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
tag_ids: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ids: [],
|
||||
isInput: false,
|
||||
tagText: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.ids = this.tag_ids.map(tag => tag.id)
|
||||
},
|
||||
computed: {
|
||||
tags() {
|
||||
let tags = []
|
||||
this.$store.state.note.tags.forEach(tag => {
|
||||
tags.push({
|
||||
id: tag.id,
|
||||
name: tag.tag_name,
|
||||
isSelectd: this.ids.includes(tag.id),
|
||||
})
|
||||
})
|
||||
|
||||
return tags
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 设置笔记标签事件
|
||||
active(tag_id, isSelect) {
|
||||
if (isSelect) {
|
||||
this.ids.forEach((item, index) => {
|
||||
if (item == tag_id) {
|
||||
this.ids.splice(index, 1)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.ids.push(tag_id)
|
||||
}
|
||||
|
||||
ServeUpdateArticleTag({
|
||||
article_id: this.note_id,
|
||||
tags: this.getSelectTags(),
|
||||
})
|
||||
},
|
||||
|
||||
// 获取选中的标签ids
|
||||
getSelectTags() {
|
||||
let ids = []
|
||||
for (let item of this.tags) {
|
||||
if (item.isSelectd) ids.push(item.id)
|
||||
}
|
||||
|
||||
return ids
|
||||
},
|
||||
|
||||
// 保存标签事件
|
||||
save() {
|
||||
let tag_name = this.tagText
|
||||
ServeEditArticleTag({
|
||||
tag_id: 0,
|
||||
tag_name,
|
||||
}).then(({ code, data }) => {
|
||||
if (code !== 200) return false
|
||||
|
||||
this.$store.commit('PUSH_NOTE_TAG', {
|
||||
id: data.id,
|
||||
tag_name: tag_name,
|
||||
count: 0,
|
||||
})
|
||||
|
||||
this.tagText = ''
|
||||
this.isInput = false
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.tag-manage {
|
||||
.title {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
color: #ccc;
|
||||
border-bottom: 1px solid #f0e9e9;
|
||||
padding-bottom: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tag-groups {
|
||||
padding: 8px 8px 8px 0;
|
||||
cursor: pointer;
|
||||
|
||||
.tag-item {
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
margin: 0 3px 5px 0;
|
||||
color: #409eff;
|
||||
background: rgba(64, 158, 255, 0.1);
|
||||
border-radius: 1px;
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: #70b5fb;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addbtn {
|
||||
height: 33px;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
line-height: 9px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tag-input {
|
||||
margin-top: 20px;
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
input {
|
||||
width: 200px;
|
||||
height: 30px;
|
||||
border: 1px solid #66b1ff;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
margin-right: 5px;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user