1、英语文章管理基本全部实现,页面及后端

2、包含列表、详情、添加、修改
3、前端引入图片上传组件上传到阿里云oss
pull/254/head
xjs 4 years ago
parent 94f4c23ea0
commit 98e5af2ef2

@ -2,7 +2,9 @@ package com.ruoyi.system.api;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.constant.ServiceNameConstants;
@ -26,4 +28,16 @@ public interface RemoteFileService
*/
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file);
/**
*
*
* @author xjs
* @since 2022-03-03
* @param url
* @return R
*/
@DeleteMapping("/remove")
R removeFile(@RequestParam("url") String url);
}

@ -30,6 +30,11 @@ public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileServ
{
return R.fail("上传文件失败:" + throwable.getMessage());
}
@Override
public R removeFile(String url) {
return R.fail("删除文件失败:" + throwable.getMessage());
}
};
}
}

@ -0,0 +1,62 @@
import request from '@/utils/request'
// 查询英语文章列表
export function listArticle(query) {
return request({
url: '/english/article/list',
method: 'get',
params: query
})
}
// 查询英语文章详细
export function getArticle(id) {
return request({
url: '/english/article/' + id,
method: 'get'
})
}
// 新增英语文章
export function addArticle(data) {
return request({
url: '/english/article',
method: 'post',
data: data
})
}
//上传图片
export function uploadImg(data){
return request({
url: '/file/upload',
method: 'post',
data: data
})
}
//删除图片
export function removeImg(url){
return request({
url: '/file/remove',
method: 'delete',
params: url
})
}
// 修改英语文章
export function updateArticle(data) {
return request({
url: '/english/article',
method: 'put',
data: data
})
}
// 删除英语文章
export function delArticle(id) {
return request({
url: '/english/article/' + id,
method: 'delete'
})
}

@ -0,0 +1,155 @@
<template>
<div class="app-container">
<el-row :gutter="15">
<el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
<el-col :span="12">
<el-form-item label="标题" prop="titleChinese">
<el-input v-model="formData.titleChinese" placeholder="请输入标题" :maxlength="100" clearable
prefix-icon='el-icon-eleme' :style="{width: '100%'}"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="内容" prop="contentChinese">
<el-input v-model="formData.contentChinese" type="textarea" placeholder="请输入内容" :maxlength="1000"
:autosize="{minRows: 8, maxRows: 12}" :style="{width: '100%'}"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="上传封面" prop="pictureUrl" required>
<el-upload ref="pictureUrl"
action="#" :http-request="requestUpload"
:limit="1"
:before-remove="removeImg"
:before-upload="picture_rulBeforeUpload" list-type="picture-card" accept="image/*"
name="pictureUrl">
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item size="large">
<el-button type="primary"
v-hasPermi="['english:article:add']"
@click="submitForm">提交
</el-button>
<el-button @click="resetForm"></el-button>
</el-form-item>
</el-col>
</el-form>
</el-row>
</div>
</template>
<script>
import {addArticle, uploadImg, removeImg} from "@/api/business/english/article";
export default {
name: "ArticleAdd",
components: {},
props: [],
data() {
return {
formData: {
titleChinese: undefined,
contentChinese: undefined,
pictureUrl: null,
},
rules: {
titleChinese: [{
required: true,
message: '请输入标题',
trigger: 'blur'
}],
contentChinese: [{
required: true,
message: '请输入内容',
trigger: 'blur'
}],
},
}
},
computed: {},
watch: {},
created() {
},
mounted() {
},
methods: {
//
addArticle() {
this.$modal.loading("请稍候...");
addArticle(this.formData).then(res => {
this.$modal.notifySuccess("添加成功");
this.$modal.closeLoading()
this.$router.push({path: '/business/english/article/articleList'})
}).catch(err =>{
this.$modal.closeLoading()
})
},
submitForm() {
this.$refs['elForm'].validate(valid => {
if (!valid) return
this.addArticle()
})
},
resetForm() {
this.$refs['elForm'].resetFields()
},
//
requestUpload() {
},
picture_rulBeforeUpload(file) {
let isRightSize = file.size / 1024 / 1024 < 10
if (!isRightSize) {
this.$message.error('文件大小超过 10MB')
}
let isAccept = new RegExp('image/*').test(file.type)
if (!isAccept) {
this.$message.error('应该选择image/*类型的文件')
}
let formData = new FormData();
formData.append("file", file);
this.$modal.loading("请稍候...");
uploadImg(formData).then(res => {
this.formData.pictureUrl = res.data.url
this.$modal.closeLoading()
this.$modal.notifySuccess("上传成功");
}).catch(err =>{
this.$modal.closeLoading()
})
return isRightSize && isAccept
},
//
removeImg() {
if (this.formData.pictureUrl) {
let pictureUrl = {"url": this.formData.pictureUrl};
removeImg(pictureUrl).then(res => {})
this.formData.pictureUrl = null;
}
},
}
}
</script>
<style scoped>
.el-upload__tip {
line-height: 1.2;
}
</style>

@ -0,0 +1,74 @@
<template>
<div class="app-container">
<div class="chinese">
<h2>{{article.titleChinese}}</h2>
<span>{{article.contentChinese}}</span>
</div>
<el-divider content-position="center">华丽的分割线</el-divider>
<div class="english">
<h2>{{article.titleEnglish}}</h2>
<span>{{article.contentEnglish}}</span>
</div>
<div>
<el-image
style="width: 200px;height: auto"
:src="article.pictureUrl"
></el-image>
</div>
</div>
</template>
<script>
import {getArticle} from "@/api/business/english/article";
export default {
name: "ArticleDetails",
data() {
return {
id: null,
article: {},
}
},
created() {
//id
this.id = this.$route.query.id;
if (this.id) {
this.getArticle();
}
},
methods: {
getArticle() {
this.$modal.loading("请稍候...");
getArticle(this.id).then(res => {
this.article = res.data;
this.$modal.closeLoading()
}).catch(err =>{
this.$modal.closeLoading()
})
},
}
}
</script>
<style scoped>
.app-container > div {
text-align:center;
margin-top: 50px;
margin-bottom: 50px;
}
</style>

@ -0,0 +1,373 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
<el-form-item label="查询条件" prop="condition">
<el-input
v-model="queryParams.condition"
placeholder="请输入查询条件"
clearable
maxlength="10"
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
v-model="daterangeCreateTime"
size="small"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
:picker-options="pickerOptions"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="dateQuery"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
<router-link :to="'/business/english/article/add'" style="margin-left: 10px">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
v-hasPermi="['english:article:add']"
>新增
</el-button>
</router-link>
</el-form-item>
</el-form>
<div v-loading="loading">
<el-timeline v-for="article in articleList">
<el-timeline-item :timestamp=article.createTime placement="top">
<el-button type="text"
@click="toEdit(article)"
icon="el-icon-edit">编辑
</el-button>
<el-divider direction="vertical"></el-divider>
<el-popconfirm
title="确定删除吗?"
@confirm="remove(article.id)"
>
<el-button type="text"
slot="reference"
v-hasPermi="['english:article:remove']"
icon="el-icon-delete">删除
</el-button>
</el-popconfirm>
<el-card>
<router-link
:to="{
path: '/business/english/article/details',
query: { id: article.id },
}"
>
<span class="text-line-h3">{{ article.titleChinese }}</span>
</router-link>
<el-divider content-position="center">作者{{ article.createUser }}</el-divider>
<span class="text-line">{{ article.contentChinese }}</span>
</el-card>
</el-timeline-item>
</el-timeline>
<el-empty description="暂无内容" v-if="articleList.length===0"></el-empty>
</div>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="listArticle"
/>
<el-dialog
title="修改文章"
:visible.sync="dialogVisible"
width="50%"
@close="close"
>
<div>
<el-row :gutter="15">
<el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
<el-col :span="24">
<el-form-item label="标题" prop="titleChinese">
<el-input v-model="formData.titleChinese" placeholder="请输入标题" :maxlength="100" clearable
prefix-icon='el-icon-eleme' :style="{width: '100%'}"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="内容" prop="contentChinese">
<el-input v-model="formData.contentChinese" type="textarea" placeholder="请输入内容" :maxlength="1000"
:autosize="{minRows: 8, maxRows: 12}"
style="width: 100%;"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="上传封面" prop="pictureUrl" required>
<el-upload ref="pictureUrl"
action="#" :http-request="requestUpload"
:file-list="fileList"
:limit="1"
:before-remove="removeImg"
:before-upload="picture_rulBeforeUpload"
list-type="picture-card" accept="image/*"
name="pictureUrl">
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
</el-col>
</el-form>
</el-row>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="close"> </el-button>
<el-button @click="resetForm"></el-button>
<el-button type="primary" @click="editSubmit"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {listArticle, updateArticle, delArticle, removeImg, uploadImg} from "@/api/business/english/article";
import {pickerOptions} from "@/layout/mixin/PickerOptions";
export default {
name: "EnglishArticle",
mixins: [pickerOptions],
data() {
return {
//list
articleList: [],
fileList: [],
dialogVisible: false,
//
queryParams: {
pageNum: 1,
pageSize: 3,
condition: null,
createTime: null,
},
//
daterangeCreateTime: [],
//
total: 0,
loading: false,
formData: {
titleChinese: undefined,
contentChinese: undefined,
pictureUrl: null,
},
rules: {
titleChinese: [{
required: true,
message: '请输入标题',
trigger: 'blur'
}],
contentChinese: [{
required: true,
message: '请输入内容',
trigger: 'blur'
}],
},
//
file: {},
}
},
created() {
this.listArticle()
},
methods: {
listArticle() {
this.loading = true
if (null != this.daterangeCreateTime && '' != this.daterangeCreateTime) {
this.queryParams.createTime = this.daterangeCreateTime[0];
this.queryParams.endCreateTime = this.daterangeCreateTime[1];
}
listArticle(this.queryParams).then(res => {
this.articleList = res.data.records
this.total = res.data.total
this.loading = false
}).catch(err => {
this.loading = false
})
},
//
editSubmit() {
this.$refs['elForm'].validate(valid => {
if (!valid) return
this.$modal.loading("请稍候...");
updateArticle(this.formData).then(res => {
this.$modal.notifySuccess("修改成功");
this.$modal.closeLoading()
}).catch(err => {
this.$modal.closeLoading()
})
this.dialogVisible = false;
})
},
resetForm() {
this.formData = {}
},
//
toEdit(article) {
this.fileList = []
this.dialogVisible = true;
this.fileList.push({
url: article.pictureUrl
})
this.formData = article
},
//
close() {
this.fileList = []
this.oldImgUrl = null
this.dialogVisible = false;
this.formData = {}
},
//
requestUpload() {
},
//
remove(id) {
this.loading = true
delArticle(id).then(res => {
this.$modal.notifySuccess("删除成功");
this.listArticle()
this.loading = false
}).catch(err => {
this.loading = false
})
},
//
removeImg() {
if (this.formData.pictureUrl) {
let pictureUrl = {"url": this.formData.pictureUrl};
removeImg(pictureUrl).then(res => {
})
this.formData.pictureUrl = null;
}
},
picture_rulBeforeUpload(file) {
this.$modal.loading("请稍候...");
let isRightSize = file.size / 1024 / 1024 < 10
if (!isRightSize) {
this.$message.error('文件大小超过 10MB')
}
let isAccept = new RegExp('image/*').test(file.type)
if (!isAccept) {
this.$message.error('应该选择image/*类型的文件')
}
let formData = new FormData();
formData.append("file", file);
uploadImg(formData).then(res => {
this.formData.pictureUrl = res.data.url
this.$modal.closeLoading()
}).catch(err => {
this.$modal.closeLoading()
})
return isRightSize && isAccept
},
/** 重置按钮操作 */
resetQuery() {
this.daterangeCreateTime = [];
this.queryParams.createTime = null
this.queryParams.endCreateTime = null
this.resetForm("queryForm");
this.handleQuery();
},
dateQuery() {
//
this.queryParams.createTime = null
this.queryParams.endCreateTime = null
this.handleQuery();
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.listArticle();
},
}
}
</script>
<style scoped>
.text-line {
height: 25px;
display: inline-block;
/*下面是必需的*/
width: 100%;
color: #0157;
white-space: nowrap; /*把文本强制显示在一行*/
overflow: hidden; /*隐藏超出部分的文字*/
text-overflow: ellipsis; /*超出显示省略号*/
}
.text-line-h3 {
display: inline-block;
/*下面是必需的*/
width: 30%;
color: #000;
font-size: 15px;
white-space: nowrap; /*把文本强制显示在一行*/
overflow: hidden; /*隐藏超出部分的文字*/
text-overflow: ellipsis; /*超出显示省略号*/
}
</style>

@ -284,7 +284,7 @@ import {getOneEnglishApi} from "@/api/business/openapi/oneenglish";
import {pickerOptions} from "@/layout/mixin/PickerOptions"
export default {
name: "Word",
name: "EnglishWord",
dicts: ['english_collect', 'english_top'],
mixins: [pickerOptions],
data() {

@ -75,7 +75,7 @@ export default {
} else {
this.saveData();
}
this.$router.push({path: '/srb/integral/integralList'})
})
},
@ -92,7 +92,6 @@ export default {
saveData() {
save(this.formData).then(res => {
this.$modal.notifySuccess("新增成功");
this.$router.push({path: '/srb/integral/list'})
})
},
@ -100,7 +99,6 @@ export default {
updateData() {
update(this.formData).then(res => {
this.$modal.notifySuccess("修改成功");
this.$router.push({path: '/srb/integral/list'})
})
},

@ -87,8 +87,8 @@ export default {
this.list = res.data
this.$modal.closeLoading()
}).catch(err => {
this.$modal.closeLoading()
})
loading.close();
},
//

@ -7,10 +7,13 @@ import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.xjs.article.domain.EnglishArticle;
import com.xjs.article.service.impl.EnglishArticleServiceImpl;
import com.xjs.validation.group.AddGroup;
import com.xjs.validation.group.UpdateGroup;
import com.xjs.web.MyBaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
@ -38,13 +41,6 @@ public class EnglishArticleController extends MyBaseController<EnglishArticle> {
return AjaxResult.success(page);
}
@GetMapping("/test")
public AjaxResult test(EnglishArticle englishArticle) {
EnglishArticle translation = englishArticleService.translation(englishArticle);
return AjaxResult.success(translation);
}
@ -67,7 +63,7 @@ public class EnglishArticleController extends MyBaseController<EnglishArticle> {
@Log(title = "英语文章", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增英语文章")
public AjaxResult add(@RequestBody EnglishArticle englishArticle) {
public AjaxResult add(@Validated(AddGroup.class) @RequestBody EnglishArticle englishArticle) {
return toAjax(englishArticleService.insertEnglishArticle(englishArticle));
}
@ -78,7 +74,7 @@ public class EnglishArticleController extends MyBaseController<EnglishArticle> {
@Log(title = "英语文章", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改英语文章")
public AjaxResult edit(@RequestBody EnglishArticle englishArticle) {
public AjaxResult edit(@Validated(UpdateGroup.class)@RequestBody EnglishArticle englishArticle) {
return toAjax(englishArticleService.updateEnglishArticle(englishArticle));
}

@ -4,10 +4,14 @@ import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.ruoyi.common.core.annotation.Excel;
import com.xjs.entity.BaseEntity;
import com.xjs.validation.group.AddGroup;
import com.xjs.validation.group.UpdateGroup;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.Date;
/**
@ -29,6 +33,8 @@ public class EnglishArticle extends BaseEntity {
/** 文章标题-中文 */
@Excel(name = "文章标题-中文")
@NotBlank(groups = {UpdateGroup.class, AddGroup.class},message = "文章标题不能为空")
@Size(max = 255, message = "请控制英语长度在255字符", groups = {UpdateGroup.class, AddGroup.class})
private String titleChinese;
/** 文章内容 */
@ -37,10 +43,13 @@ public class EnglishArticle extends BaseEntity {
/** 文章内容-中文 */
@Excel(name = "文章内容-中文")
@NotBlank(groups = {UpdateGroup.class, AddGroup.class},message = "文章内容不能为空")
@Size(max = 2000, message = "请控制英语长度在2000字符", groups = {UpdateGroup.class, AddGroup.class})
private String contentChinese;
/** 文章图片地址 */
@Excel(name = "文章图片地址")
@NotBlank(groups = {UpdateGroup.class, AddGroup.class},message = "文章封面地址不能为空")
private String pictureUrl;
/** 创建用户 */

@ -6,18 +6,19 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.RemoteFileService;
import com.xjs.article.domain.EnglishArticle;
import com.xjs.article.mapper.EnglishArticleMapper;
import com.xjs.article.service.EnglishArticleService;
import com.xjs.business.api.RemoteTranslationFeign;
import com.xjs.business.api.domain.TranslationVo;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.*;
/**
* service
@ -26,11 +27,19 @@ import java.util.Vector;
* @since 2022-03-03
*/
@Service
@Log4j2
public class EnglishArticleServiceImpl extends ServiceImpl<EnglishArticleMapper, EnglishArticle> implements EnglishArticleService {
/**
*
*/
private static final String SUPER_ADMIN = "admin";
@Autowired
private RemoteTranslationFeign remoteTranslationFeign;
@Autowired
private RemoteFileService remoteFileService;
@Override
@ -42,7 +51,23 @@ public class EnglishArticleServiceImpl extends ServiceImpl<EnglishArticleMapper,
public IPage<EnglishArticle> selectEnglishArticleList(Page<EnglishArticle> page, EnglishArticle englishArticle) {
//封装查询条件
LambdaQueryWrapper<EnglishArticle> wrapper = new LambdaQueryWrapper<>();
// todo 封装查询条件
//获取当前登录用户
String username = SecurityUtils.getUsername();
//admin管理员可以查看所有文章、其他用户只能查看自己的文章
if (!SUPER_ADMIN.equals(username)) {
wrapper.eq(EnglishArticle::getCreateUser, username);
}
wrapper.orderByDesc(EnglishArticle::getCreateTime);
boolean b = Objects.nonNull(englishArticle.getCreateTime()) && Objects.nonNull(englishArticle.getEndCreateTime());
wrapper.between(b, EnglishArticle::getCreateTime, englishArticle.getCreateTime(), englishArticle.getEndCreateTime());
String condition = englishArticle.getCondition();
if (StringUtils.isNotEmpty(condition)) {
wrapper.like(EnglishArticle::getContentChinese, condition).or().like(EnglishArticle::getTitleChinese, condition);
}
return super.page(page, wrapper);
}
@ -50,19 +75,42 @@ public class EnglishArticleServiceImpl extends ServiceImpl<EnglishArticleMapper,
@Override
public boolean insertEnglishArticle(EnglishArticle englishArticle) {
//封装添加参数 远程调用翻译
englishArticle = this.translation(englishArticle);
return super.save(englishArticle);
EnglishArticle newArticle = this.translation(englishArticle);
String username = SecurityUtils.getUsername();
newArticle.setCreateUser(username);
return super.save(newArticle);
}
@Override
public boolean updateEnglishArticle(EnglishArticle englishArticle) {
return super.updateById(englishArticle);
//判断是否有权限修改
boolean b = this.checkOperatePermission(englishArticle);
if (!b) {
return false;
}
EnglishArticle newArticle = this.translation(englishArticle);
return super.updateById(newArticle);
}
@Override
public boolean deleteEnglishArticleByIds(Long[] ids) {
//删除oss中的文件
for (Long id : ids) {
EnglishArticle byId = super.getById(id);
boolean b = this.checkOperatePermission(byId);
if (!b) {
return false;
}
R r = remoteFileService.removeFile(byId.getPictureUrl());
if (r.getCode() == HttpStatus.SUCCESS) {
log.info("图片删除成功");
}
}
return super.removeByIds(Arrays.asList(ids));
}
@ -72,13 +120,31 @@ public class EnglishArticleServiceImpl extends ServiceImpl<EnglishArticleMapper,
}
/**
*
*
* @return
*/
private boolean checkOperatePermission(EnglishArticle englishArticle) {
//判断是否有权限修改
String username = SecurityUtils.getUsername();
if (!SUPER_ADMIN.equals(username)) {
//判断是否当前账号
if (!username.equals(englishArticle.getCreateUser())) {
return false;
}
}
return true;
}
/**
*
*
* @param englishArticle
* @return
*/
public EnglishArticle translation(EnglishArticle englishArticle) {
private EnglishArticle translation(EnglishArticle englishArticle) {
// 使用线程安全的Vector
Vector<Thread> threads = new Vector<Thread>();

Loading…
Cancel
Save