feat(文章): 文章模块

dev
zhangjiabao 7 months ago
parent f3f6d19e6e
commit 175fc05608

@ -0,0 +1,81 @@
/*
* @Description:
* @version: v1
* @Author: zhangjiabao
* @Date: 2024-02-24 23:38:16
*/
import request from "@/utils/request";
export default {
getList(data, page, size) {
return request({
url: `/luoo-music/cms/article/search/${page}/${size}`,
method: "post",
data: data,
});
},
getDetail(id) {
return request({
url: `/luoo-music/cms/article/${id}`,
method: "get",
});
},
/**
* 删除
* @param {*} id
* @returns
*/
delete(id) {
return request({
url: `/luoo-music/cms/article/${id}`,
method: "delete",
});
},
/**
* 发布
* @param {*} id
* @returns
*/
publish(id, type, time) {
return request({
url: `/luoo-music/cms/article/publish/${id}`,
method: "put",
data: {
isScheduled: type,
pubTime: time,
},
});
},
/**
* 更新状态
* @param {*} id
* @param {*} state
* @returns
*/
updateStatus(id, state) {
return request({
url: `/luoo-music/cms/article/update/state/${id}`,
method: "put",
data: state,
});
},
/**
* 更新
* @param {*} id
* @param {*} data
* @returns
*/
update(id, data) {
return request({
url: `/luoo-music/cms/article/${id}`,
method: "put",
data: data,
});
},
add(data) {
return request({
url: `/luoo-music/cms/article/add`,
method: "post",
data: data,
});
},
};

@ -2,8 +2,8 @@
* @Author: zhangjiabao
* @Date: 2024-01-25 11:05:17
* @LastEditors: zhangjiabao
* @LastEditTime: 2024-02-05 23:08:57
* @FilePath: \luoo_manage_fe\src\router\index.js
* @LastEditTime: 2024-02-24 20:59:42
* @FilePath: /luoo_manage_fe/src/router/index.js
*/
import Vue from "vue";
import Router from "vue-router";
@ -158,9 +158,23 @@ export const constantRouterMap = [
{
path: "article",
name: "ContentArticle",
component: () => import("@/views/common/unfinished"),
component: () => import("@/views/article/index"),
meta: { title: "文章列表" }
},
{
path: "articleModify",
name: "ContentArticle",
hidden: true,
component: () => import("@/views/article/articleModifyPage"),
meta: { title: "文章修改" }
},
{
path: "articleDetail",
name: "ContentArticle",
hidden: true,
component: () => import("@/views/article/articleDetailPage"),
meta: { title: "文章详情" }
},
{
path: "comment",
name: "Comment",

@ -0,0 +1,285 @@
<!--
* @Description:
* @version: v1
* @Author: zhangjiabao
* @Date: 2024-01-20 02:11:55
-->
<template>
<div>
<el-form
style="width: 800px;margin: 0 auto;"
label-position="left"
label-suffix=":"
label-width="100px"
>
<el-form-item prop="title" label="文章标题">
<span>VOL.{{ data.articleNo }} {{ data.title }}</span>
</el-form-item>
<el-form-item prop="tag" label="文章标签">
<span>{{ getTagNameById(data.tag) }}</span>
</el-form-item>
<el-form-item prop="userId" label="文章作者">
<span>{{ getAuthorNameById(data.userId) }}</span>
</el-form-item>
<el-form-item prop="image" label="文章封面">
<img
style="width: 170px;height: 120px;"
v-if="data.image"
:src="data.image"
/>
</el-form-item>
<el-form-item label="文章音乐"></el-form-item>
</el-form>
<el-table
size="mini"
:data="data.songs"
style="width: calc(50% + 400px); margin: 0 0 auto auto;"
stripe
class="draggable-table"
border
fit
element-loading-text="加载中"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column prop="image" label="专辑封面" width="100px">
<template slot-scope="slot">
<img
style="width: 50;height: 50px;"
v-if="slot.row.image"
:src="slot.row.image"
/>
</template>
</el-table-column>
<el-table-column prop="name" label="歌曲名称"></el-table-column>
<el-table-column prop="artist" label="歌手/乐队"></el-table-column>
<el-table-column prop="album" label="所属专辑"></el-table-column>
<el-table-column prop="duration" label="时长" width="300px">
<template slot-scope="scope">
<audio
style="width:270px"
preload="none"
:src="scope.row.url"
controls
></audio>
</template>
</el-table-column>
</el-table>
<el-form
style="width: calc(50% + 400px); margin: 15px 0 auto auto;"
label-position="left"
label-width="100px"
label-suffix=":"
>
<el-form-item label="文案摘要">
<span>{{ data.summary }}</span>
</el-form-item>
<el-form-item label="文章文案">
<div v-html="data.content"></div>
</el-form-item>
<el-form-item label="评论记录">
<div v-loading="commentLoading">
<span v-if="commentPage.total === 0" style="margin-left:40%;"
>无数据</span
>
<el-card
style="margin-top: 5px;background-color: rgba(249, 249, 249, 0.4980392156862745);padding: 5px 10px 5px 10px"
shadow="hover"
v-for="c in commentList"
:key="c['_id']"
>
<span
style="font-family:'微软雅黑', sans-serif;font-weight:400;font-size:14px;color:#666666;"
>{{ c["nickName"] }}</span
>
<span
style="font-family:'微软雅黑', sans-serif;font-weight:400;font-size:12px;color:#999999;"
>{{ c["publishTime"] }}</span
>
<span style="float: right;"
><i class="el-icon-thumb"></i
><span>{{ c["thumbupCount"] }}</span></span
>
<br />
<span class="comment">{{ c["content"] }}</span>
</el-card>
<!-- 分页 -->
<el-pagination
class="pagination-right"
background
layout="total, prev, pager, next, sizes"
:current-page.sync="commentPage.current"
:page-size="commentPage.size"
:total="commentPage.total"
@size-change="handlePageSizeChange"
@current-change="handlePageCurrentChange"
></el-pagination>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script>
import commentApi from "@/api/comment";
export default {
name: "ArticleDetail",
props: {
data: {
type: Object,
default: {
id: null,
name: null,
tag: [],
number: null,
userId: null,
coverPhoto: null,
songs: []
}
},
remoteTagList: {
type: Array,
default: []
},
publisher: {
type: Array,
default: []
}
},
data() {
return {
commentList: [],
commentLoading: true,
commentPage: {
current: 1,
size: 10,
total: 0
}
};
},
filters: {},
created() {
this.fetchComment();
},
components: {},
mounted() {},
methods: {
fetchComment() {
this.commentLoading = true;
commentApi
.getListById(
this.data.id,
this.commentPage.current,
this.commentPage.size
)
.then(res => {
if (res.code === 200) {
this.commentList = res.data.rows;
this.commentPage.total = res.data.total;
this.commentLoading = false;
} else {
this.$message(res.message);
this.commentList = [];
this.commentPage.total = 0;
this.commentLoading = false;
}
});
},
handlePageSizeChange() {
this.fetchComment();
},
handlePageCurrentChange() {
this.fetchComment();
},
// ************************* *************************
getAuthorNameById(userId) {
for (let index in this.publisher) {
let item = this.publisher[index];
if (item.id === userId) {
return item.name;
}
return "";
}
},
getTagNameById(tagIdList) {
let tagList = [];
for (let tagIdIndex in tagIdList) {
let tagId = tagIdList[tagIdIndex];
for (let index in this.remoteTagList) {
let item = this.remoteTagList[index];
if (item.value === tagId) {
tagList.push(item.label);
}
if (!item.children) {
continue;
}
for (let subIndex in item.children) {
let subItem = item.children[subIndex];
if (subItem.value === tagId) {
tagList.push(subItem.label);
}
}
}
}
let name = "";
for (let index in tagList) {
name = name + "#" + tagList[index] + " ";
}
return name;
}
}
};
</script>
<style scoped>
.comment {
font-family: "微软雅黑", sans-serif;
font-weight: 400;
font-style: normal;
color: #999999;
text-align: left;
line-height: 22px;
}
.pagination-right {
text-align: right;
margin-top: 10px;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both;
}
.card-header {
height: 23px;
}
.app-container {
font-family: "Microsoft YaHei Bold", "Microsoft YaHei Regular",
"Microsoft YaHei", sans-serif;
}
.card-header-text {
font-family: "Microsoft YaHei Bold", "Microsoft YaHei Regular",
"Microsoft YaHei", sans-serif;
font-weight: 700;
font-style: normal;
font-size: 16px;
text-align: left;
line-height: 20px;
letter-spacing: normal;
color: #333333;
}
.box-card {
min-height: 300px;
height: calc(100vh - 140px);
border-radius: 10px;
.el-card__header {
background-color: rgba(249, 249, 249, 1);
}
.el-card__body {
overflow: auto;
height: 100%;
}
}
</style>

@ -0,0 +1,171 @@
<!--
* @Author: zhangjiabao
* @Date: 2024-02-03 00:03:50
* @LastEditors: zhangjiabao
* @LastEditTime: 2024-02-25 00:36:28
* @FilePath: /luoo_manage_fe/src/views/article/articleDetailPage.vue
-->
<template>
<div class="app-container">
<el-card class="box-card" style="overflow:auto">
<div slot="header" class="card-header clearfix">
<span class="card-header-text">文章详情</span>
</div>
<ArticleDetail
style="margin-left: -300px"
:data="data"
:remoteTagList="remoteData.tags"
:publisher="remoteData.publisher"
></ArticleDetail>
</el-card>
</div>
</template>
<script>
import ArticleDetail from "./articleDetail.vue";
import articleApi from "@/api/article";
import tagApi from "@/api/tag";
export default {
name: "ArticleDetailPage",
components: { ArticleDetail },
props: {},
data() {
return {
remoteData: { tags: [], publisher: [] },
data: {
id: null,
title: null,
tag: [],
articleNo: null,
userId: null,
image: null,
songs: [],
size: 0,
content: "",
duration: 0
}
};
},
computed: {},
watch: {},
created() {
this.fetchDetail(this.$route.query.id);
this.fetchSuperFilterTags();
this.fetchCreaterList();
},
mounted() {},
methods: {
fetchDetail(id) {
articleApi.getDetail(id).then(res => {
if (res.code === 200) {
this.data = JSON.parse(JSON.stringify(res.data));
this.type = 1;
this.contentShow = true;
} else {
this.type = 0;
this.$message.error(res.message);
}
});
},
/**
* 创建人下拉框
*/
fetchCreaterList() {
tagApi.createrList().then(res => {
if (res.code === 200) {
this.remoteData.publisher = res.data;
} else {
this.remoteData.publisher = [];
}
});
},
/**
* 高级筛选tag
*/
fetchSuperFilterTags() {
tagApi.queryHierarchical().then(res => {
let newList = [];
for (let index in res.data) {
let item = res.data[index];
let newItem = { value: item.id, label: item.nameCh, children: [] };
for (let childrenIndex in item.children) {
let childrenItem = item.children[childrenIndex];
newItem.children.push({
value: childrenItem.id,
label: childrenItem.nameCh
});
}
newList.push(newItem);
}
this.remoteData.tags = newList;
});
}
}
};
</script>
<style scoped>
.pagination-right {
text-align: right;
margin-top: 10px;
}
.box-card {
min-height: 300px;
height: calc(100vh - 140px);
border-radius: 10px;
.el-card__header {
background-color: rgba(249, 249, 249, 1);
}
}
/deep/ .el-dialog {
border-radius: 5px;
}
.popup-dialog {
.el-dialog__header {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
height: 50px;
background-color: #f5f5f5f5;
}
.el-dialog__title {
font-size: 15px;
font-weight: bolder;
}
.el-dialog__footer {
border-top: solid;
border-top-width: thin;
border-top-color: rgb(233, 233, 233);
padding: 10px 20px 10px;
}
}
.in-card-form {
height: 60px;
display: flex;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both;
}
.card-header {
height: 23px;
}
.app-container {
font-family: "Microsoft YaHei Bold", "Microsoft YaHei Regular",
"Microsoft YaHei", sans-serif;
}
.card-header-text {
font-family: "Microsoft YaHei Bold", "Microsoft YaHei Regular",
"Microsoft YaHei", sans-serif;
font-weight: 700;
font-style: normal;
font-size: 16px;
text-align: left;
line-height: 20px;
letter-spacing: normal;
color: #333333;
}
</style>

@ -0,0 +1,245 @@
<!--
* @Description:
* @version: v1
* @Author: zhangjiabao
* @Date: 2024-01-20 02:11:55
-->
<template>
<div>
<el-form
ref="modifyDialogForm"
:model="data"
:rules="rules"
style="width: 800px;margin: 0 auto;"
label-position="left"
label-suffix=":"
label-width="100px"
>
<el-form-item prop="title" label="文章标题">
<el-input
v-model="data.title"
size="small"
style="width:187px"
placeholder="请输入文章标题"
></el-input>
</el-form-item>
<el-form-item prop="type" label="文章类型">
<el-select placeholder="选择文章类型" size="small" v-model="data.type">
<el-option
v-for="item in ['用户协议', '隐私协议', '公告文章', '普通文章']"
:label="item"
:value="item"
:key="item + ''"
></el-option>
</el-select>
</el-form-item>
<el-form-item
size="small"
prop="userId"
placeholder="输入发布人名称"
label="文章作者"
>
<el-select
placeholder="输入发布人名称"
size="small"
v-model="data.userId"
>
<el-option
v-for="item in publisher"
:label="item.name"
:value="item.id"
:key="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item prop="image" label="文章封面">
<img style="width: 170px;height: 120px;" :src="data.image" />
<el-upload
:action="`${uploadBase}/luoo-music/cms/article/upload/image`"
:headers="uploadHeader"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
:on-change="handleUploadArticleImageSuccess"
multiple
:limit="1"
:on-exceed="handleExceed"
:file-list="coverUploadFileList"
>
<el-button size="small" type="primary" icon="el-icon-receiving"
>上传图片</el-button
>
<div
slot="tip"
style="font-family: '微软雅黑', sans-serif; font-weight: 400; font-style: normal; color: #999999;"
>
支持格式.jpg, .png 单个文件不能超过5MB 建议图片分辨率640*520
</div>
</el-upload>
</el-form-item>
<el-form-item prop="isScheduled" label="发布方式">
<el-button
:type="data.isScheduled === '1' ? 'default' : 'primary'"
size="small"
@click="handelSwitchIsSchedule(false)"
>立即发布</el-button
>
<el-button
:type="data.isScheduled === '0' ? 'default' : 'primary'"
size="small"
@click="handelSwitchIsSchedule(true)"
>定时发布</el-button
>
<br />
<el-date-picker
placeholder="请选择时间"
v-if="data.isScheduled === '1'"
size="small"
type="datetime"
v-model="data.pubTime"
></el-date-picker>
</el-form-item>
<el-form-item prop="allowCommit" label="评论设置">
<el-checkbox
v-model="data.allowCommit"
size="small"
:true-label="'1'"
:false-label="'0'"
>允许评论</el-checkbox
>
</el-form-item>
<el-form-item
prop="autoPush"
label="消息推送"
:true-label="'1'"
:false-label="'0'"
>
<el-checkbox v-model="data.autoPush" size="small"></el-checkbox>
</el-form-item>
</el-form>
<el-form
label-suffix=":"
style="width: calc(50% + 400px); margin: 15px 0 auto auto;"
label-position="left"
label-width="100px"
>
<el-form-item prop="content" label="文章详情">
<quill-editor
ref="text"
v-model="data.content"
style="height: 500px;"
class="myQuillEditor"
:options="editorOption"
/>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { getToken } from "@/utils/auth";
import { quillEditor } from "vue-quill-editor";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
export default {
name: "ArticleModify",
props: {
data: {
type: Object,
default: {
id: null,
allowCommit: "0",
autoPush: "0",
content: "",
image: "",
isScheduled: "0",
pubTime: "",
title: "",
type: "",
userId: ""
}
},
publisher: {
type: Array,
default: []
}
},
data() {
return {
uploadHeader: {
"Admin-Token": getToken(),
"X-Token": getToken(),
Authorization: getToken()
},
uploadBase: process.env.BASE_API,
coverUploadFileList: [],
// editorOption
// action: '/api/product/richtext_img_upload.do', //
// methods: 'post', //
// token: '', // tokentokensessionStorage
// name: 'upload_file', //
// size: 500, // Kb, 1M = 1024Kb
// accept: 'multipart/form-data, image/png, image/gif, image/jpeg, image/bmp, image/x-icon,image/jpg' //
editorOption: {},
rules: {
title: [{ required: true, message: "请输入文章标题", trigger: "blur" }],
type: [
{ required: true, message: "请选择文章类型", trigger: "change" }
],
userId: [
{ required: true, message: "请选择文章作者", trigger: "blur" }
],
image: [{ required: true, message: "请上传封面", trigger: "change" }],
isScheduled: [
{ required: true, message: "请选择发布方式", trigger: "change" }
],
allowCommit: [
{ required: true, message: "请选择评论设置", trigger: "change" }
],
autoPush: [
{ required: true, message: "请选择消息推送", trigger: "change" }
]
}
};
},
filters: {},
created() {},
components: {
quillEditor
},
mounted() {},
methods: {
// ************************* *************************
handelSwitchIsSchedule(isScheduled) {
this.data.isScheduled = isScheduled ? "1" : "0";
},
// ************************* *************************
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
},
handleExceed(files, fileList) {
this.$message.warning(
`当前限制选择 1 个文件,本次选择了 ${
files.length
} 个文件共选择了 ${files.length + fileList.length} 个文件`
);
},
beforeRemove(file, fileList) {
return this.$confirm(`确定移除 ${file.name}`);
},
handleUploadArticleImageSuccess(response, file, fileList) {
if (response.status === "success") {
this.$message.success("上传成功");
this.data.image = response.response.data["fullUrl"];
this.coverUploadFileList = [];
}
}
}
};
</script>

@ -0,0 +1,269 @@
<!--
* @Description: 文章
* @version: v1
* @Author: zhangjiabao
* @Date: 2024-01-19 23:29:58
-->
<template>
<div class="app-container">
<el-card class="box-card">
<div slot="header" class="card-header clearfix">
<span class="card-header-text">{{
["文章发布", "文章编辑"][type]
}}</span>
<div style="float: right; margin-top: -5px;">
<el-button
icon="el-icon-s-fold"
size="small"
@click="handleBackToList"
>返回列表</el-button
>
<el-button
type="primary"
icon="el-icon-circle-check"
size="small"
@click="save"
:loading="saveLoading"
:disabled="saveLoading"
>保存文章</el-button
>
</div>
</div>
<el-result v-if="resultShow" icon="success" title="保存成功">
<template slot="extra">
<el-button
@click="handleBackToList"
plain
type="primary"
size="medium"
>返回文章列表</el-button
>
<el-button @click="handleNew" plain type="default" size="medium"
>新建文章</el-button
>
</template>
</el-result>
<ArticleModify
style="margin-left: -200px;"
ref="modifyDialog"
v-if="contentShow"
:data="data"
:remoteTagList="remoteData.tags"
:publisher="remoteData.publisher"
></ArticleModify>
</el-card>
</div>
</template>
<script>
import articleApi from "@/api/article";
import tagApi from "@/api/tag";
import ArticleModify from "./articleModify.vue";
export default {
name: "ArticlePublish",
data() {
return {
type: 0,
remoteData: { publisher: [] },
contentShow: false,
resultShow: false,
saveLoading: false,
data: {
id: null,
allowCommit: "0",
autoPush: "0",
content: "",
image: "",
isScheduled: "0",
pubTime: "",
title: "",
type: "",
userId: ""
}
};
},
components: {
ArticleModify
},
filters: {},
created() {
if (this.$route.query.type && this.$route.query.type === "modify") {
if (this.$route.query.id) {
this.fetchDetail(this.$route.query.id);
}
} else {
this.contentShow = true;
}
this.fetchCreaterList();
},
methods: {
fetchDetail(id) {
articleApi.getDetail(id).then(res => {
if (res.code === 200) {
this.data = JSON.parse(JSON.stringify(res.data));
this.type = 1;
this.contentShow = true;
} else {
this.type = 0;
this.$message.error(res.message);
}
});
},
/**
* 创建人下拉框
*/
fetchCreaterList() {
tagApi.createrList().then(res => {
if (res.code === 200) {
this.remoteData.publisher = res.data;
} else {
this.remoteData.publisher = [];
}
});
},
handleBackToList() {
this.$router.push("/contentManage/article");
},
handleNew() {
this.data = {
id: null,
allowCommit: "0",
autoPush: "0",
content: "",
image: "",
isScheduled: "0",
pubTime: "",
title: "",
type: "",
userId: ""
};
this.type = 0;
this.resultShow = false;
this.contentShow = true;
this.$refs.modifyDialog.selectTag = [];
},
/**
* 保存
*/
save() {
this.saveLoading = true;
this.$refs.modifyDialog.$refs.modifyDialogForm.validate(valid => {
if (valid) {
let orgData = this.$refs.modifyDialog.data;
let param = orgData;
if (param.content === "" || param.content === null) {
this.$message.error("请填入内容");
this.saveLoading = false;
return;
}
console.log(param);
//
if (this.type === 0) {
articleApi.add(param).then(res => {
if (res.code === 200) {
this.data = {
id: null,
allowCommit: "0",
autoPush: "0",
content: "",
image: "",
isScheduled: "0",
pubTime: "",
title: "",
type: "",
userId: ""
};
this.$refs.modifyDialog.selectTag = [];
this.saveLoading = false;
this.contentShow = false;
this.resultShow = true;
this.$message.success(res.message);
} else {
this.saveLoading = false;
this.$message.error(res.message);
}
});
return;
}
if (this.type === 1) {
articleApi.update(param.id, param).then(res => {
if (res.code === 200) {
this.data = {
id: null,
allowCommit: "0",
autoPush: "0",
content: "",
image: "",
isScheduled: "0",
pubTime: "",
title: "",
type: "",
userId: ""
};
this.$refs.modifyDialog.selectTag = [];
this.saveLoading = false;
this.contentShow = false;
this.resultShow = true;
this.$message.success(res.message);
} else {
this.saveLoading = false;
this.$message.error(res.message);
}
});
return;
}
} else {
this.$message.error("请完善表单相关信息!");
this.saveLoading = false;
return false;
}
});
}
}
};
</script>
<style scoped>
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both;
}
.card-header {
height: 23px;
}
.app-container {
font-family: "Microsoft YaHei Bold", "Microsoft YaHei Regular",
"Microsoft YaHei", sans-serif;
}
.card-header-text {
font-family: "Microsoft YaHei Bold", "Microsoft YaHei Regular",
"Microsoft YaHei", sans-serif;
font-weight: 700;
font-style: normal;
font-size: 16px;
text-align: left;
line-height: 20px;
letter-spacing: normal;
color: #333333;
}
.box-card {
min-height: 300px;
height: calc(100vh - 140px);
border-radius: 10px;
padding-bottom: 30px;
.el-card__header {
background-color: rgba(249, 249, 249, 1);
}
.el-card__body {
overflow: auto;
height: calc(100% - 50px);
padding-bottom: 80px;
}
}
</style>

@ -0,0 +1,593 @@
<!--
* @Description: 文章
* @version: v1
* @Author: zhangjiabao
* @Date: 2024-01-19 23:29:58
-->
<template>
<div class="app-container">
<!-- card -->
<el-card class="box-card">
<div slot="header" class="card-header clearfix">
<span class="card-header-text">文章列表</span>
</div>
<!-- 查询条件 -->
<el-form :inline="true" class="in-card-form" label-suffix=":">
<el-form-item>
<el-input
placeholder="文章标题"
size="medium"
disabled
v-model="queryForm.data.name"
></el-input>
</el-form-item>
<el-form-item>
<el-select
placeholder="文章分类"
size="medium"
disabled
v-model="queryForm.data.type"
>
<el-option
v-for="item in queryForm.remoteData.type"
:label="item.name"
:value="item.id"
:key="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select
placeholder="发布者"
size="medium"
disabled
v-model="queryForm.data.creatorId"
>
<el-option
v-for="item in queryForm.remoteData.publisher"
:label="item.name"
:value="item.id"
:key="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-date-picker
placeholder="发布时间"
disabled
v-model="queryForm.data.timeRange"
type="daterange"
:range-separator="
queryForm.data.timeRange.length === 0 ? null : '~'
"
:editable="false"
:clearable="false"
value-format="yyyy-MM-dd"
start-placeholder="选择时间范围"
size="medium"
style="width: 280px;"
>
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button
icon="el-icon-search"
type="primary"
size="medium"
@click="handleQueryClick"
>查询</el-button
>
</el-form-item>
<el-form-item>
<el-button
icon="el-icon-refresh-left"
size="medium"
@click="handleResetClick"
>重置</el-button
>
</el-form-item>
</el-form>
<el-form :inline="true" class="in-card-form">
<el-form-item>
<el-button
type="primary"
icon="el-icon-plus"
size="medium"
@click="handleAddClick"
>发布文章</el-button
>
</el-form-item>
</el-form>
<!-- 表格 -->
<el-table
stripe
:data="table.data"
height="calc(100vh - 400px)"
v-loading="table.loading"
v-if="table.show"
element-loading-text="加载中"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
border
fit
>
<el-table-column align="center" label="序号" width="95">
<template slot-scope="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column label="文章封面">
<template slot-scope="scope">
<img style="height: 50px;" :src="scope.row.image" />
</template>
</el-table-column>
<el-table-column label="文章标题" prop="title"></el-table-column>
<el-table-column label="文章分类" prop="type"></el-table-column>
<el-table-column label="文章浏览量" prop="vistisNum"></el-table-column>
<el-table-column label="作者" prop="userName"></el-table-column>
<el-table-column label="发布时间" prop="date"></el-table-column>
<el-table-column fixed="right" label="操作" width="300px">
<template slot-scope="scope">
<el-button
plain
icon="el-icon-info"
@click="handleDetailClick(scope.row)"
type="primary"
size="mini"
>详情</el-button
>
<el-button
plain
icon="el-icon-edit"
@click="handleModifyClick(scope.row)"
type="primary"
size="mini"
>编辑</el-button
>
<el-popconfirm
confirm-button-text="确定"
cancel-button-text="取消"
icon="el-icon-info"
icon-color="red"
title="是否确认删除文章?"
style="margin-left: 10px;"
@confirm="handleDeleteClick(scope.row)"
>
<el-button
slot="reference"
plain
icon="el-icon-delete"
type="danger"
size="mini"
>删除</el-button
>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
class="pagination-right"
background
v-if="table.show"
layout="total, prev, pager, next, sizes"
:current-page.sync="table.page.current"
:page-size="table.page.size"
:total="table.page.total"
@size-change="handlePageSizeChange"
@current-change="handlePageCurrentChange"
>
</el-pagination>
</el-card>
<el-dialog
title="详情"
class="popup-dialog"
:visible="true"
v-if="modifyDialog.visible"
width="80%"
top="60px"
:before-close="handleModifyDialogBeforeClose"
>
<div
v-loading="modifyDialog.loading"
style="height: 70vh;overflow: auto;padding-left:20px;padding-right:20px"
>
<ArticleDetail
v-if="modifyDialog.visible && modifyDialog.type === 2"
:data="modifyDialog.data"
:remoteTagList="queryForm.remoteData.superFilterTags"
:publisher="queryForm.remoteData.publisher"
></ArticleDetail>
</div>
<span slot="footer" class="dialog-footer">
<el-button size="small" type="primary" @click="handleDialogCancel"
>确定</el-button
>
<el-button size="small" @click="handleDialogCancel"></el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import articleApi from "@/api/article";
import tagApi from "@/api/tag";
import ArticleDetail from "./articleDetail.vue";
export default {
name: "Article",
data() {
return {
//
queryForm: {
remoteData: {
publisher: [],
type: []
},
lastQuery: {},
superFilterDialog: {
visible: false
},
data: {
name: null,
type: null,
creatorId: null,
timeRange: []
}
},
//
table: {
show: true,
loading: true,
data: [],
page: {
current: 1,
size: 10,
total: 0
}
},
//
modifyDialog: {
visible: false,
loading: true,
type: 0, // 0for 1for 2for
data: {} //
}
};
},
components: {
ArticleDetail
},
filters: {},
created() {
this.fetchCreaterList();
this.fetchTypeList();
this.fetchData(this.queryForm.data);
},
methods: {
log(e) {
this.$message.success(e);
},
// ************************* *************************
/**
* 查询表格数据
*/
fetchData(param) {
//
this.queryForm.lastQuery = param;
let queryParam = {
end: "",
keyword: param.name,
start: "",
tags: param.tags,
userId: param.creatorId,
type: param.type
};
if (param.timeRange) {
if (param.timeRange[0]) {
queryParam.start = param.timeRange[0];
}
if (param.timeRange[1]) {
queryParam.end = param.timeRange[1];
}
}
this.table.loading = true;
this.table.data = [];
articleApi
.getList(queryParam, this.table.page.current, this.table.page.size)
.then(res => {
if (res.code === 200) {
this.table.data = res.data.rows;
this.table.page.total = res.data.total;
} else {
this.$message.error(res.message);
}
this.table.loading = false;
});
},
/**
* 创建人下拉框
*/
fetchCreaterList() {
tagApi.createrList().then(res => {
if (res.code === 200) {
this.queryForm.remoteData.publisher = res.data;
} else {
this.queryForm.remoteData.publisher = [];
}
});
},
/**
* 类型下拉框
*/
fetchTypeList() {
this.queryForm.remoteData.type = [];
// tagApi.typeList().then(res => {
// if (res.code === 200) {
// this.queryForm.remoteData.type = res.data;
// } else {
// this.queryForm.remoteData.type = [];
// }
// });
},
// ************************* *************************
/**
* 匹配发布者
*/
getAuthorNameById(userId) {
for (let index in this.queryForm.remoteData.publisher) {
let item = this.queryForm.remoteData.publisher[index];
if (item.id === userId) {
return item.name;
}
return "";
}
},
/**
* 匹配文章分类
*/
getTagNameById(type) {
for (let index in this.queryForm.remoteData.type) {
let item = this.queryForm.remoteData.type[index];
if (item.id === type) {
return item.name;
}
return "";
}
},
// ************************* *************************
/**
* 查询按扭点击事件
*/
handleQueryClick() {
this.table.page.current = 1;
this.table.page.total = 0;
this.fetchData(this.queryForm.data);
},
/**
* 重置点击事件
*/
handleResetClick() {
this.queryForm.data = {
parentId: null,
name: null,
type: null,
creatorId: null,
timeRange: []
};
},
/**
* 新建标签点击事件
*/
handleAddClick() {
this.$router.push(`/contentManage/articleModify?type=add`);
},
/**
* 编辑标签点击事件
*/
handleModifyClick(row) {
this.$router.push(
`/contentManage/articleModify?id=${row.id}&type=modify`
);
},
/**
* 发布点击事件
*/
handlePublishClick(row) {
this.publishDialog.id = row.id;
this.publishDialog.time = null;
this.publishDialog.type = 0;
this.publishDialog.visible = true;
},
/**
* 详情点击事件
*/
handleDetailClick(row) {
this.modifyDialog.visible = true;
this.modifyDialog.loading = true;
articleApi.getDetail(row.id).then(res => {
if (res.code === 200) {
this.modifyDialog.data = JSON.parse(JSON.stringify(res.data));
this.modifyDialog.type = 2;
this.modifyDialog.loading = false;
} else {
this.modifyDialog.data = JSON.parse(JSON.stringify(row));
this.modifyDialog.type = 2;
this.modifyDialog.loading = false;
this.$message.error(res.message);
}
});
},
handleDeleteClick(row) {
articleApi.delete(row.id).then(res => {
if (res.code === 200) {
this.$message.success(res.message);
this.fetchData(this.queryForm.lastQuery);
} else {
this.$message.error(res.message);
}
});
},
/**
* 弹窗取消按扭点击事件
*/
handleDialogCancel() {
this.modifyDialog.visible = false;
},
/**
* 弹窗取消按扭点击事件
*/
handlePublishDialogCancel() {
this.publishDialog.visible = false;
},
/**
* 发布弹窗确认按扭
*/
handlePublishDialogSubmit() {
articleApi
.publish(
this.publishDialog.id,
this.publishDialog.type,
this.publishDialog.time
)
.then(res => {
if (res.code === 200) {
this.$message.success(res.message);
this.fetchData(this.queryForm.lastQuery);
this.publishDialog.visible = false;
return;
} else {
this.$message.error(res.message);
}
});
},
/**
* 弹窗重置
*/
handleDialogReset() {
this.$message.success("重置");
},
/**
* 弹窗关闭按扭点击事件
*/
handleModifyDialogBeforeClose(done) {
this.modifyDialog.visible = false;
done();
},
/**
* 弹窗关闭按扭点击事件
*/
handleTagsDialogBeforeClose(done) {
this.queryForm.superFilterDialog.visible = false;
done();
},
/**
* 弹窗关闭按扭点击事件
*/
handlePublishDialogBeforeClose(done) {
this.publishDialog.visible = false;
done();
},
/**
* 分页组件页数变化事件
*/
handlePageCurrentChange(val) {
this.table.page.current = val;
this.fetchData(this.queryForm.lastQuery);
},
/**
* 分页组件大小变化事件
*/
handlePageSizeChange(val) {
this.table.page.size = val;
this.fetchData(this.queryForm.lastQuery);
},
/**
* 切换标签事件
*/
handleChangeActivateTab(tab) {
this.activateTab = tab;
//
this.handleResetClick();
//
this.table.data = [];
this.table.page.current = 1;
this.table.page.size = 10;
this.table.show = false;
this.$nextTick(() => {
this.table.show = true;
});
this.fetchData(this.queryForm.data);
}
}
};
</script>
<style scoped>
.pagination-right {
text-align: right;
margin-top: 10px;
}
.box-card {
min-height: 300px;
height: calc(100vh - 140px);
border-radius: 10px;
.el-card__header {
background-color: rgba(249, 249, 249, 1);
}
}
/deep/ .el-dialog {
border-radius: 5px;
}
.popup-dialog {
.el-dialog__header {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
height: 50px;
background-color: #f5f5f5f5;
}
.el-dialog__title {
font-size: 15px;
font-weight: bolder;
}
.el-dialog__footer {
border-top: solid;
border-top-width: thin;
border-top-color: rgb(233, 233, 233);
padding: 10px 20px 10px;
}
}
.in-card-form {
height: 60px;
display: flex;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both;
}
.card-header {
height: 23px;
}
.app-container {
font-family: "Microsoft YaHei Bold", "Microsoft YaHei Regular",
"Microsoft YaHei", sans-serif;
}
.card-header-text {
font-family: "Microsoft YaHei Bold", "Microsoft YaHei Regular",
"Microsoft YaHei", sans-serif;
font-weight: 700;
font-style: normal;
font-size: 16px;
text-align: left;
line-height: 20px;
letter-spacing: normal;
color: #333333;
}
</style>

@ -0,0 +1,81 @@
<!--
* @Author: zhangjiabao
* @Date: 2024-02-22 15:25:43
* @LastEditors: zhangjiabao
* @LastEditTime: 2024-02-22 15:47:16
* @FilePath: /luoo_manage_fe/src/views/dashboard/homePageByJournal.vue
-->
<template>
<div>
<el-carousel :interval="6000" type="card" height="600px">
<el-carousel-item v-for="item in data" :key="item['id']">
<div style="height:100%; width: 100%; text-align:center">
<img
class="medium"
style="max-height:calc(100% - 20px); width: 100%; text-align:center"
:src="item['image']"
@click="jumpToJournal(item['id'])"
/>
<br />
<span @click="jumpToJournal(item['id'])">{{
"VOL " + item["journalNo"] + " " + item["title"]
}}</span>
</div>
</el-carousel-item>
</el-carousel>
</div>
</template>
<script>
import journalApi from "@/api/journal";
export default {
name: "HomePageByJournal",
components: {},
props: {},
data() {
return {
data: []
};
},
computed: {},
watch: {},
created() {
this.fetchData();
},
mounted() {},
methods: {
/**
* 查询表格数据
*/
fetchData(param) {
let queryParam = {
end: "",
keyword: null,
start: "",
tab: 1,
tags: null,
userId: null
};
this.data = [];
journalApi.getList(queryParam, 1, 10).then(res => {
if (res.code === 200) {
this.data = res.data.rows;
}
});
},
jumpToJournal(id) {
this.$router.push(`/journal/detail?id=${id}`);
}
}
};
</script>
<style scoped>
.el-carousel__item h3 {
color: #475669;
font-size: 14px;
opacity: 0.75;
line-height: 200px;
margin: 0;
}
</style>

@ -2,25 +2,27 @@
* @Author: zhangjiabao
* @Date: 2024-01-02 15:41:16
* @LastEditors: zhangjiabao
* @LastEditTime: 2024-01-25 11:22:50
* @LastEditTime: 2024-02-24 21:11:46
* @FilePath: /luoo_manage_fe/src/views/dashboard/index.vue
-->
<template>
<div class="dashboard-container">
<!-- <div class="dashboard-text">name:{{name}}</div> -->
<!-- <div class="dashboard-text">roles:<span v-for='role in roles' :key='role'>{{role}}</span></div> -->
<span>QUEYUE</span>
<HomePageByJournal> </HomePageByJournal>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import HomePageByJournal from "@/views/dashboard/homePageByJournal";
export default {
name: "dashboard",
computed: {
...mapGetters(["name", "roles"])
}
},
components: { HomePageByJournal }
};
</script>

@ -1,20 +1,31 @@
<!--
* @Author: zhangjiabao
* @Date: 2024-02-23 09:33:20
* @LastEditors: zhangjiabao
* @LastEditTime: 2024-02-24 22:13:54
* @FilePath: /luoo_manage_fe/src/views/layout/components/Navbar.vue
-->
<template>
<el-menu class="navbar" mode="horizontal">
<hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
<hamburger
class="hamburger-container"
:toggleClick="toggleSideBar"
:isActive="sidebar.opened"
></hamburger>
<breadcrumb></breadcrumb>
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<img class="user-avatar" :src="avatar+'?imageView2/1/w/80/h/80'">
<svg-icon style="width: 30px; height: 40px;" icon-class="user" />
<i class="el-icon-caret-bottom"></i>
</div>
<el-dropdown-menu class="user-dropdown" slot="dropdown">
<router-link class="inlineBlock" to="/">
<el-dropdown-item>
Home
首页
</el-dropdown-item>
</router-link>
<el-dropdown-item divided>
<span @click="logout" style="display:block;">LogOut</span>
<span @click="logout" style="display:block;">登出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -22,9 +33,9 @@
</template>
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import { mapGetters } from "vuex";
import Breadcrumb from "@/components/Breadcrumb";
import Hamburger from "@/components/Hamburger";
export default {
components: {
@ -32,22 +43,19 @@ export default {
Hamburger
},
computed: {
...mapGetters([
'sidebar',
'avatar'
])
...mapGetters(["sidebar", "avatar"])
},
methods: {
toggleSideBar() {
this.$store.dispatch('ToggleSideBar')
this.$store.dispatch("ToggleSideBar");
},
logout() {
this.$store.dispatch('LogOut').then(() => {
location.reload() // vue-router bug
})
this.$store.dispatch("LogOut").then(() => {
location.reload(); // vue-router bug
});
}
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@ -91,4 +99,3 @@ export default {
}
}
</style>

Loading…
Cancel
Save