Просмотр исходного кода

support blog deleting in backend

fengchang 1 год назад
Родитель
Сommit
8513946448

+ 68 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/controller/BlogController.java

@@ -0,0 +1,68 @@
+package com.hichina.admin.hichinaadminbackend.controller;
+
+import com.aliyuncs.utils.StringUtils;
+import com.github.pagehelper.PageHelper;
+import com.hichina.admin.hichinaadminbackend.mapper.BlogMapper;
+import com.hichina.admin.hichinaadminbackend.model.DTO.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+a
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/v1/blog")
+public class BlogController {
+
+    @Autowired
+    private BlogMapper blogMapper;
+
+    @DeleteMapping("/batch")
+    @Transactional
+    public HichinaResponse deleteBlogs(@RequestBody GeneralBatchDeleteRequest req){
+
+        // delete base product record
+        blogMapper.batchDelete(req.getToDelete());
+
+        HichinaResponse ret = new HichinaResponse();
+        ret.setOk(true);
+        ret.setData(req.getToDelete());
+        ret.setMessage("成功批量删除博客");
+
+        return ret;
+    }
+
+    @GetMapping("/list")
+    public HichinaResponse getBloglist(@RequestParam(value = "page", required = true) Integer page,
+                                       @RequestParam(value = "pageSize", required = true) Integer size,
+                                       @RequestParam(value = "query") String query){
+
+        HichinaResponse ret = new HichinaResponse();
+
+        if(page>0){
+            PageHelper.startPage(page,size);
+        }
+
+        List<BlogListItemDTO> blogListItemDTOS;
+        Integer cnt = 0;
+        if(StringUtils.isEmpty(query)){
+            blogListItemDTOS = blogMapper.findBlogSummaryList();
+            cnt = blogMapper.countBlogSummaryList();
+        }else{
+            blogListItemDTOS = blogMapper.findBlogSummaryListByQuery(query);
+            cnt = blogMapper.countBlogSummaryListByQuery(query);
+        }
+
+        PaginationWrapper paginationWrapper = new PaginationWrapper();
+        paginationWrapper.setData(blogListItemDTOS);
+        paginationWrapper.setPageSize(size);
+        paginationWrapper.setCurrentPage(page);
+        paginationWrapper.setTotal(cnt);
+
+        ret.setData(paginationWrapper);
+        ret.setOk(true);
+        ret.setMessage("Succeed getting blog list");
+
+        return ret;
+    }
+}

+ 26 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/mapper/BlogMapper.java

@@ -1,14 +1,40 @@
 package com.hichina.admin.hichinaadminbackend.mapper;
 
+import com.hichina.admin.hichinaadminbackend.model.DTO.BlogListItemDTO;
+import org.apache.ibatis.annotations.Delete;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 import org.springframework.stereotype.Component;
 
 import java.util.Date;
+import java.util.List;
 
 @Mapper
 @Component(value = "blogMapper")
 public interface BlogMapper {
+    @Delete("<script>" +
+            "Delete FROM blog WHERE blog_id in \n" +
+            "    <foreach item='item' collection='blogIds' open='(' separator=',' close=')'>\n" +
+            "    #{item}" +
+            "    </foreach>" +
+            "</script>")
+    void batchDelete(List<String> blogIds);
+
+
     @Update("UPDATE blog SET head_image_url = REGEXP_SUBSTR(content, '(http|https)://[^ \\n]+(jpg|jpeg|png|gif)') WHERE last_update_time>#{startDate} and head_image_url is null or LENGTH(head_image_url)<1 and content REGEXP '(http|https)://[^ \\n]+(jpg|jpeg|png|gif)';")
     void updateCoverWithFirstImage(Date startDate);
+
+    @Select("select blog_id, title, created_time from blog where draft=0 order by created_time desc")
+    List<BlogListItemDTO> findBlogSummaryList();
+
+    @Select("select count(*) from blog where draft=0 and title like CONCAT('%',CONCAT(#{query},'%'))")
+    Integer countBlogSummaryListByQuery(String query);
+
+    @Select("select count(*) from blog where draft=0")
+    Integer countBlogSummaryList();
+
+    @Select("select blog_id, title, created_time from blog where draft=0 and title like CONCAT('%',CONCAT(#{query},'%')) or blog_id like CONCAT('%',CONCAT(#{query},'%')) order by created_time desc")
+    List<BlogListItemDTO> findBlogSummaryListByQuery(String query);
+
 }

+ 28 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/Blog.java

@@ -0,0 +1,28 @@
+package com.hichina.admin.hichinaadminbackend.model;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class Blog {
+    private String blogId;
+
+    private String userId;
+
+    private Date createdTime;
+
+    private Date lastUpdateTime;
+
+    private String title;
+
+    private String headImageUrl;
+
+    private String language;
+
+    private String content;
+
+    private Boolean draft;
+}

+ 14 - 0
hichina-admin-backend/src/main/java/com/hichina/admin/hichinaadminbackend/model/DTO/BlogListItemDTO.java

@@ -0,0 +1,14 @@
+package com.hichina.admin.hichinaadminbackend.model.DTO;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+public class BlogListItemDTO {
+    private String blogId;
+    private String title;
+    private Date createdTime;
+}

+ 16 - 0
hichina-admin-front/src/layouts/MainLayout.vue

@@ -123,6 +123,22 @@
             </q-item-section>
           </q-item>
         </q-expansion-item>
+        <q-expansion-item
+          default-opened
+          icon="book"
+          label="博文管理"
+          v-if="getMyRole() == 'SUPERADMIN' || getMyRole() == 'EMPLOYEE'"
+          :content-inset-level="1"
+        >
+          <q-item clickable target="_blank" @click="goPage('/blogmanage')">
+            <q-item-section avatar>
+              <q-icon :name="'book'" />
+            </q-item-section>
+            <q-item-section>
+              <q-item-label>博客</q-item-label>
+            </q-item-section>
+          </q-item>
+        </q-expansion-item>
         <q-expansion-item
           default-opened
           icon="auto_stories"

+ 202 - 0
hichina-admin-front/src/pages/BlogManagePage.vue

@@ -0,0 +1,202 @@
+<template>
+  <q-page padding>
+    <q-dialog v-model="confirmDelete" persistent>
+      <q-card>
+        <q-card-section class="row items-center">
+          <q-avatar icon="signal_wifi_off" color="primary" text-color="white" />
+          <span class="q-ml-sm">确定要删除所选吗?</span>
+        </q-card-section>
+
+        <q-card-actions align="right">
+          <q-btn flat label="取消" color="primary" v-close-popup />
+          <q-btn
+            flat
+            label="确认删除"
+            color="primary"
+            @click="executeDelete"
+            v-close-popup
+          />
+        </q-card-actions>
+      </q-card>
+    </q-dialog>
+    <div class="row justify-center q-pa-md">
+      <q-table
+        ref="blogTableRef"
+        style="max-width: 98%; min-width: 90%"
+        title="博文管理"
+        :rows="blogs"
+        :columns="columns"
+        v-model:pagination="serverPagination"
+        :loading="loading"
+        :filter="filter"
+        row-key="blogId"
+        :separator="'cell'"
+        @request="onRequest"
+        selection="multiple"
+        v-model:selected="selected"
+      >
+        <template v-slot:top-right>
+          <q-btn
+            style="margin-right: 50px"
+            round
+            color="primary"
+            @click="goDelete"
+            icon="delete"
+          />
+          <q-input
+            borderless
+            dense
+            debounce="300"
+            v-model="filter"
+            placeholder="Search"
+          >
+            <template v-slot:append>
+              <q-icon name="search" />
+            </template>
+          </q-input>
+        </template>
+      </q-table>
+    </div>
+  </q-page>
+</template>
+
+<script>
+import { api } from "boot/axios";
+import { useQuasar } from "quasar";
+export default {
+  name: "BlogManagePage",
+  setup() {
+    const $q = useQuasar();
+
+    return {
+      showNotifyMessageFail(msg) {
+        $q.notify({
+          message: msg,
+          color: "red",
+          position: "top-right",
+        });
+      },
+      showNotifyMessageSucceed(msg) {
+        $q.notify({
+          message: msg,
+          color: "green",
+          position: "top-right",
+        });
+      },
+      showFullPageLoading() {
+        this.disableAction = true;
+        $q.loading.show();
+      },
+      hideFullPageLoading() {
+        $q.loading.hide();
+        this.disableAction = false;
+      },
+    };
+  },
+  mounted() {
+    this.$refs.blogTableRef.requestServerInteraction();
+  },
+  methods: {
+    executeDelete() {
+      console.log("to delete selected:");
+      console.log(this.selected);
+
+      var listOfId2Del = [];
+      for (var i in this.selected) {
+        listOfId2Del.push(this.selected[i].blogId);
+      }
+
+      var params = {};
+      params.toDelete = listOfId2Del;
+      api
+        .delete("/api/v1/blog/batch", { data: params })
+        .then((response) => {
+          this.showNotifyMessageSucceed(response.data.message);
+          this.$refs.blogTableRef.requestServerInteraction();
+        })
+        .catch((e) => {
+          this.showNotifyMessageFail(e.toString());
+        });
+    },
+    goDelete() {
+      if (this.selected.length < 1) {
+        this.showNotifyMessageFail("没有选中任何项");
+        return;
+      }
+      this.confirmDelete = true;
+    },
+    onRequest(props) {
+      const { page, rowsPerPage } = props.pagination;
+      const filter = props.filter;
+      this.loading = true;
+
+      var params = {};
+      params.page = page;
+      params.pageSize = rowsPerPage;
+      params.query = filter;
+      api
+        .get("/api/v1/blog/list", {
+          params: params,
+        })
+        .then((response) => {
+          this.blogs = response.data.data.data;
+          this.serverPagination.page = response.data.data.currentPage;
+          this.serverPagination.rowsNumber = response.data.data.total;
+          this.serverPagination.rowsPerPage = response.data.data.pageSize;
+
+          this.loading = false;
+        })
+        .catch((e) => {
+          console.log(e);
+          this.showNotifyMessageFail(e.response);
+        });
+    },
+  },
+  data() {
+    return {
+      confirmDelete: false,
+      selected: [],
+      loading: false,
+      filter: "",
+      blogs: [],
+      serverPagination: {
+        page: 1,
+        rowsPerPage: 100,
+        rowsNumber: 10, // specifying this determines pagination is server-side
+      },
+      columns: [
+        {
+          name: "blogId",
+          required: true,
+          label: "博客ID",
+          align: "left",
+          field: "blogId",
+          sortable: false,
+          style:
+            "max-width: 100px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+        {
+          name: "title",
+          required: true,
+          label: "标题",
+          align: "left",
+          field: "title",
+          sortable: false,
+          style:
+            "max-width: 100px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+        {
+          name: "createdTime",
+          required: true,
+          label: "创建时间",
+          align: "left",
+          field: "createdTime",
+          sortable: false,
+          style:
+            "max-width: 120px;min-width: 120px;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
+        },
+      ],
+    };
+  },
+};
+</script>

+ 10 - 0
hichina-admin-front/src/router/routes.js

@@ -100,6 +100,16 @@ const routes = [
       },
     ],
   },
+  {
+    path: "/blogmanage",
+    component: () => import("layouts/MainLayout.vue"),
+    children: [
+      {
+        path: "",
+        component: () => import("pages/BlogManagePage.vue"),
+      },
+    ],
+  },
   {
     path: "/guidebook-intro",
     component: () => import("layouts/MainLayout.vue"),