完成栏目功能
diff --git a/backend/build.gradle b/backend/build.gradle
index f922424..d9047cc 100644
--- a/backend/build.gradle
+++ b/backend/build.gradle
@@ -58,6 +58,11 @@
implementation 'com.github.penggle:kaptcha:2.3.2'
testCompile group: 'junit', name: 'junit', version: '4.12'
+
+ compile group: 'net.coobird', name: 'thumbnailator', version: '0.4.8'
+ compile group: 'com.sun.jersey.contribs', name: 'jersey-multipart', version: '1.19.3'
+ compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.9.1'
+ compile group: 'log4j', name: 'log4j', version: '1.2.17'
}
dependencyManagement {
diff --git a/backend/src/main/java/com/supwisdom/dlpay/portal/dao/impl/ArticleRepositoryImpl.java b/backend/src/main/java/com/supwisdom/dlpay/portal/dao/impl/ArticleRepositoryImpl.java
new file mode 100644
index 0000000..a4bafe4
--- /dev/null
+++ b/backend/src/main/java/com/supwisdom/dlpay/portal/dao/impl/ArticleRepositoryImpl.java
@@ -0,0 +1,75 @@
+package com.supwisdom.dlpay.portal.dao.impl;
+
+import com.supwisdom.dlpay.framework.jpa.BaseRepository;
+import com.supwisdom.dlpay.framework.jpa.Finder;
+import com.supwisdom.dlpay.framework.jpa.page.Pagination;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.portal.bean.ArticleSearchBean;
+import com.supwisdom.dlpay.portal.dao.ArticleRepository;
+import com.supwisdom.dlpay.portal.domain.TBArticle;
+import org.hibernate.transform.Transformers;
+import org.jetbrains.annotations.NotNull;
+
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+public class ArticleRepositoryImpl extends BaseRepository implements ArticleRepository {
+ @NotNull
+ @Override
+ public Pagination getArticleList(@NotNull ArticleSearchBean bean) throws ParseException {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
+ StringBuilder sql = new StringBuilder("select a.*,c.name columnname from tb_article a left join tb_column c on a.columnid = c.columnid where 1=1 ");
+ String title = bean.getTitle();
+ String status = bean.getStatus();
+ String savestartdate = bean.getSavestartdate();
+ String saveenddate = bean.getSaveenddate();
+ String releasestartdate = bean.getReleasestartdate();
+ String releaseenddate = bean.getReleaseenddate();
+ int pageno = bean.getPageno();
+ int pagesize = bean.getPagesize();
+ if (!StringUtil.isEmpty(title)) {
+ sql.append(" and a.title like :title");
+ }
+ if (!StringUtil.isEmpty(status)) {
+ sql.append(" and a.status =:status");
+ }
+ if (!StringUtil.isEmpty(releasestartdate)) {
+ sql.append(" and a.releasetime >=:releasestartdate");
+ }
+ if (!StringUtil.isEmpty(releaseenddate)) {
+ sql.append(" and a.releasetime <=:releaseenddate");
+ }
+ if (!StringUtil.isEmpty(savestartdate)) {
+ sql.append(" and a.createtime >=:savestartdate");
+ }
+ if (!StringUtil.isEmpty(saveenddate)) {
+ sql.append(" and a.createtime <=:saveenddate");
+ }
+ sql.append(" order by a.releasetime");
+ Finder f = Finder.create(sql.toString());
+ if (!StringUtil.isEmpty(title)) {
+ f.setParameter("title", "%" + title.trim() + "%");
+ }
+ if (!StringUtil.isEmpty(status)) {
+ f.setParameter("status", status);
+ }
+ if (!StringUtil.isEmpty(releasestartdate)) {
+ Timestamp timestamp = new Timestamp(sdf.parse(releasestartdate+"000000").getTime());
+ f.setParameter("releasestartdate", timestamp);
+ }
+ if (!StringUtil.isEmpty(releaseenddate)) {
+ Timestamp timestamp = new Timestamp(sdf.parse(releaseenddate+"235959").getTime());
+ f.setParameter("releaseenddate", timestamp);
+ }
+ if (!StringUtil.isEmpty(savestartdate)) {
+ Timestamp timestamp = new Timestamp(sdf.parse(savestartdate+"000000").getTime());
+ f.setParameter("savestartdate", timestamp);
+ }
+ if (!StringUtil.isEmpty(saveenddate)) {
+ Timestamp timestamp = new Timestamp(sdf.parse(saveenddate+"235959").getTime());
+ f.setParameter("saveenddate", timestamp);
+ }
+ return findNative(f, Transformers.aliasToBean(TBArticle.class), pageno, pagesize);
+ }
+}
diff --git a/backend/src/main/java/com/supwisdom/dlpay/portal/dao/impl/ColumnRepositoryImpl.java b/backend/src/main/java/com/supwisdom/dlpay/portal/dao/impl/ColumnRepositoryImpl.java
new file mode 100644
index 0000000..54661d7
--- /dev/null
+++ b/backend/src/main/java/com/supwisdom/dlpay/portal/dao/impl/ColumnRepositoryImpl.java
@@ -0,0 +1,38 @@
+package com.supwisdom.dlpay.portal.dao.impl;
+
+import com.supwisdom.dlpay.framework.jpa.BaseRepository;
+import com.supwisdom.dlpay.framework.jpa.Finder;
+import com.supwisdom.dlpay.framework.jpa.page.Pagination;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import com.supwisdom.dlpay.portal.bean.ColumnSearchBean;
+import com.supwisdom.dlpay.portal.dao.ColumnRepository;
+import com.supwisdom.dlpay.portal.domain.TBColumn;
+import org.hibernate.transform.Transformers;
+import org.jetbrains.annotations.NotNull;
+
+public class ColumnRepositoryImpl extends BaseRepository implements ColumnRepository {
+ @NotNull
+ @Override
+ public Pagination getColumnList(@NotNull ColumnSearchBean bean) {
+ StringBuilder sql = new StringBuilder("select s.*,p.name parentname from tb_column s left join tb_column p on s.parentid = p.columnid where 1=1 ");
+ String name = bean.getName();
+ String code = bean.getCode();
+ int pageno = bean.getPageno();
+ int pagesize = bean.getPagesize();
+ if (!StringUtil.isEmpty(name)) {
+ sql.append(" and s.name like :name");
+ }
+ if (!StringUtil.isEmpty(code)) {
+ sql.append(" and s.code like :code");
+ }
+ sql.append(" order by s.isleaf,s.ordernum");
+ Finder f = Finder.create(sql.toString());
+ if (!StringUtil.isEmpty(name)) {
+ f.setParameter("name", "%" + name.trim() + "%");
+ }
+ if (!StringUtil.isEmpty(code)) {
+ f.setParameter("code", "%" + code.trim() + "%");
+ }
+ return findNative(f, Transformers.aliasToBean(TBColumn.class), pageno, pagesize);
+ }
+}
diff --git a/backend/src/main/java/com/supwisdom/dlpay/portal/domain/TBArticle.java b/backend/src/main/java/com/supwisdom/dlpay/portal/domain/TBArticle.java
new file mode 100644
index 0000000..e03bf41
--- /dev/null
+++ b/backend/src/main/java/com/supwisdom/dlpay/portal/domain/TBArticle.java
@@ -0,0 +1,185 @@
+package com.supwisdom.dlpay.portal.domain;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.*;
+import java.sql.Timestamp;
+
+
+@Entity
+@Table(name = "tb_article", indexes = {
+ @Index(name = "article_idx1", columnList = "articleno", unique = true),
+ @Index(name = "article_idx2", columnList = "title"),
+ @Index(name = "article_idx3", columnList = "releasetime,createtime,status")
+})
+public class TBArticle {
+ @Id
+ @GenericGenerator(name = "idGenerator", strategy = "uuid")
+ @GeneratedValue(generator = "idGenerator")
+ @Column(name = "articleid", nullable = false, length = 32)
+ private String articleid;
+
+ @Column(name = "columnid", nullable = false, length = 32)
+ private String columnid;
+
+ @Column(name = "articleno", unique = true, nullable = false, length = 20)
+ private String articleno;
+
+ @Column(name = "title", nullable = false, length = 30)
+ private String title;
+
+ @Lob
+ @Column(name = "content", columnDefinition = "TEXT")
+ private String content;
+
+ @Column(name = "createtime")
+ private Timestamp createtime;
+
+ @Column(name = "updatetime")
+ private Timestamp updatetime;
+
+ @Column(name = "releasetime")
+ private Timestamp releasetime;
+
+ @Column(name = "operid", length = 32)
+ private String operid;
+
+ @Column(name = "hits")
+ private Integer hits;
+
+ @Column(name = "islink", length = 1)
+ private String islink;
+
+ @Column(name = "isdisplay", length = 1)
+ private String isdisplay;
+
+ @Column(name = "isdelete", length = 1)
+ private String isdelete;
+
+ @Column(name = "status", length = 20)
+ private String status;
+
+ @Transient
+ private String columnname;
+
+
+ public String getArticleid() {
+ return articleid;
+ }
+
+ public void setArticleid(String articleid) {
+ this.articleid = articleid;
+ }
+
+ public String getColumnid() {
+ return columnid;
+ }
+
+ public void setColumnid(String columnid) {
+ this.columnid = columnid;
+ }
+
+ public String getArticleno() {
+ return articleno;
+ }
+
+ public void setArticleno(String articleno) {
+ this.articleno = articleno;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public Timestamp getCreatetime() {
+ return createtime;
+ }
+
+ public void setCreatetime(Timestamp createtime) {
+ this.createtime = createtime;
+ }
+
+ public Timestamp getUpdatetime() {
+ return updatetime;
+ }
+
+ public void setUpdatetime(Timestamp updatetime) {
+ this.updatetime = updatetime;
+ }
+
+ public Timestamp getReleasetime() {
+ return releasetime;
+ }
+
+ public void setReleasetime(Timestamp releasetime) {
+ this.releasetime = releasetime;
+ }
+
+ public String getOperid() {
+ return operid;
+ }
+
+ public void setOperid(String operid) {
+ this.operid = operid;
+ }
+
+ public Integer getHits() {
+ return hits;
+ }
+
+ public void setHits(Integer hits) {
+ this.hits = hits;
+ }
+
+ public String getIslink() {
+ return islink;
+ }
+
+ public void setIslink(String islink) {
+ this.islink = islink;
+ }
+
+ public String getIsdisplay() {
+ return isdisplay;
+ }
+
+ public void setIsdisplay(String isdisplay) {
+ this.isdisplay = isdisplay;
+ }
+
+ public String getIsdelete() {
+ return isdelete;
+ }
+
+ public void setIsdelete(String isdelete) {
+ this.isdelete = isdelete;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getColumnname() {
+ return columnname;
+ }
+
+ public void setColumnname(String columnname) {
+ this.columnname = columnname;
+ }
+}
diff --git a/backend/src/main/java/com/supwisdom/dlpay/portal/domain/TBColumn.java b/backend/src/main/java/com/supwisdom/dlpay/portal/domain/TBColumn.java
new file mode 100644
index 0000000..500ac27
--- /dev/null
+++ b/backend/src/main/java/com/supwisdom/dlpay/portal/domain/TBColumn.java
@@ -0,0 +1,131 @@
+package com.supwisdom.dlpay.portal.domain;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "tb_column")
+public class TBColumn {
+ @Id
+ @GenericGenerator(name = "idGenerator", strategy = "uuid")
+ @GeneratedValue(generator = "idGenerator")
+ @Column(name = "columnid", nullable = false, length = 32)
+ private String columnid;
+
+ @Column(name = "parentid", length = 32)
+ private String parentid;
+
+ @Column(name = "isleaf", length = 1,nullable = false)
+ private String isleaf;
+
+ @Column(name = "name", length = 30,unique = true,nullable = false)
+ private String name;
+
+ /**
+ * 是否需要审核后才能发布
+ */
+ @Column(name = "needreview", length = 1,nullable = false)
+ private String needreview;
+
+ /**
+ * 是否公开:游客登录只能查看公开栏目下的文章
+ */
+ @Column(name = "ispublic", length = 1,nullable = false)
+ private String ispublic;
+
+ /**
+ * 是否能发布:该栏目下的文章是否能发布
+ */
+ @Column(name = "publishable", length = 1,nullable = false)
+ private String publishable;
+
+ @Column(name = "code", length = 30,unique = true,nullable = false)
+ private String code;
+
+ @Column(name = "ordernum")
+ private Integer ordernum;
+
+ @Transient
+ private String parentname;
+
+ public String getColumnid() {
+ return columnid;
+ }
+
+ public void setColumnid(String columnid) {
+ this.columnid = columnid;
+ }
+
+ public String getParentid() {
+ return parentid;
+ }
+
+ public void setParentid(String parentid) {
+ this.parentid = parentid;
+ }
+
+ public String getIsleaf() {
+ return isleaf;
+ }
+
+ public void setIsleaf(String isleaf) {
+ this.isleaf = isleaf;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNeedreview() {
+ return needreview;
+ }
+
+ public void setNeedreview(String needreview) {
+ this.needreview = needreview;
+ }
+
+ public String getIspublic() {
+ return ispublic;
+ }
+
+ public void setIspublic(String ispublic) {
+ this.ispublic = ispublic;
+ }
+
+ public String getPublishable() {
+ return publishable;
+ }
+
+ public void setPublishable(String publishable) {
+ this.publishable = publishable;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getParentname() {
+ return parentname;
+ }
+
+ public void setParentname(String parentname) {
+ this.parentname = parentname;
+ }
+
+ public Integer getOrdernum() {
+ return ordernum;
+ }
+
+ public void setOrdernum(Integer ordernum) {
+ this.ordernum = ordernum;
+ }
+}
diff --git a/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java b/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java
index 4de5a35..fe195fd 100644
--- a/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java
+++ b/backend/src/main/java/com/supwisdom/dlpay/portal/util/PortalConstant.java
@@ -1,8 +1,17 @@
package com.supwisdom.dlpay.portal.util;
-public class PortalConstant{
+public class PortalConstant {
public static final String YES = "1";
public static final String NO = "0";
public static final String SYSPARA_IMAGESERVER_URL = "imageserver.url.image";
+ public static final String SYSPARA_IMAGE_MAXSIZE = "imagemaxsize";
+ public static final String SYSPARA_IMAGE_MINISIZE = "minimagesize";
+ public static final String SYSPARA_IMAGE_URLASSIGN = "imageserver.url.assign";
+ public static final String SYSPARA_IMAGE_URLPUSH = "imageserver.url.push";
+ public static final String SYSPARA_ARTICLE_CURRENTNO = "article.currentno";
+
+ public static final String ARTICLE_STATUS_SAVE = "save";
+ public static final String ARTICLE_STATUS_RELEASED = "released";
+ public static final String ARTICLE_STATUS_REVIEW = "review";
}
diff --git a/backend/src/main/java/com/supwisdom/dlpay/portal/util/PutImageToServer.java b/backend/src/main/java/com/supwisdom/dlpay/portal/util/PutImageToServer.java
new file mode 100644
index 0000000..c43b076
--- /dev/null
+++ b/backend/src/main/java/com/supwisdom/dlpay/portal/util/PutImageToServer.java
@@ -0,0 +1,161 @@
+package com.supwisdom.dlpay.portal.util;
+
+import com.google.gson.Gson;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.multipart.FormDataBodyPart;
+import com.sun.jersey.multipart.FormDataMultiPart;
+import com.sun.jersey.multipart.file.StreamDataBodyPart;
+import com.sun.jersey.multipart.impl.MultiPartWriter;
+import com.supwisdom.dlpay.framework.util.StringUtil;
+import org.apache.log4j.Logger;
+
+import javax.ws.rs.core.MediaType;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+/**
+ * Created by jov on 2017/1/10.
+ */
+public class PutImageToServer {
+ public static Gson gson = new Gson();
+ private static Logger logger = Logger.getLogger(PutImageToServer.class);
+
+ public static boolean postImage(String url, InputStream inputStream) {
+ try {
+ FormDataMultiPart part = new FormDataMultiPart();
+ part.bodyPart(new StreamDataBodyPart("files", inputStream));
+ part.bodyPart(new FormDataBodyPart("cm","true"));
+ ClientConfig cc = new DefaultClientConfig();
+ cc.getClasses().add(MultiPartWriter.class);
+ Client c = Client.create(cc);
+ c.setConnectTimeout(10 * 1000);
+ WebResource resource = c.resource(url);
+ ClientResponse response = resource.type(MediaType.MULTIPART_FORM_DATA_TYPE).post(ClientResponse.class, part);
+ logger.error("code=" + response.getStatus() + ",ret=" + response.getEntity(String.class));
+ if (response.getStatus() / 100 == 2) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (Exception e) {
+ logger.error("error:",e);
+ return false;
+ }
+ }
+
+ public static ImageBean postBefore(String url) {
+ try {
+ Client c = Client.create();
+ c.setConnectTimeout(10 * 1000);
+ WebResource resource = c.resource(url);
+ ClientResponse response = resource.get(ClientResponse.class);
+ if (response.getStatus() == 200) {
+ String ret = response.getEntity(String.class);
+ ImageBean imageBean = gson.fromJson(ret, ImageBean.class);
+ return imageBean;
+ } else {
+ logger.error("code=" + response.getStatus() + ",ret=" + response.getEntity(String.class));
+ return null;
+ }
+ } catch (Exception e) {
+ logger.error("error:",e);
+ return null;
+ }
+ }
+
+ public static boolean downloadFileFromUrl(String img, File file) {
+ if (StringUtil.isEmpty(img) || file == null) {
+ return false;
+ }
+ if (!file.exists()) {
+ file.mkdir();
+ }
+ try {
+ URL url = new URL(img);
+ DataInputStream dataInputStream = new DataInputStream(url.openStream());
+ FileOutputStream fileOutputStream = new FileOutputStream(file);
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = dataInputStream.read(buffer)) > 0) {
+ fileOutputStream.write(buffer, 0, length);
+ }
+ dataInputStream.close();
+ fileOutputStream.close();
+ return true;
+ } catch (MalformedURLException e) {
+ logger.error("error:",e);
+ } catch (IOException e) {
+ logger.error("error:",e);
+ }
+ return false;
+ }
+
+ @XmlRootElement
+ public class ImageBean {
+ private String fid;
+ private String url;
+ private String publicUrl;
+ private int count;
+ private List<LocalBean> locations;
+
+ public String getFid() {
+ return fid;
+ }
+
+ public void setFid(String fid) {
+ this.fid = fid;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getPublicUrl() {
+ return publicUrl;
+ }
+
+ public void setPublicUrl(String publicUrl) {
+ this.publicUrl = publicUrl;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public List<LocalBean> getLocations() {
+ return locations;
+ }
+
+ public void setLocations(List<LocalBean> locations) {
+ this.locations = locations;
+ }
+ }
+
+ @XmlRootElement
+ public class LocalBean {
+ private String publicUrl;
+
+ public String getPublicUrl() {
+ return publicUrl;
+ }
+
+ public void setPublicUrl(String publicUrl) {
+ this.publicUrl = publicUrl;
+ }
+ }
+}
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/PortalApi.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/PortalApi.kt
index 089c1a5..43bc662 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/PortalApi.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/PortalApi.kt
@@ -7,12 +7,13 @@
import com.supwisdom.dlpay.framework.service.OperatorDetailService
import com.supwisdom.dlpay.framework.service.SystemUtilService
import com.supwisdom.dlpay.framework.util.StringUtil
-import com.supwisdom.dlpay.mobile.domain.TBMsg
-import com.supwisdom.dlpay.portal.bean.FeedbackSearchBean
-import com.supwisdom.dlpay.portal.bean.MsgTemplateSearchBean
-import com.supwisdom.dlpay.portal.bean.SendMsgBean
+import com.supwisdom.dlpay.portal.bean.*
+import com.supwisdom.dlpay.portal.domain.TBArticle
+import com.supwisdom.dlpay.portal.domain.TBColumn
import com.supwisdom.dlpay.portal.domain.TBMsgTemplate
import com.supwisdom.dlpay.portal.domain.TBReply
+import com.supwisdom.dlpay.portal.service.ArticleService
+import com.supwisdom.dlpay.portal.service.ColumnService
import com.supwisdom.dlpay.portal.service.FeedbackService
import com.supwisdom.dlpay.portal.service.MsgService
import com.supwisdom.dlpay.portal.util.PortalConstant
@@ -23,6 +24,8 @@
import org.springframework.http.ResponseEntity
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.bind.annotation.*
+import org.springframework.web.multipart.MultipartHttpServletRequest
+import java.util.regex.Pattern
@RestController
@RequestMapping("/portalapi")
@@ -39,6 +42,10 @@
lateinit var systemUtilService: SystemUtilService
@Autowired
lateinit var msgService: MsgService
+ @Autowired
+ lateinit var articleService: ArticleService
+ @Autowired
+ lateinit var columnService: ColumnService
val logger = KotlinLogging.logger { }
@RequestMapping("/test")
@@ -150,8 +157,8 @@
}
}
- @RequestMapping(value = ["/template/save"],method = [RequestMethod.POST])
- fun saveMsgTemplate(@RequestBody template:TBMsgTemplate):JsonResult?{
+ @RequestMapping(value = ["/template/save"], method = [RequestMethod.POST])
+ fun saveMsgTemplate(@RequestBody template: TBMsgTemplate): JsonResult? {
return try {
val p = SecurityContextHolder.getContext().authentication
val oper = operatorDetailService.findByOperid(p.name)
@@ -164,8 +171,8 @@
}
}
- @RequestMapping(value = ["/template/sendmsg"],method = [RequestMethod.POST])
- fun sendMsg(@RequestBody bean: SendMsgBean):JsonResult?{
+ @RequestMapping(value = ["/template/sendmsg"], method = [RequestMethod.POST])
+ fun sendMsg(@RequestBody bean: SendMsgBean): JsonResult? {
return try {
msgService.sendMsg(bean)
return JsonResult.ok()
@@ -174,4 +181,86 @@
JsonResult.error(e.message)
}
}
+
+ @RequestMapping(value = ["/article/uploadpic"], method = [RequestMethod.POST])
+ fun uploadPic(request: MultipartHttpServletRequest): JsonResult? {
+ val map = articleService.uploadPic(request)
+ return JsonResult.ok()
+ }
+
+ @RequestMapping("/column/all")
+ fun getAllColumn(): JsonResult? {
+ return try {
+ val list = columnService.getAllColumnList()
+ if (list == null || list.isEmpty()) {
+ return JsonResult.ok().put("msg", "无数据")
+ }
+ return JsonResult.ok().put("list", list)
+ } catch (e: Exception) {
+ logger.error { e.message }
+ JsonResult.error("查询所有栏目列表异常")
+ }
+ }
+
+ @RequestMapping("/column/list")
+ fun getColumnList(bean: ColumnSearchBean): JsonResult? {
+ return try {
+ val page = columnService.getColumnList(bean)
+ if (page.list == null || page.list.size == 0) {
+ return JsonResult.ok().put("msg", "无数据")
+ }
+ return JsonResult.ok().put("page", page)
+ } catch (e: Exception) {
+ logger.error { e.message }
+ JsonResult.error("查询栏目列表异常")
+ }
+ }
+
+ @RequestMapping(value = ["/column/save"], method = [RequestMethod.POST])
+ fun saveColumn(@RequestBody column: TBColumn): JsonResult? {
+ return try {
+ val matches = Pattern.matches("^[a-zA-Z0-9]{1,30}$", column.code)
+ if (!matches) {
+ return JsonResult.error("请输入合法的栏目CODE!")
+ }
+ val msg = columnService.saveColumn(column)
+ return if (StringUtil.isEmpty(msg)) {
+ JsonResult.ok()
+ } else {
+ JsonResult.error(msg)
+ }
+ } catch (e: Exception) {
+ logger.error { e.message }
+ JsonResult.error("保存栏目异常")
+ }
+ }
+
+ @RequestMapping(value = ["/article/save"], method = [RequestMethod.POST])
+ fun saveArticle(@RequestBody article: TBArticle): JsonResult? {
+ return try {
+ val p = SecurityContextHolder.getContext().authentication
+ val oper = operatorDetailService.findByOperid(p.name)
+ article.operid = oper.operid
+ articleService.saveArticle(article)
+ JsonResult.ok()
+ } catch (e: Exception) {
+ logger.error { e.message }
+ JsonResult.error("保存文章异常")
+ }
+ }
+
+ @RequestMapping("/article/list")
+ fun getArticleList(bean: ArticleSearchBean): JsonResult? {
+ return try {
+ val page = articleService.getArticleList(bean)
+ if (page.list == null || page.list.size == 0) {
+ return JsonResult.ok().put("msg", "无数据")
+ }
+ return JsonResult.ok().put("page", page)
+ } catch (e: Exception) {
+ logger.error { e.message }
+ JsonResult.error("查询文章列表异常")
+ }
+ }
+
}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/bean/ArticleSearchBean.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/bean/ArticleSearchBean.kt
new file mode 100644
index 0000000..322bb0e
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/bean/ArticleSearchBean.kt
@@ -0,0 +1,12 @@
+package com.supwisdom.dlpay.portal.bean
+
+class ArticleSearchBean {
+ var title: String = ""
+ var status: String = ""
+ var savestartdate: String = ""
+ var saveenddate: String = ""
+ var releasestartdate: String = ""
+ var releaseenddate: String = ""
+ var pageno: Int = 0
+ var pagesize: Int = 10
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/bean/ColumnSearchBean.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/bean/ColumnSearchBean.kt
new file mode 100644
index 0000000..c4cbfbf
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/bean/ColumnSearchBean.kt
@@ -0,0 +1,8 @@
+package com.supwisdom.dlpay.portal.bean
+
+class ColumnSearchBean {
+ var name: String = ""
+ var code: String = ""
+ var pageno: Int = 0
+ var pagesize: Int = 10
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/AnnexDao.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/AnnexDao.kt
index 59968d8..eb91d8f 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/AnnexDao.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/AnnexDao.kt
@@ -5,6 +5,6 @@
import org.springframework.stereotype.Repository
@Repository
-interface AnnexDao : JpaRepository<TBAnnex, String>, FeedbackRepository {
+interface AnnexDao : JpaRepository<TBAnnex, String> {
fun getByFbid(fbid: String): List<TBAnnex>
}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ArticleDao.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ArticleDao.kt
new file mode 100644
index 0000000..6f89cc4
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ArticleDao.kt
@@ -0,0 +1,10 @@
+package com.supwisdom.dlpay.portal.dao
+
+import com.supwisdom.dlpay.portal.domain.TBArticle
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+@Repository
+interface ArticleDao:JpaRepository<TBArticle,String>,ArticleRepository{
+
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ArticleRepository.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ArticleRepository.kt
new file mode 100644
index 0000000..9c27094
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ArticleRepository.kt
@@ -0,0 +1,10 @@
+package com.supwisdom.dlpay.portal.dao
+
+import com.supwisdom.dlpay.framework.jpa.page.Pagination
+import com.supwisdom.dlpay.portal.bean.ArticleSearchBean
+import java.text.ParseException
+
+interface ArticleRepository {
+ @Throws(ParseException::class)
+ fun getArticleList(bean:ArticleSearchBean): Pagination
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ColumnDao.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ColumnDao.kt
new file mode 100644
index 0000000..fa53b3c
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ColumnDao.kt
@@ -0,0 +1,11 @@
+package com.supwisdom.dlpay.portal.dao
+
+import com.supwisdom.dlpay.portal.domain.TBColumn
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.stereotype.Repository
+
+@Repository
+interface ColumnDao: JpaRepository<TBColumn, String>,ColumnRepository {
+ fun findByNameAndColumnidNot(name: String,columnid:String): TBColumn?
+ fun findByCodeAndColumnidNot(code:String,columnid:String): TBColumn?
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ColumnRepository.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ColumnRepository.kt
new file mode 100644
index 0000000..4a3c44f
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/dao/ColumnRepository.kt
@@ -0,0 +1,8 @@
+package com.supwisdom.dlpay.portal.dao
+
+import com.supwisdom.dlpay.framework.jpa.page.Pagination
+import com.supwisdom.dlpay.portal.bean.ColumnSearchBean
+
+interface ColumnRepository {
+ fun getColumnList(bean: ColumnSearchBean): Pagination
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/ArticleService.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/ArticleService.kt
new file mode 100644
index 0000000..3488b2f
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/ArticleService.kt
@@ -0,0 +1,16 @@
+package com.supwisdom.dlpay.portal.service
+
+import com.supwisdom.dlpay.framework.jpa.page.Pagination
+import com.supwisdom.dlpay.portal.bean.ArticleSearchBean
+import com.supwisdom.dlpay.portal.domain.TBArticle
+import org.springframework.transaction.annotation.Transactional
+import org.springframework.web.multipart.MultipartHttpServletRequest
+
+interface ArticleService {
+ @Transactional
+ fun uploadPic(request: MultipartHttpServletRequest): Map<String, String>
+ @Transactional
+ fun saveArticle(article: TBArticle)
+ @Transactional
+ fun getArticleList(bean:ArticleSearchBean): Pagination
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/ColumnService.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/ColumnService.kt
new file mode 100644
index 0000000..ea3db3e
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/ColumnService.kt
@@ -0,0 +1,15 @@
+package com.supwisdom.dlpay.portal.service
+
+import com.supwisdom.dlpay.framework.jpa.page.Pagination
+import com.supwisdom.dlpay.portal.bean.ColumnSearchBean
+import com.supwisdom.dlpay.portal.domain.TBColumn
+import org.springframework.transaction.annotation.Transactional
+
+interface ColumnService {
+ @Transactional
+ fun saveColumn(column:TBColumn):String?
+ @Transactional
+ fun getColumnList(bean: ColumnSearchBean): Pagination
+ @Transactional
+ fun getAllColumnList():List<TBColumn>?
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/FeedbackService.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/FeedbackService.kt
index 005de34..2b8e221 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/FeedbackService.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/FeedbackService.kt
@@ -3,9 +3,13 @@
import com.supwisdom.dlpay.framework.jpa.page.Pagination
import com.supwisdom.dlpay.portal.bean.FeedbackSearchBean
import com.supwisdom.dlpay.portal.domain.TBReply
+import org.springframework.transaction.annotation.Transactional
interface FeedbackService {
+ @Transactional
fun getFeedbackList(bean: FeedbackSearchBean): Pagination
+ @Transactional
fun getReplyListByFbId(fbId: String): List<TBReply>
+ @Transactional
fun saveReplyListByFbId(reply: TBReply):String?
}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ArticleServiceImpl.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ArticleServiceImpl.kt
new file mode 100644
index 0000000..986639b
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ArticleServiceImpl.kt
@@ -0,0 +1,237 @@
+package com.supwisdom.dlpay.portal.service.Impl
+
+import com.supwisdom.dlpay.framework.dao.BusinessparaDao
+import com.supwisdom.dlpay.framework.jpa.page.Pagination
+import com.supwisdom.dlpay.framework.service.SystemUtilService
+import com.supwisdom.dlpay.framework.util.StringUtil
+import com.supwisdom.dlpay.portal.bean.ArticleSearchBean
+import com.supwisdom.dlpay.portal.dao.ArticleDao
+import com.supwisdom.dlpay.portal.domain.TBArticle
+import com.supwisdom.dlpay.portal.service.ArticleService
+import com.supwisdom.dlpay.portal.util.PortalConstant
+import com.supwisdom.dlpay.portal.util.PutImageToServer
+import mu.KotlinLogging
+import net.coobird.thumbnailator.Thumbnails
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+import org.springframework.web.multipart.MultipartFile
+import org.springframework.web.multipart.MultipartHttpServletRequest
+import java.io.File
+import java.io.FileInputStream
+import java.io.IOException
+import java.lang.RuntimeException
+import java.util.*
+import kotlin.math.sqrt
+
+@Service
+class ArticleServiceImpl : ArticleService {
+ @Autowired
+ lateinit var systemUtilService: SystemUtilService
+ @Autowired
+ lateinit var articleDao: ArticleDao
+ @Autowired
+ lateinit var businessparaDao: BusinessparaDao
+
+ val logger = KotlinLogging.logger { }
+
+ override fun saveArticle(article: TBArticle) {
+ val timestamp = systemUtilService.sysdatetime.sysdate
+ val currentNoBusiness = businessparaDao.findByParakey(PortalConstant.SYSPARA_ARTICLE_CURRENTNO)
+ ?: throw RuntimeException("文章编号参数未配置")
+ val currentNo = currentNoBusiness.paraval.toInt()
+ currentNoBusiness.paraval = (currentNo + 1).toString()
+ businessparaDao.save(currentNoBusiness)
+ if (StringUtil.isEmpty(article.articleid)) {
+ article.createtime = timestamp
+ article.articleno = String.format("%08d", currentNo)
+ article.hits = 0
+ article.isdisplay = PortalConstant.YES
+ article.isdelete = PortalConstant.NO
+ article.status = PortalConstant.ARTICLE_STATUS_SAVE
+ }else{
+ article.updatetime = timestamp
+ }
+ articleDao.save(article)
+ }
+
+ override fun getArticleList(bean: ArticleSearchBean): Pagination {
+ return articleDao.getArticleList(bean)
+ }
+
+ override fun uploadPic(request: MultipartHttpServletRequest): Map<String, String> {
+ val map: MutableMap<String, String> = HashMap()
+ try {
+ logger.error("===put up pic")
+ val pathDir = "/files/"
+ /** 得到图片保存目录的真实路径 */
+ val logoRealPathDir = request.session.servletContext
+ .getRealPath(pathDir)
+ /** 根据真实路径创建目录 */
+ val logoSaveFile = File(logoRealPathDir)
+ if (!logoSaveFile.exists()) logoSaveFile.mkdirs()
+ /** 页面控件的文件流 */
+ val multipartFile: MultipartFile
+ multipartFile = request.getFile("file")
+ //System.out.println(multipartFile);
+ /* 获取文件的后缀 */
+ val suffix = multipartFile.originalFilename.substring(
+ multipartFile.originalFilename.lastIndexOf("."))
+ //* 拼成完整的文件保存路径加文件 *//*
+ val name = System.currentTimeMillis().toString() + suffix
+ val fileName = logoRealPathDir + File.separator + name
+ val file = File(fileName)
+ // String data = file.getPath();
+ try {
+ multipartFile.transferTo(file)
+ } catch (e: IllegalStateException) {
+ logger.error("error:", e)
+ } catch (e: IOException) {
+ logger.error("error:", e)
+ }
+ //savePicToDb(fileName, goodsidString);*/
+ //String filepath = logoRealPathDir + File.separator + multipartFile.getOriginalFilename();
+ val size = multipartFile.size
+ var maxSize = 200 * 1024.toLong()
+ val imageMaxConf = systemUtilService.getBusinessValue(PortalConstant.SYSPARA_IMAGE_MAXSIZE)
+ if (imageMaxConf != null && !StringUtil.isEmpty(imageMaxConf)) {
+ try {
+ maxSize = imageMaxConf.toLong()
+ } catch (e: Exception) {
+ }
+ }
+ var minwidth = 150
+ var minheight = 150
+ val imageMinConf = systemUtilService.getBusinessValue(PortalConstant.SYSPARA_IMAGE_MINISIZE)
+ if (imageMinConf != null && !StringUtil.isEmpty(imageMinConf)) {
+ try {
+ val temps = imageMinConf.split(",")
+ if (temps.size == 2) {
+ minwidth = temps[0].toInt()
+ minheight = temps[1].toInt()
+ }
+ } catch (e: Exception) {
+ logger.error("error:", e)
+ }
+ }
+ logger.error("===put up pic 1")
+ var baseURL = systemUtilService.getBusinessValue(PortalConstant.SYSPARA_IMAGE_URLASSIGN)
+ if (baseURL == null || StringUtil.isEmpty(baseURL)) {
+ map["retmsg"] = "系统未配置图片服务器地址,无法保存图片!"
+ map["retcode"] = "1"
+ return map
+ }
+ if (!baseURL.startsWith("http")) {
+ baseURL = "http://$baseURL"
+ }
+ if (baseURL.endsWith("/")) {
+ baseURL = baseURL.substring(0, baseURL.length - 1)
+ }
+ logger.error("===put up pic 2")
+ var minImageURL: String
+ val picid: String
+ val minpicid: String
+ var imageURL = systemUtilService.getBusinessValue(PortalConstant.SYSPARA_IMAGE_URLPUSH)
+ if (imageURL == null || StringUtil.isEmpty(imageURL)) {
+ map["retmsg"] = "系统未配置图片服务器地址,无法保存图片!"
+ map["retcode"] = "1"
+ return map
+ }
+ logger.error("===put up pic 4")
+ if (!imageURL.startsWith("http")) {
+ imageURL = "http://$imageURL"
+ }
+ if (!imageURL.endsWith("/")) {
+ imageURL += "/"
+ }
+ minImageURL = imageURL
+ if (!minImageURL.startsWith("http")) {
+ minImageURL = "http://$minImageURL"
+ }
+ if (!minImageURL.endsWith("/")) {
+ minImageURL += "/"
+ }
+ val assign = "$baseURL/dir/assign?count=2"
+ val bean = PutImageToServer.postBefore(assign)
+ if (bean == null || StringUtil.isEmpty(bean.getFid())) {
+ map["retmsg"] = "图片上传失败,无法连接图片服务器!"
+ map["retcode"] = "1"
+ return map
+ }
+ imageURL += bean.fid
+ picid = bean.fid
+ val minImgBean = PutImageToServer.postBefore(assign)
+ if (minImgBean == null || StringUtil.isEmpty(minImgBean.getFid())) {
+ map["retmsg"] = "图片上传失败,无法连接图片服务器!"
+ map["retcode"] = "1"
+ return map
+ }
+ minpicid = minImgBean.fid
+ minImageURL += minImgBean.fid
+ //}
+ val processToFile = logoRealPathDir + File.separator + picid + ".jpg"
+ var scale = 1.0
+ if (size > 0 && size > maxSize) {
+ scale = maxSize * 1.0 / size
+ }
+ try {
+ if (scale < 0.7) {
+ val s = sqrt(maxSize.toDouble()).toInt()
+ Thumbnails.of(fileName).size(s, s).outputFormat("jpg").toFile(processToFile)
+ } else {
+ Thumbnails.of(fileName).scale(1.0).outputQuality(scale).outputFormat("jpg").toFile(processToFile)
+ }
+ logger.error("===put up pic 5")
+ } catch (e: Exception) {
+ map["retmsg"] = "图片上传失败,压缩失败"
+ map["retcode"] = "1"
+ file.delete()
+ return map
+ }
+ val file1 = File(processToFile)
+ val processToMinFile = logoRealPathDir + File.separator + minpicid + ".jpg"
+ try {
+ Thumbnails.of(fileName).size(minwidth, minheight).outputFormat("jpg").toFile(processToMinFile)
+ } catch (e: Exception) {
+ map["retmsg"] = "图片上传失败,压缩失败"
+ map["retcode"] = "1"
+ file.delete()
+ file1.delete()
+ return map
+ }
+ val file2 = File(processToMinFile)
+ logger.error("==========imageurl=$imageURL")
+ if (!PutImageToServer.postImage(imageURL, FileInputStream(file1))) {
+ map["retmsg"] = "图片上传失败,请稍后再试"
+ map["retcode"] = "1"
+ file1.delete()
+ file2.delete()
+ file.delete()
+ logger.error("=step1 fail=")
+ return map
+ }
+ if (!PutImageToServer.postImage(minImageURL, FileInputStream(file2))) {
+ map["retmsg"] = "图片上传失败,请稍后再试"
+ map["retcode"] = "1"
+ file1.delete()
+ file2.delete()
+ file.delete()
+ logger.error("=step2 fail=")
+ } else {
+ map["retmsg"] = "上传成功"
+ map["retcode"] = "0"
+ file1.delete()
+ file2.delete()
+ file.delete()
+ map["picid"] = picid
+ map["minpicid"] = minpicid
+ logger.error("=step3 success=")
+ }
+ } catch (e: Exception) {
+ map["retmsg"] = "图片上传失败--" + e.message
+ map["retcode"] = "1"
+ // return map;
+ logger.error("=step fail=", e)
+ }
+ return map
+ }
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ColumnServiceImpl.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ColumnServiceImpl.kt
new file mode 100644
index 0000000..f83e441
--- /dev/null
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/Impl/ColumnServiceImpl.kt
@@ -0,0 +1,48 @@
+package com.supwisdom.dlpay.portal.service.Impl
+
+import com.supwisdom.dlpay.framework.jpa.page.Pagination
+import com.supwisdom.dlpay.framework.util.StringUtil
+import com.supwisdom.dlpay.portal.bean.ColumnSearchBean
+import com.supwisdom.dlpay.portal.dao.ColumnDao
+import com.supwisdom.dlpay.portal.domain.TBColumn
+import com.supwisdom.dlpay.portal.service.ColumnService
+import com.supwisdom.dlpay.portal.util.PortalConstant
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+
+@Service
+class ColumnServiceImpl : ColumnService {
+ @Autowired
+ lateinit var columnDao: ColumnDao
+
+ override fun getColumnList(bean: ColumnSearchBean): Pagination {
+ return columnDao.getColumnList(bean)
+ }
+
+ override fun getAllColumnList(): List<TBColumn>? {
+ return columnDao.findAll()
+ }
+
+ override fun saveColumn(column: TBColumn):String? {
+ var columnid = "####"
+ if (!StringUtil.isEmpty(column.columnid)) {
+ columnid = column.columnid
+ }else{
+ if (StringUtil.isEmpty(column.parentid)) {
+ column.isleaf = PortalConstant.NO
+ } else {
+ column.isleaf = PortalConstant.YES
+ }
+ }
+ val nameColumn = columnDao.findByNameAndColumnidNot(column.name,columnid)
+ if (nameColumn!=null){
+ return "栏目名称已存在,请更换!"
+ }
+ val codeColumn = columnDao.findByCodeAndColumnidNot(column.code,columnid)
+ if (codeColumn!=null){
+ return "栏目CODE已存在,请更换!"
+ }
+ columnDao.save(column)
+ return null
+ }
+}
\ No newline at end of file
diff --git a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/MsgService.kt b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/MsgService.kt
index ebec659..f874828 100644
--- a/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/MsgService.kt
+++ b/backend/src/main/kotlin/com/supwisdom/dlpay/portal/service/MsgService.kt
@@ -4,9 +4,13 @@
import com.supwisdom.dlpay.portal.bean.MsgTemplateSearchBean
import com.supwisdom.dlpay.portal.bean.SendMsgBean
import com.supwisdom.dlpay.portal.domain.TBMsgTemplate
+import org.springframework.transaction.annotation.Transactional
interface MsgService {
+ @Transactional
fun getMsgTemplateList(bean:MsgTemplateSearchBean):Pagination
+ @Transactional
fun saveMsgTemplate(template:TBMsgTemplate)
+ @Transactional
fun sendMsg(bean: SendMsgBean)
}
diff --git a/backend/src/test/java/test.java b/backend/src/test/java/test.java
index 1659da6..e7b34d4 100644
--- a/backend/src/test/java/test.java
+++ b/backend/src/test/java/test.java
@@ -1,3 +1,5 @@
+import org.junit.Test;
+
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
@@ -29,4 +31,9 @@
}
System.out.println(sqlFragment);
}
+
+ @Test
+ public void test() {
+ System.out.println(String.format("%08d",2));
+ }
}
diff --git a/frontend/src/api/article.js b/frontend/src/api/article.js
index 407bda1..a05932e 100644
--- a/frontend/src/api/article.js
+++ b/frontend/src/api/article.js
@@ -1,5 +1,32 @@
import request from '@/utils/request'
+export function uploadImage(data) {
+ return request({
+ url: '/article/uploadpic',
+ method: 'post',
+ headers: {
+ 'content-type': 'multipart/form-data'
+ },
+ data
+ })
+}
+
+export function saveArticle(data) {
+ return request({
+ url: '/article/save',
+ method: 'post',
+ data
+ })
+}
+
+export function getArticleList(query) {
+ return request({
+ url: '/article/list',
+ method: 'get',
+ params: query
+ })
+}
+
export function fetchList(query) {
return request({
url: '/vue-element-admin/article/list',
diff --git a/frontend/src/api/column.js b/frontend/src/api/column.js
new file mode 100644
index 0000000..32616b6
--- /dev/null
+++ b/frontend/src/api/column.js
@@ -0,0 +1,24 @@
+import request from '@/utils/request'
+
+export function getColumnList(query) {
+ return request({
+ url: '/column/list',
+ method: 'get',
+ params: query
+ })
+}
+
+export function getAllColumn() {
+ return request({
+ url: '/column/all',
+ method: 'get'
+ })
+}
+
+export function saveColumnList(data) {
+ return request({
+ url: '/column/save',
+ method: 'post',
+ data
+ })
+}
diff --git a/frontend/src/components/Tinymce/components/EditorImage.vue b/frontend/src/components/Tinymce/components/EditorImage.vue
index 07d48e6..366bb4f 100644
--- a/frontend/src/components/Tinymce/components/EditorImage.vue
+++ b/frontend/src/components/Tinymce/components/EditorImage.vue
@@ -1,7 +1,7 @@
<template>
<div class="upload-container">
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
- upload
+ 上传本地图片
</el-button>
<el-dialog :visible.sync="dialogVisible">
<el-upload
@@ -12,18 +12,19 @@
:on-success="handleSuccess"
:before-upload="beforeUpload"
class="editor-slide-upload"
- action="https://httpbin.org/post"
+ action="#"
+ :http-request="uploadImage"
list-type="picture-card"
>
<el-button size="small" type="primary">
- Click upload
+ 点击上传
</el-button>
</el-upload>
<el-button @click="dialogVisible = false">
- Cancel
+ 取消
</el-button>
<el-button type="primary" @click="handleSubmit">
- Confirm
+ 确认
</el-button>
</el-dialog>
</div>
@@ -31,6 +32,7 @@
<script>
// import { getToken } from 'api/qiniu'
+import { uploadImage } from '@/api/article'
export default {
name: 'EditorSlideUpload',
@@ -44,17 +46,34 @@
return {
dialogVisible: false,
listObj: {},
- fileList: []
+ fileList: [],
+ url: ''
}
},
+ created() {
+ this.url = process.env.VUE_APP_BASE_API
+ },
methods: {
checkAllSuccess() {
return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
},
+ uploadImage(file) {
+ const params = new FormData()
+ params.append('file', file.file)
+ uploadImage(params).then(response => {
+ console.log(response)
+ }).catch(error => {
+ this.$message({
+ message: error.msg || '图片上传异常',
+ type: 'error'
+ })
+ this.listLoading = false
+ })
+ },
handleSubmit() {
const arr = Object.keys(this.listObj).map(v => this.listObj[v])
if (!this.checkAllSuccess()) {
- this.$message('Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!')
+ this.$message('请稍候,图片正在上传。如果网络出现问题, 请刷新页面重新上传!')
return
}
this.$emit('successCBK', arr)
@@ -63,6 +82,7 @@
this.dialogVisible = false
},
handleSuccess(response, file) {
+ console.log(response)
const uid = file.uid
const objKeyArr = Object.keys(this.listObj)
for (let i = 0, len = objKeyArr.length; i < len; i++) {
diff --git a/frontend/src/components/Tinymce/index.vue b/frontend/src/components/Tinymce/index.vue
index cb6b91c..7a9877f 100644
--- a/frontend/src/components/Tinymce/index.vue
+++ b/frontend/src/components/Tinymce/index.vue
@@ -116,7 +116,7 @@
const _this = this
window.tinymce.init({
selector: `#${this.tinymceId}`,
- language: this.languageTypeList['en'],
+ language: this.languageTypeList['zh'],
height: this.height,
body_class: 'panel-body ',
object_resizing: false,
diff --git a/frontend/src/store/modules/permission.js b/frontend/src/store/modules/permission.js
index f1d5583..9e2aa4e 100644
--- a/frontend/src/store/modules/permission.js
+++ b/frontend/src/store/modules/permission.js
@@ -22,9 +22,19 @@
export function generaMenu(routes, data) {
data.forEach(item => {
// alert(JSON.stringify(item))
+ if (item.respath === '/article/list') {
+ const editArticle = {
+ path: '/article/edit/:id(\\d+)',
+ component: (resolve) => require([`@/views/article/edit`], resolve),
+ name: 'EditArticle',
+ meta: { title: '编辑文章', id: '1c039c677ded4f1dab63a4fa9c0a27ca', noCache: true, roles: ['admin'] },
+ hidden: true
+ }
+ routes.push(editArticle)
+ }
const menu = {
path: item.respath === '#' ? item.resid + '_key' : item.respath,
- component: item.respath === '#' ? Layout : (resolve) => require([`@/views${item.respath}/index`], resolve),
+ component: item.respath === '#' ? Layout : (resolve) => require([`@/views${item.respath}`], resolve),
// hidden: true,
children: [],
name: 'menu_' + item.resid,
diff --git a/frontend/src/views/article/components/ArticleDetail.vue b/frontend/src/views/article/components/ArticleDetail.vue
new file mode 100644
index 0000000..f634220
--- /dev/null
+++ b/frontend/src/views/article/components/ArticleDetail.vue
@@ -0,0 +1,248 @@
+<template>
+ <div class="createPost-container">
+ <el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
+ <sticky :z-index="10" :class-name="'sub-navbar '+postForm.status">
+ <el-button v-loading="loading" style="margin-left: 10px;" type="primary" @click="submitForm">
+ 发布
+ </el-button>
+ <el-button v-loading="loading" type="primary" @click="draftForm">
+ 保存
+ </el-button>
+ </sticky>
+
+ <div class="createPost-main-container">
+ <el-row>
+
+ <el-col :span="24">
+ <el-form-item style="margin-bottom: 40px;" prop="title">
+ <MDinput v-model="postForm.title" :maxlength="30" name="name" required>
+ 标题
+ </MDinput>
+ </el-form-item>
+
+ <div class="postInfo-container">
+ <el-row>
+ <el-col :span="8">
+ <el-form-item label-width="100px" prop="columnid" label="所属栏目:" class="postInfo-container-item">
+ <el-select
+ v-model="postForm.columnid"
+ no-data-text="当前无栏目,请先新建栏目"
+ filterable
+ default-first-option
+ placeholder="搜索栏目"
+ >
+ <el-option
+ v-for="(item,index) in columnList"
+ :key="item+index"
+ :label="item.name"
+ :value="item.columnid"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </div>
+ </el-col>
+ </el-row>
+
+ <el-form-item prop="content" style="margin-bottom: 30px;">
+ <Tinymce ref="editor" v-model="postForm.content" :height="400" />
+ </el-form-item>
+ </div>
+ </el-form>
+ </div>
+</template>
+
+<script>
+import Tinymce from '@/components/Tinymce'
+import MDinput from '@/components/MDinput'
+import Sticky from '@/components/Sticky' // 粘性header组件
+import { saveArticle } from '@/api/article'
+import { getAllColumn } from '@/api/column'
+
+const defaultForm = {
+ status: 'draft',
+ title: '', // 文章题目
+ content: '', // 文章内容
+ columnid: '',
+ display_time: undefined, // 前台展示时间
+ articleid: undefined,
+ islink: ''
+}
+
+export default {
+ name: 'ArticleDetail',
+ components: { Tinymce, MDinput, Sticky },
+ props: {
+ isEdit: {
+ type: Boolean,
+ default: false
+ }
+ },
+ data() {
+ return {
+ postForm: Object.assign({}, defaultForm),
+ loading: false,
+ columnList: [],
+ rules: {
+ title: [{ required: true, message: '请输入文章标题', trigger: 'blur' }],
+ columnid: [{ required: true, message: '请选择文章栏目', trigger: 'blur' }],
+ content: [{ required: true, message: '文章内容不能为空', trigger: 'blur' }]
+ },
+ tempRoute: {}
+ }
+ },
+ computed: {
+ contentShortLength() {
+ return this.postForm.content_short.length
+ },
+ lang() {
+ return this.$store.getters.language
+ },
+ displayTime: {
+ // set and get is useful when the data
+ // returned by the back end api is different from the front end
+ // back end return => "2013-06-25 06:59:25"
+ // front end need timestamp => 1372114765000
+ get() {
+ return (+new Date(this.postForm.display_time))
+ },
+ set(val) {
+ this.postForm.display_time = new Date(val)
+ }
+ }
+ },
+ created() {
+ if (this.isEdit) {
+ const id = this.$route.params && this.$route.params.id
+ this.fetchData(id)
+ }
+
+ // Why need to make a copy of this.$route here?
+ // Because if you enter this page and quickly switch tag, may be in the execution of the setTagsViewTitle function, this.$route is no longer pointing to the current page
+ // https://github.com/PanJiaChen/vue-element-admin/issues/1221
+ this.tempRoute = Object.assign({}, this.$route)
+ getAllColumn().then(response => {
+ if (response.list) {
+ this.columnList = response.list
+ } else {
+ this.columnList = []
+ }
+ }).catch(error => {
+ this.$message({
+ message: error.msg || '请求异常',
+ type: 'error'
+ })
+ })
+ },
+ methods: {
+ fetchData(id) {
+ // fetchArticle(id).then(response => {
+ // this.postForm = response.data
+
+ // // just for test
+ // this.postForm.title += ` Article Id:${this.postForm.id}`
+ // this.postForm.content_short += ` Article Id:${this.postForm.id}`
+
+ // // set tagsview title
+ // this.setTagsViewTitle()
+
+ // // set page title
+ // this.setPageTitle()
+ // }).catch(err => {
+ // console.log(err)
+ // })
+ },
+ setTagsViewTitle() {
+ const title = this.lang === 'zh' ? '编辑文章' : 'Edit Article'
+ const route = Object.assign({}, this.tempRoute, { title: `${title}-${this.postForm.id}` })
+ this.$store.dispatch('tagsView/updateVisitedView', route)
+ },
+ setPageTitle() {
+ const title = 'Edit Article'
+ document.title = `${title} - ${this.postForm.id}`
+ },
+ submitForm() {
+ this.$refs.postForm.validate(valid => {
+ if (valid) {
+ this.loading = true
+ this.$notify({
+ title: '成功',
+ message: '发布文章成功',
+ type: 'success',
+ duration: 2000
+ })
+ this.postForm.status = 'published'
+ this.loading = false
+ } else {
+ console.log('error submit!!')
+ return false
+ }
+ })
+ },
+ draftForm() {
+ if (this.postForm.content.length === 0 || this.postForm.title.length === 0) {
+ this.$message({
+ message: '请填写必要的标题和内容',
+ type: 'warning'
+ })
+ return
+ }
+ this.postForm.islink = '0'
+ saveArticle(this.postForm).then(response => {
+ this.$message({
+ message: '保存成功',
+ type: 'success',
+ showClose: true,
+ duration: 1000
+ })
+ }).catch(error => {
+ this.$message({
+ message: error.msg || '请求异常',
+ type: 'error'
+ })
+ })
+ this.postForm.status = 'draft'
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "~@/styles/mixin.scss";
+
+.createPost-container {
+ position: relative;
+
+ .createPost-main-container {
+ padding: 40px 45px 20px 50px;
+
+ .postInfo-container {
+ position: relative;
+ @include clearfix;
+ margin-bottom: 10px;
+
+ .postInfo-container-item {
+ float: left;
+ }
+ }
+ }
+
+ .word-counter {
+ width: 40px;
+ position: absolute;
+ right: 10px;
+ top: 0px;
+ }
+}
+
+.article-textarea ::v-deep {
+ textarea {
+ padding-right: 40px;
+ resize: none;
+ border: none;
+ border-radius: 0px;
+ border-bottom: 1px solid #bfcbd9;
+ }
+}
+</style>
diff --git a/frontend/src/views/article/components/Dropdown/Comment.vue b/frontend/src/views/article/components/Dropdown/Comment.vue
new file mode 100644
index 0000000..d34b2b9
--- /dev/null
+++ b/frontend/src/views/article/components/Dropdown/Comment.vue
@@ -0,0 +1,41 @@
+<template>
+ <el-dropdown :show-timeout="100" trigger="click">
+ <el-button plain>
+ {{ !comment_disabled?'Comment: opened':'Comment: closed' }}
+ <i class="el-icon-caret-bottom el-icon--right" />
+ </el-button>
+ <el-dropdown-menu slot="dropdown" class="no-padding">
+ <el-dropdown-item>
+ <el-radio-group v-model="comment_disabled" style="padding: 10px;">
+ <el-radio :label="true">
+ Close comment
+ </el-radio>
+ <el-radio :label="false">
+ Open comment
+ </el-radio>
+ </el-radio-group>
+ </el-dropdown-item>
+ </el-dropdown-menu>
+ </el-dropdown>
+</template>
+
+<script>
+export default {
+ props: {
+ value: {
+ type: Boolean,
+ default: false
+ }
+ },
+ computed: {
+ comment_disabled: {
+ get() {
+ return this.value
+ },
+ set(val) {
+ this.$emit('input', val)
+ }
+ }
+ }
+}
+</script>
diff --git a/frontend/src/views/article/components/Dropdown/Platform.vue b/frontend/src/views/article/components/Dropdown/Platform.vue
new file mode 100644
index 0000000..0a52726
--- /dev/null
+++ b/frontend/src/views/article/components/Dropdown/Platform.vue
@@ -0,0 +1,46 @@
+<template>
+ <el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click">
+ <el-button plain>
+ Platfroms({{ platforms.length }})
+ <i class="el-icon-caret-bottom el-icon--right" />
+ </el-button>
+ <el-dropdown-menu slot="dropdown" class="no-border">
+ <el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
+ <el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key">
+ {{ item.name }}
+ </el-checkbox>
+ </el-checkbox-group>
+ </el-dropdown-menu>
+ </el-dropdown>
+</template>
+
+<script>
+export default {
+ props: {
+ value: {
+ required: true,
+ default: () => [],
+ type: Array
+ }
+ },
+ data() {
+ return {
+ platformsOptions: [
+ { key: 'a-platform', name: 'a-platform' },
+ { key: 'b-platform', name: 'b-platform' },
+ { key: 'c-platform', name: 'c-platform' }
+ ]
+ }
+ },
+ computed: {
+ platforms: {
+ get() {
+ return this.value
+ },
+ set(val) {
+ this.$emit('input', val)
+ }
+ }
+ }
+}
+</script>
diff --git a/frontend/src/views/article/components/Dropdown/SourceUrl.vue b/frontend/src/views/article/components/Dropdown/SourceUrl.vue
new file mode 100644
index 0000000..8f47485
--- /dev/null
+++ b/frontend/src/views/article/components/Dropdown/SourceUrl.vue
@@ -0,0 +1,38 @@
+<template>
+ <el-dropdown :show-timeout="100" trigger="click">
+ <el-button plain>
+ Link
+ <i class="el-icon-caret-bottom el-icon--right" />
+ </el-button>
+ <el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px">
+ <el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
+ <el-input v-model="source_uri" placeholder="Please enter the content">
+ <template slot="prepend">
+ URL
+ </template>
+ </el-input>
+ </el-form-item>
+ </el-dropdown-menu>
+ </el-dropdown>
+</template>
+
+<script>
+export default {
+ props: {
+ value: {
+ type: String,
+ default: ''
+ }
+ },
+ computed: {
+ source_uri: {
+ get() {
+ return this.value
+ },
+ set(val) {
+ this.$emit('input', val)
+ }
+ }
+ }
+}
+</script>
diff --git a/frontend/src/views/article/components/Dropdown/index.js b/frontend/src/views/article/components/Dropdown/index.js
new file mode 100644
index 0000000..bc0c171
--- /dev/null
+++ b/frontend/src/views/article/components/Dropdown/index.js
@@ -0,0 +1,3 @@
+export { default as CommentDropdown } from './Comment'
+export { default as PlatformDropdown } from './Platform'
+export { default as SourceUrlDropdown } from './SourceUrl'
diff --git a/frontend/src/views/article/create.vue b/frontend/src/views/article/create.vue
new file mode 100644
index 0000000..f28ce28
--- /dev/null
+++ b/frontend/src/views/article/create.vue
@@ -0,0 +1,13 @@
+<template>
+ <article-detail :is-edit="false" />
+</template>
+
+<script>
+import ArticleDetail from './components/ArticleDetail'
+
+export default {
+ name: 'CreateArticle',
+ components: { ArticleDetail }
+}
+</script>
+
diff --git a/frontend/src/views/article/edit.vue b/frontend/src/views/article/edit.vue
new file mode 100644
index 0000000..87b6126
--- /dev/null
+++ b/frontend/src/views/article/edit.vue
@@ -0,0 +1,13 @@
+<template>
+ <article-detail :is-edit="true" />
+</template>
+
+<script>
+import ArticleDetail from './components/ArticleDetail'
+
+export default {
+ name: 'EditForm',
+ components: { ArticleDetail }
+}
+</script>
+
diff --git a/frontend/src/views/article/list.vue b/frontend/src/views/article/list.vue
new file mode 100644
index 0000000..1ae114d
--- /dev/null
+++ b/frontend/src/views/article/list.vue
@@ -0,0 +1,261 @@
+<template>
+ <div class="app-container">
+ <div class="filter-container">
+ <div class="filter-item" style="margin-right:15px">文章标题</div>
+ <el-input
+ v-model="formData.title"
+ placeholder="输入文章标题"
+ style="width: 320px;margin-right:50px"
+ class="filter-item"
+ />
+ <div class="filter-item" style="margin-right:15px">发布日期</div>
+ <el-date-picker
+ v-model="releaseDate"
+ type="daterange"
+ align="left"
+ style="width:320px;margin-right:50px"
+ unlink-panels
+ range-separator="至"
+ start-placeholder="开始日期"
+ end-placeholder="结束日期"
+ value-format="yyyyMMdd"
+ :picker-options="pickerOptions"
+ />
+ </div>
+ <div class="filter-container">
+ <div class="filter-item" style="margin-right:15px">保存日期</div>
+ <el-date-picker
+ v-model="saveDate"
+ type="daterange"
+ align="left"
+ style="width:320px;margin-right:50px"
+ unlink-panels
+ range-separator="至"
+ start-placeholder="开始日期"
+ end-placeholder="结束日期"
+ value-format="yyyyMMdd"
+ :picker-options="pickerOptions"
+ />
+ <div class="filter-item" style="margin-right:15px">文章状态</div>
+ <el-select
+ v-model="formData.status"
+ style="width:200px;margin-right:120px"
+ >
+ <el-option
+ v-for="item in statusOptions"
+ :key="item.value"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+
+ <el-button
+ class="filter-item"
+ type="primary"
+ icon="el-icon-search"
+ @click="handleFilter()"
+ >
+ 搜索
+ </el-button>
+ <el-button class="filter-item" type="info" @click="clearFilter">
+ 清空
+ </el-button>
+ </div>
+ <el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
+ <el-table-column align="center" label="编号" width="100">
+ <template slot-scope="{row}">
+ <span>{{ row.articleno }}</span>
+ </template>
+ </el-table-column>
+
+ <el-table-column align="center" label="标题">
+ <template slot-scope="{row}">
+ <span>{{ row.title }}</span>
+ </template>
+ </el-table-column>
+
+ <el-table-column width="120px" align="center" label="栏目">
+ <template slot-scope="{row}">
+ <span>{{ row.columnname }}</span>
+ </template>
+ </el-table-column>
+
+ <el-table-column width="100px" label="状态" align="center">
+ <template slot-scope="{row}">
+ <el-tag v-if="row.status==='save'" effect="dark" size="medium">已保存</el-tag>
+ <el-tag v-if="row.status==='released'" effect="dark" type="success" size="medium">已发布</el-tag>
+ <el-tag v-if="row.status==='review'" effect="dark" type="warning" size="medium">审核中</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column width="100px" label="保存时间">
+ <template slot-scope="{row}">
+ <span>{{ dateFormat(row.createtime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column width="100px" label="发布时间">
+ <template slot-scope="{row}">
+ <span>{{ dateFormat(row.releasetime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column width="100px" label="是否展示">
+ <template slot-scope="{row}">
+ <span>{{ row.isdisplay }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column width="100px" label="操作">
+ <template slot-scope="{}">
+ <span>删除</span>
+ </template>
+ </el-table-column>
+
+ <pagination v-show="total>0" :total="total" :page.sync="formData.page" :limit.sync="formData.limit" @pagination="getArticleList" />
+ </el-table></div>
+</template>
+
+<script>
+import moment from 'moment'
+import { getArticleList } from '@/api/article'
+import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
+
+export default {
+ name: 'ArticleList',
+ components: { Pagination },
+ filters: {
+ statusFilter(status) {
+ const statusMap = {
+ published: 'success',
+ draft: 'info',
+ deleted: 'danger'
+ }
+ return statusMap[status]
+ }
+ },
+ data() {
+ return {
+ list: null,
+ total: 0,
+ listLoading: false,
+ formData: {
+ title: '',
+ status: '',
+ savestartdate: '',
+ saveenddate: '',
+ releasestartdate: '',
+ releaseenddate: '',
+ pageno: 1,
+ pagesize: 20
+ },
+ saveDate: null,
+ releaseDate: null,
+ statusOptions: [{
+ value: '',
+ label: '所有'
+ },
+ {
+ value: 'save',
+ label: '已保存'
+ }, {
+ value: 'released',
+ label: '已发布'
+ }, {
+ value: 'review',
+ label: '审核中'
+ }],
+ pickerOptions: {
+ shortcuts: [{
+ text: '今天',
+ onClick(picker) {
+ const end = new Date()
+ const start = new Date()
+ picker.$emit('pick', [start, end])
+ }
+ }, {
+ text: '最近三天',
+ onClick(picker) {
+ const end = new Date()
+ const start = new Date()
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 3)
+ picker.$emit('pick', [start, end])
+ }
+ }, {
+ text: '最近一周',
+ onClick(picker) {
+ const end = new Date()
+ const start = new Date()
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
+ picker.$emit('pick', [start, end])
+ }
+ }]
+ }
+ }
+ },
+ created() {
+ this.getArticleList()
+ },
+ methods: {
+ getArticleList() {
+ this.listLoading = true
+ const saveDate = this.saveDate
+ const releaseDate = this.releaseDate
+ if (saveDate != null) {
+ this.formData.savestartdate = saveDate[0]
+ this.formData.saveenddate = saveDate[1]
+ } else {
+ this.formData.savestartdate = ''
+ this.formData.saveenddate = ''
+ }
+ if (releaseDate != null) {
+ this.formData.releasestartdate = releaseDate[0]
+ this.formData.releaseenddate = releaseDate[1]
+ } else {
+ this.formData.releasestartdate = ''
+ this.formData.releaseenddate = ''
+ }
+ getArticleList(this.formData).then(response => {
+ console.log(response)
+ if (response.page) {
+ this.list = response.page.list
+ this.total = response.page.totalCount
+ } else {
+ this.list = null
+ this.total = 0
+ }
+ this.listLoading = false
+ }).catch(error => {
+ this.$message({
+ message: error.msg || '请求异常',
+ type: 'error'
+ })
+ this.listLoading = false
+ })
+ },
+ handleFilter() {
+ this.formData.pageno = 1
+ this.getArticleList()
+ },
+ clearFilter() {
+ this.formData.title = ''
+ this.formData.status = ''
+ this.saveDate = null
+ this.releaseDate = null
+ },
+ dateFormat(date) {
+ if (date === null) {
+ return ''
+ }
+ return moment(date).format('YYYY-MM-DD HH:mm:ss')
+ }
+ }
+}
+</script>
+
+<style scoped>
+.edit-input {
+ padding-right: 100px;
+}
+.cancel-btn {
+ position: absolute;
+ right: 15px;
+ top: 10px;
+}
+</style>
diff --git a/frontend/src/views/column/index.vue b/frontend/src/views/column/index.vue
new file mode 100644
index 0000000..42e1e7d
--- /dev/null
+++ b/frontend/src/views/column/index.vue
@@ -0,0 +1,340 @@
+<template>
+ <div class="app-container">
+ <div class="filter-container">
+ <div class="filter-item" style="margin-right:15px">栏目名称</div>
+ <el-input
+ v-model="formData.name"
+ placeholder="输入栏目名称"
+ style="width: 350px;margin-right:50px"
+ class="filter-item"
+ />
+ <div class="filter-item" style="margin-right:15px">栏目CODE</div>
+ <el-input
+ v-model="formData.code"
+ placeholder="输入栏目CODE"
+ style="width: 350px;margin-right:50px"
+ class="filter-item"
+ />
+ <el-button
+ class="filter-item"
+ type="primary"
+ icon="el-icon-search"
+ @click="handleFilter()"
+ >
+ 搜索
+ </el-button>
+ </div>
+ <el-button type="primary" icon="el-icon-circle-plus-outline" @click="addColumn()">
+ 新增栏目
+ </el-button>
+ <el-table
+ :key="tableKey"
+ v-loading="listLoading"
+ :data="list"
+ border
+ fit
+ highlight-current-row
+ style="width: 100%;margin-top:10px"
+ >
+ <el-table-column label="上级栏目">
+ <template slot-scope="{row}">
+ <span>{{ row.parentname }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="栏目CODE" align="center">
+ <template slot-scope="{row}">
+ <span>{{ row.code }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="栏目名称" align="center">
+ <template slot-scope="{row}">
+ <span>{{ row.name }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="序号" align="center" width="70">
+ <template slot-scope="{row}">
+ <span>{{ row.ordernum }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="是否公开" width="100" align="center">
+ <template slot-scope="{row}">
+ <i
+ v-if="row.ispublic==='1'"
+ style="color:#67C23A;font-size:20px"
+ class="el-icon-circle-check"
+ />
+ <i
+ v-else
+ style="color:#F56C6C;font-size:20px"
+ class="el-icon-circle-close"
+ />
+ </template>
+ </el-table-column>
+ <el-table-column label="是否要审核" width="100" align="center">
+ <template slot-scope="{row}">
+ <i
+ v-if="row.needreview==='1'"
+ style="color:#67C23A;font-size:20px"
+ class="el-icon-circle-check"
+ />
+ <i
+ v-else
+ style="color:#F56C6C;font-size:20px"
+ class="el-icon-circle-close"
+ />
+ </template>
+ </el-table-column>
+ <el-table-column label="是否能发布" width="100" align="center">
+ <template slot-scope="{row}">
+ <i
+ v-if="row.publishable==='1'"
+ style="color:#67C23A;font-size:20px"
+ class="el-icon-circle-check"
+ />
+ <i
+ v-else
+ style="color:#F56C6C;font-size:20px"
+ class="el-icon-circle-close"
+ />
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" align="center" width="180">
+ <template slot-scope="{row}">
+ <el-tooltip class="item" effect="dark" content="修改" placement="bottom">
+ <el-button type="primary" icon="el-icon-edit" circle size="mini" @click="updateColumn(row)" />
+ </el-tooltip>
+ <el-tooltip v-if="row.isleaf==='0'" class="item" effect="dark" content="添加子栏目" placement="bottom">
+ <el-button type="primary" size="mini" @click="addSubColumn(row)">添加子栏目</el-button>
+ </el-tooltip>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <pagination
+ v-show="total>0"
+ :total="total"
+ :page.sync="formData.pageno"
+ :limit.sync="formData.pagesize"
+ style="margin-top:0;"
+ @pagination="getColumnList"
+ />
+ <el-dialog
+ :title="title"
+ :visible.sync="columnDialogVisible"
+ width="30%"
+ >
+ <div>
+ <el-form ref="columnForm" :model="columnForm" :rules="rules" label-width="100px">
+ <el-form-item label="栏目名称" prop="name" class="form-input-item">
+ <el-input
+ v-model="columnForm.name"
+ maxlength="30"
+ show-word-limit
+ style="width:80%"
+ />
+ </el-form-item>
+ <el-form-item label="栏目CODE" prop="code" class="form-input-item">
+ <el-input
+ v-model="columnForm.code"
+ maxlength="30"
+ show-word-limit
+ style="width:80%"
+ placeholder="只能由字母或数字组成"
+ />
+ </el-form-item>
+ <el-form-item label="是否公开" prop="ispublic">
+ <el-select
+ v-model="columnForm.ispublic"
+ style="width:80%"
+ placeholder="游客只能浏览公开内容"
+ >
+ <el-option label="是" value="1" />
+ <el-option label="否" value="0" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="是否要审核" prop="needreview">
+ <el-select
+ v-model="columnForm.needreview"
+ style="width:80%"
+ placeholder="栏目下文章审核后才能发布"
+ >
+ <el-option label="是" value="1" />
+ <el-option label="否" value="0" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="是否能发布" prop="publishable">
+ <el-select
+ v-model="columnForm.publishable"
+ style="width:80%"
+ placeholder="该栏目下不能发布文章"
+ >
+ <el-option label="是" value="1" />
+ <el-option label="否" value="0" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="栏目排序" prop="ordernum">
+ <el-input v-model="columnForm.ordernum" style="width:80%" />
+ </el-form-item>
+ </el-form>
+ </div>
+ <div style="text-align:center">
+ <el-button
+ type="primary"
+ @click="saveColumn('columnForm')"
+ >保存
+ </el-button>
+ <el-button @click="columnDialogVisible = false">取消</el-button>
+ </div>
+ </el-dialog>
+ </div>
+</template>
+<script>
+import {
+ getColumnList,
+ saveColumnList
+} from '@/api/column'
+import Pagination from '@/components/Pagination'
+export default {
+ name: 'Column',
+ components: {
+ Pagination
+ },
+ data() {
+ return {
+ formData: {
+ name: '',
+ code: '',
+ pageno: 1,
+ pagesize: 10
+ },
+ listLoading: false,
+ tableKey: 0,
+ list: null,
+ total: 0,
+ title: '',
+ columnForm: {
+ columnid: '',
+ name: '',
+ code: '',
+ ispublic: '',
+ needreview: '',
+ publishable: '',
+ parentid: '',
+ ordernum: null
+ },
+ columnDialogVisible: false,
+ rules: {
+ name: [
+ { required: true, message: '请输入栏目名称', trigger: 'blur' }
+ ],
+ code: [
+ { required: true, message: '请输入栏目CODE', trigger: 'blur' },
+ { pattern: /^[0-9a-zA-Z]{1,30}$/, message: '只能由字母或数字组成', trigger: 'blur' }
+ ],
+ ispublic: [
+ { required: true, message: '请选择是否公开', trigger: 'change' }
+ ],
+ needreview: [
+ { required: true, message: '请选择是否需要审核', trigger: 'change' }
+ ],
+ publishable: [
+ { required: true, message: '请选择是否能发布', trigger: 'change' }
+ ],
+ ordernum: [
+ { pattern: /^[0-9]{1,10}$/, message: '只能输入数字', trigger: 'blur' }
+ ]
+ }
+ }
+ },
+ created() {
+ this.getColumnList()
+ },
+ methods: {
+ getColumnList() {
+ this.listLoading = true
+ getColumnList(this.formData).then(response => {
+ if (response.page) {
+ this.list = response.page.list
+ this.total = response.page.totalCount
+ } else {
+ this.list = null
+ this.total = 0
+ }
+ this.listLoading = false
+ }).catch(error => {
+ this.$message({
+ message: error.msg || '请求异常',
+ type: 'error'
+ })
+ this.listLoading = false
+ })
+ },
+ handleFilter() {
+ this.formData.pageno = 1
+ this.getColumnList()
+ },
+ addColumn() {
+ this.title = '新增栏目'
+ this.resetForm('columnForm')
+ this.columnDialogVisible = true
+ },
+ updateColumn(row) {
+ this.title = '修改栏目'
+ this.resetForm('columnForm')
+ this.columnForm = Object.assign({}, row)
+ this.columnDialogVisible = true
+ },
+ addSubColumn(row) {
+ this.title = '[' + row.name + ']添加子栏目'
+ this.resetForm('columnForm')
+ this.columnForm.parentid = row.columnid
+ this.columnDialogVisible = true
+ },
+ saveColumn(formName) {
+ this.$refs[formName].validate((valid) => {
+ if (valid) {
+ saveColumnList(this.columnForm).then(response => {
+ this.$notify({
+ title: '成功',
+ message: '保存成功!',
+ type: 'success',
+ duration: 2000
+ })
+ this.columnDialogVisible = false
+ this.getColumnList()
+ }).catch(error => {
+ this.$message({
+ message: error.msg || '请求异常',
+ type: 'error'
+ })
+ })
+ } else {
+ return false
+ }
+ })
+ },
+ resetForm(formName) {
+ this.columnForm = {
+ columnid: '',
+ name: '',
+ code: '',
+ ispublic: '',
+ needreview: '',
+ publishable: '',
+ parentid: '',
+ ordernum: null
+ }
+ this.$nextTick(() => {
+ this.$refs[formName].clearValidate()
+ })
+ }
+ }
+}
+</script>
+<style>
+.form-input-item
+ .el-input__inner{
+ padding-right: 50px;
+ }
+
+</style>
diff --git a/frontend/src/views/example/components/ArticleDetail.vue b/frontend/src/views/example/components/ArticleDetail.vue
index 157497b..1c8fcbe 100644
--- a/frontend/src/views/example/components/ArticleDetail.vue
+++ b/frontend/src/views/example/components/ArticleDetail.vue
@@ -16,7 +16,6 @@
<div class="createPost-main-container">
<el-row>
- <Warning />
<el-col :span="24">
<el-form-item style="margin-bottom: 40px;" prop="title">
@@ -83,7 +82,6 @@
import { validURL } from '@/utils/validate'
import { fetchArticle } from '@/api/article'
import { searchUser } from '@/api/remote-search'
-import Warning from './Warning'
import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
const defaultForm = {
@@ -102,7 +100,7 @@
export default {
name: 'ArticleDetail',
- components: { Tinymce, MDinput, Upload, Sticky, Warning, CommentDropdown, PlatformDropdown, SourceUrlDropdown },
+ components: { Tinymce, MDinput, Upload, Sticky, CommentDropdown, PlatformDropdown, SourceUrlDropdown },
props: {
isEdit: {
type: Boolean,
@@ -153,6 +151,9 @@
contentShortLength() {
return this.postForm.content_short.length
},
+ lang() {
+ return this.$store.getters.language
+ },
displayTime: {
// set and get is useful when the data
// returned by the back end api is different from the front end
@@ -196,7 +197,7 @@
})
},
setTagsViewTitle() {
- const title = 'Edit Article'
+ const title = this.lang === 'zh' ? '编辑文章' : 'Edit Article'
const route = Object.assign({}, this.tempRoute, { title: `${title}-${this.postForm.id}` })
this.$store.dispatch('tagsView/updateVisitedView', route)
},
diff --git a/frontend/src/views/example/components/Warning.vue b/frontend/src/views/example/components/Warning.vue
index 8d2a7e5..d24fe29 100644
--- a/frontend/src/views/example/components/Warning.vue
+++ b/frontend/src/views/example/components/Warning.vue
@@ -1,9 +1,6 @@
<template>
<aside>
- Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support
- caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching
- effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all
- pages directly. See details
+ {{ $t('example.warning') }}
<a
href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
target="_blank"