diff --git a/src/api/tag.js b/src/api/tag.js new file mode 100644 index 0000000..5ea073e --- /dev/null +++ b/src/api/tag.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' +const qs = require('qs') + +export default { + tagList(params) { + return request({ + url: 'tag/list', + method: 'get', + params + }) + }, + tagInfo(params) { + return request({ + url: 'tag/info', + method: 'get', + params + }) + }, + add(data) { + return request({ + url: 'tag/add', + method: 'post', + data + }) + }, + update(data) { + return request({ + url: 'tag/update', + method: 'put', + data + }) + }, + delete(params) { + return request({ + url: 'tag/delete', + method: 'delete', + params, + paramsSerializer: function(params) { + return qs.stringify(params, { arrayFormat: 'repeat' }) + } + }) + } +} + diff --git a/src/router/index.js b/src/router/index.js index 410f7a8..6788161 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -249,6 +249,12 @@ export const constantRoutes = [ name: 'manager-categories', component: () => import('@/views/setting/articles/categoryManager'), meta: { title: '分类管理', icon: 'fenlei' } + }, + { + path: 'manager-tags', + name: 'manager-tags', + component: () => import('@/views/setting/articles/tagManager'), + meta: { title: '标签管理', icon: 'biaoqian' } } ] }, diff --git a/src/styles/setting.scss b/src/styles/setting.scss index 7312cc2..7822023 100644 --- a/src/styles/setting.scss +++ b/src/styles/setting.scss @@ -103,6 +103,14 @@ a:hover { margin-top: 10px; max-width: 300px; } +.el-form-item__content { + .instruction { + font-size: 12px; + line-height: 1; + padding-top: 4px; + position: absolute; + } +} .instruction { color: #9a9a9a; font-size: .8125rem; diff --git a/src/views/markdown/index.vue b/src/views/markdown/index.vue index 81e1772..cb4cef4 100644 --- a/src/views/markdown/index.vue +++ b/src/views/markdown/index.vue @@ -19,13 +19,13 @@ -
+
保存草稿 - {{ editStatus?'发布文章':'更新文章' }} + {{ editStatus?'更新文章':'发布文章' }}

文章封面:

@@ -55,6 +55,38 @@ clearable @change="selectCategory" > +

标签:

+ + {{tag}} + + + + + + + + + + + + +
该标签已存在
+ + New Tag
@@ -86,6 +118,7 @@ import Vditor from 'vditor' import "vditor/src/assets/scss/index.scss" import categoryApi from "@/api/category"; + import tagApi from "@/api/tag"; let toolbar = [ 'emoji', @@ -158,12 +191,21 @@ categories: [], categoryIdsList: [], draft: false, - articleLink: `${window.origin}/artiles/` + articleLink: `${window.origin}/artiles/`, + tags: [], + dynamicTags: [], + inputVisible: false, + inputValue: '', + inputValueExist: false, + inputNewTagClass: 'input-new-tag', + inputErrorClass: 'input-error', + vditorLoading: true, } }, mounted() { this.getMarkdown() this.categoryTree() + this.getTags() const that = this window.onresize = function temp() { that.reHeight() @@ -186,6 +228,20 @@ this.getMarkdown(true) this.categoryTree() }, + querySearch(queryString, cb) { + let tags = this.tags + let results = queryString ? tags.filter((tag) => { + return (tag.name.toLowerCase().indexOf(queryString.toLowerCase()) === 0) + }) : tags + // 调用 callback 返回建议列表的数据 + results = results.map(res => { + return {value: res.name} + }) + cb(results) + }, + handleSelect(item){ + this.handleInputConfirm() + }, valueHasChanged(){ this.$emit('update:hasChange', true) }, @@ -195,24 +251,29 @@ markdownApi.getMarkdown({ mark: this.$route.query.id }).then((res) => { - this.file = res.data - this.content = this.file.contentText - this.filename = this.file.name.split('.md')[0] - this.storageLocation = res.data.path - if(this.categories && this.categories.length > 0){ - this.findCategoryIds(null, this.categories, this.file.categoryIds) - } // 初始化编辑器 if(isReload){ this.contentEditor.setValue(res.data.contentText) } else { this.vditorInit(res.data.contentText) } + this.file = res.data + this.content = this.file.contentText + this.filename = this.file.name.split('.md')[0] + this.storageLocation = res.data.path + // 加载分类 + if(this.categories && this.categories.length > 0){ + this.findCategoryIds(null, this.categories, this.file.categoryIds) + } + // 加载标签 + if(this.tags && this.tags.length > 0) { + this.loadDynamicTags() + } }).then(() => { const that = this setTimeout(function (){ that.$emit('update:hasChange', false) - }, 100) + }, 200) }) } else { if(isReload){ @@ -223,13 +284,46 @@ } }, categoryTree() { - categoryApi.categoryTree({userId: this.$store.state.user.userId}).then(res => { + categoryApi.categoryTree().then(res => { this.categories = res.data if(this.file.categoryIds && this.file.categoryIds.length > 0){ this.findCategoryIds(null, this.categories, this.file.categoryIds) } }) }, + getTags() { + tagApi.tagList().then(res => { + this.tags = res.data + if(this.file.tagIds && this.file.tagIds.length > 0) { + this.loadDynamicTags() + } + }) + }, + loadDynamicTags() { + this.dynamicTags = this.tags.filter(tag => this.file.tagIds.includes(tag.id)).map(tag => tag.name) + }, + handleClose(tag) { + this.dynamicTags.splice(this.dynamicTags.indexOf(tag), 1) + }, + showInput() { + this.inputVisible = true + this.$nextTick(_ => { + this.$refs.saveTagInput.$refs.input.focus() + }); + }, + handleInputConfirm() { + let inputValue = this.inputValue + if (inputValue) { + if (this.dynamicTags.includes(inputValue)){ + this.inputValueExist = true + return + } + this.dynamicTags.push(inputValue) + } + this.inputValueExist = false + this.inputVisible = false + this.inputValue = '' + }, findCategoryIds(parentCategory, categories, categoryIds) { if(!categoryIds){ return @@ -269,6 +363,7 @@ }, after: () => { this.contentEditor.setValue(content) + console.log('sdf') }, input: (val) => { this.valueHasChanged() @@ -374,6 +469,7 @@ isDraft: this.draft, slug: this.file.slug ? this.file.slug : this.filename, categoryIds: categoryIds, + tagNames: this.dynamicTags, currentDirectory: this.storageLocation, contentText: this.contentEditor.getValue() }).then(() => { @@ -469,7 +565,37 @@ } } } - +/deep/ .el-tag { + margin-left: 8px; + margin-top: 8px; +} +/deep/ .button-new-tag { + margin-left: 8px; + margin-top: 8px; + height: 32px; + line-height: 30px; + padding-top: 0; + padding-bottom: 0; +} +/deep/ .input-new-tag { + margin-left: 8px; + margin-top: 8px; + max-width: 94px; + vertical-align: bottom; +} +/deep/ .input-error { + input { + border-color: #f56c6c; + } +} +.instruction-error { + color: #F56C6C; + font-size: 12px; + line-height: 1; + padding-top: 4px; + position: absolute; + margin-left: 8px; +} @media (min-width: 1200px) { .article-editor { max-width: 1200px; diff --git a/src/views/setting/articles/articleManager.vue b/src/views/setting/articles/articleManager.vue index 4e49cc1..905f3f6 100644 --- a/src/views/setting/articles/articleManager.vue +++ b/src/views/setting/articles/articleManager.vue @@ -286,7 +286,7 @@ export default { } }, categoryTree() { - categoryApi.categoryTree({userId: this.$store.state.user.userId}).then(res => { + categoryApi.categoryTree().then(res => { this.categories = res.data if(this.$route.query.categoryIds){ this.query.categoryIds = this.$route.query.categoryIds diff --git a/src/views/setting/articles/categoryManager.vue b/src/views/setting/articles/categoryManager.vue index ccb5c66..189265a 100644 --- a/src/views/setting/articles/categoryManager.vue +++ b/src/views/setting/articles/categoryManager.vue @@ -6,7 +6,8 @@ - + +
分类缩略名用于创建友好的链接形式.
- + +
此文字用于描述分类, 在该分类首页中它会被显示.
+
+ + +
在这里填入图片的URL地址, 以在分类页面显示一个背景大图.
{ this.categoryList = res.data }) }, categoryTree() { - categoryApi.categoryTree({userId: this.$store.state.user.userId}).then(res => { + categoryApi.categoryTree().then(res => { this.categories = res.data }) }, @@ -253,7 +259,6 @@ export default { categoryApi.delete({categoryIds: categoryIds}).then(() => { this.onDeleted() }).catch(() => { - console.log('sdf') this.onError() }) }) @@ -268,9 +273,6 @@ export default { }, // 添加分类 addCategory(data) { - if (!this.form.hasOwnProperty('userId')) { - data.append('userId', this.$store.state.user.userId) - } categoryApi.add(data).then(() => { this.onSuccess() }).catch(() => { diff --git a/src/views/setting/articles/tagManager.vue b/src/views/setting/articles/tagManager.vue new file mode 100644 index 0000000..da6d7be --- /dev/null +++ b/src/views/setting/articles/tagManager.vue @@ -0,0 +1,244 @@ + + + + + +