parent
610a1547e1
commit
643cd69154
@ -0,0 +1,81 @@
|
||||
syntax = "proto3";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
// import "validate/validate.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
package api.v1;
|
||||
|
||||
option go_package = "moredoc/api/v1;v1";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "api.v1";
|
||||
|
||||
message Article {
|
||||
int64 id = 1;
|
||||
string identifier = 2;
|
||||
string author = 3;
|
||||
int64 view_count = 4;
|
||||
string title = 5;
|
||||
string keywords = 6;
|
||||
string description = 7;
|
||||
string content = 8;
|
||||
google.protobuf.Timestamp created_at = 9 [ (gogoproto.stdtime) = true ];
|
||||
google.protobuf.Timestamp updated_at = 10 [ (gogoproto.stdtime) = true ];
|
||||
}
|
||||
|
||||
message DeleteArticleRequest { repeated int64 id = 1; }
|
||||
|
||||
// 根据ID或者文章标识获取文章
|
||||
message GetArticleRequest {
|
||||
int64 id = 1;
|
||||
string identifier = 2;
|
||||
}
|
||||
|
||||
message ListArticleRequest {
|
||||
int64 page = 1;
|
||||
int64 size = 2;
|
||||
string wd = 3;
|
||||
repeated string field = 4;
|
||||
string order = 5;
|
||||
}
|
||||
|
||||
message ListArticleReply {
|
||||
int64 total = 1;
|
||||
repeated Article article = 2;
|
||||
}
|
||||
|
||||
service ArticleAPI {
|
||||
rpc CreateArticle(Article) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {
|
||||
post : '/api/v1/article',
|
||||
body : '*',
|
||||
};
|
||||
}
|
||||
|
||||
rpc UpdateArticle(Article) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {
|
||||
put : '/api/v1/article',
|
||||
body : '*',
|
||||
};
|
||||
}
|
||||
|
||||
rpc DeleteArticle(DeleteArticleRequest) returns (google.protobuf.Empty) {
|
||||
option (google.api.http) = {
|
||||
delete : '/api/v1/article',
|
||||
};
|
||||
}
|
||||
|
||||
rpc GetArticle(GetArticleRequest) returns (Article) {
|
||||
option (google.api.http) = {
|
||||
get : '/api/v1/article',
|
||||
};
|
||||
}
|
||||
|
||||
rpc ListArticle(ListArticleRequest) returns (ListArticleReply) {
|
||||
option (google.api.http) = {
|
||||
get : '/api/v1/article/list',
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import service from '~/utils/request'
|
||||
|
||||
export const createArticle = (data) => {
|
||||
return service({
|
||||
url: '/api/v1/article',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export const updateArticle = (data) => {
|
||||
return service({
|
||||
url: '/api/v1/article',
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteArticle = (params) => {
|
||||
return service({
|
||||
url: '/api/v1/article',
|
||||
method: 'delete',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
export const getArticle = (params) => {
|
||||
return service({
|
||||
url: '/api/v1/article',
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
export const listArticle = (params) => {
|
||||
return service({
|
||||
url: '/api/v1/article/list',
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,212 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card shadow="never" class="search-card">
|
||||
<FormSearch
|
||||
:fields="searchFormFields"
|
||||
:loading="loading"
|
||||
:show-create="true"
|
||||
:show-delete="true"
|
||||
:disabled-delete="selectedRow.length === 0"
|
||||
@onSearch="onSearch"
|
||||
@onCreate="onCreate"
|
||||
@onDelete="batchDelete"
|
||||
/>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mgt-20px">
|
||||
<TableList
|
||||
:loading="loading"
|
||||
:table-data="articles"
|
||||
:fields="tableListFields"
|
||||
:show-actions="true"
|
||||
:show-view="false"
|
||||
:show-edit="true"
|
||||
:show-delete="true"
|
||||
:show-select="true"
|
||||
@selectRow="selectRow"
|
||||
@editRow="editRow"
|
||||
@deleteRow="deleteRow"
|
||||
/>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mgt-20px">
|
||||
<div class="text-right">
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="search.page"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="search.size"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handlePageChange"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog
|
||||
:title="article.id ? '编辑文章' : '新增文章'"
|
||||
:visible.sync="formArticleVisible"
|
||||
>
|
||||
<FormArticle
|
||||
ref="articleForm"
|
||||
:init-article="article"
|
||||
@success="formSuccess"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listArticle, deleteArticle, getArticle } from '~/api/article'
|
||||
import TableList from '~/components/TableList.vue'
|
||||
import FormSearch from '~/components/FormSearch.vue'
|
||||
import FormArticle from '~/components/FormArticle.vue'
|
||||
export default {
|
||||
components: { TableList, FormSearch, FormArticle },
|
||||
layout: 'admin',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
formArticleVisible: false,
|
||||
search: {
|
||||
wd: '',
|
||||
page: 1,
|
||||
status: [],
|
||||
size: 10,
|
||||
},
|
||||
articles: [],
|
||||
total: 0,
|
||||
searchFormFields: [],
|
||||
tableListFields: [],
|
||||
selectedRow: [],
|
||||
article: {
|
||||
id: 0,
|
||||
title: '',
|
||||
identifier: '',
|
||||
keywords: '',
|
||||
description: '',
|
||||
content: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.initSearchForm()
|
||||
this.initTableListFields()
|
||||
await this.listArticle()
|
||||
},
|
||||
methods: {
|
||||
async listArticle() {
|
||||
this.loading = true
|
||||
const res = await listArticle(this.search)
|
||||
if (res.status === 200) {
|
||||
this.articles = res.data.article
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.data.message)
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.search.size = val
|
||||
this.listArticle()
|
||||
},
|
||||
handlePageChange(val) {
|
||||
this.search.page = val
|
||||
this.listArticle()
|
||||
},
|
||||
onSearch(search) {
|
||||
this.search = { ...this.search, page: 1, ...search }
|
||||
this.listArticle()
|
||||
},
|
||||
onCreate() {
|
||||
this.article = { id: 0 }
|
||||
this.formArticleVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.articleForm.reset()
|
||||
})
|
||||
},
|
||||
async editRow(row) {
|
||||
const res = await getArticle({ id: row.id })
|
||||
if (res.status === 200) {
|
||||
this.article = res.data
|
||||
this.formArticleVisible = true
|
||||
} else {
|
||||
this.$message.error(res.data.message)
|
||||
}
|
||||
},
|
||||
formSuccess() {
|
||||
this.formArticleVisible = false
|
||||
this.listArticle()
|
||||
},
|
||||
batchDelete() {
|
||||
this.$confirm(
|
||||
`您确定要删除选中的【${this.selectedRow.length}条】文章吗?删除之后不可恢复!`,
|
||||
'温馨提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(async () => {
|
||||
const ids = this.selectedRow.map((item) => item.id)
|
||||
const res = await deleteArticle({ id: ids })
|
||||
if (res.status === 200) {
|
||||
this.$message.success('删除成功')
|
||||
this.listArticle()
|
||||
} else {
|
||||
this.$message.error(res.data.message)
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
deleteRow(row) {
|
||||
this.$confirm(
|
||||
`您确定要删除文章【${row.title}】吗?删除之后不可恢复!`,
|
||||
'温馨提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(async () => {
|
||||
const res = await deleteArticle({ id: row.id })
|
||||
if (res.status === 200) {
|
||||
this.$message.success('删除成功')
|
||||
this.listArticle()
|
||||
} else {
|
||||
this.$message.error(res.data.message)
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
selectRow(rows) {
|
||||
this.selectedRow = rows
|
||||
},
|
||||
initSearchForm() {
|
||||
this.searchFormFields = [
|
||||
{
|
||||
type: 'text',
|
||||
label: '关键字',
|
||||
name: 'wd',
|
||||
placeholder: '请输入关键字',
|
||||
},
|
||||
]
|
||||
},
|
||||
initTableListFields() {
|
||||
this.tableListFields = [
|
||||
{ prop: 'id', label: 'ID', width: 80, type: 'number', fixed: 'left' },
|
||||
{ prop: 'title', label: '标题', minWidth: 150, fixed: 'left' },
|
||||
{ prop: 'identifier', label: '标识', width: 200 },
|
||||
{ prop: 'view_count', label: '浏览', width: 80, type: 'number' },
|
||||
{ prop: 'keywords', label: '关键字', width: 200 },
|
||||
{ prop: 'description', label: '摘要', minWidth: 200 },
|
||||
{ prop: 'created_at', label: '创建时间', width: 160, type: 'datetime' },
|
||||
{ prop: 'updated_at', label: '更新时间', width: 160, type: 'datetime' },
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
Loading…
Reference in new issue