diff --git a/.idea/GitCommitMessageStorage.xml b/.idea/GitCommitMessageStorage.xml
new file mode 100644
index 0000000..e4fd56a
--- /dev/null
+++ b/.idea/GitCommitMessageStorage.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/BookController.kt b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/BookController.kt
index 5c56280..1e34454 100644
--- a/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/BookController.kt
+++ b/src/main/kotlin/com/lanyuanxiaoyao/bookstore/controller/BookController.kt
@@ -1,10 +1,14 @@
package com.lanyuanxiaoyao.bookstore.controller
+import cn.hutool.core.io.IoUtil
import cn.hutool.core.util.IdUtil
+import cn.hutool.core.util.StrUtil
+import cn.hutool.core.util.URLUtil
import com.lanyuanxiaoyao.bookstore.Book
import com.lanyuanxiaoyao.bookstore.BookRepository
import com.lanyuanxiaoyao.bookstore.PageResponse
import jakarta.annotation.Resource
+import jakarta.servlet.http.HttpServletResponse
import jakarta.transaction.Transactional
import org.slf4j.LoggerFactory
import org.springframework.data.domain.PageRequest
@@ -27,7 +31,7 @@ class BookController {
@GetMapping("list")
fun list(
@RequestParam("page", defaultValue = "1") page: Int,
- @RequestParam("size", defaultValue = "10") size: Int
+ @RequestParam("size", defaultValue = "10") size: Int,
): PageResponse {
val pageable = bookRepository.findAll(PageRequest.of(0.coerceAtLeast(page - 1), size))
return PageResponse(
@@ -67,6 +71,30 @@ class BookController {
return bookRepository.findAllTag()
}
+ @Transactional
+ @GetMapping("export/{bookId}")
+ fun export(@PathVariable("bookId") bookId: String, response: HttpServletResponse) {
+ val book = bookRepository.findById(bookId).orElseThrow()
+ val builder = StringBuilder()
+ for (chapter in book.chapters.sortedBy { it.sequence }) {
+ if (chapter.name.isNullOrBlank()) {
+ builder.appendLine("第${chapter.sequence}章")
+ } else {
+ builder.appendLine("第${chapter.sequence}章 ${chapter.name}")
+ }
+ builder.appendLine()
+ for (line in chapter.content.sortedBy { it.sequence }) {
+ builder.appendLine(line.text)
+ }
+ builder.appendLine()
+ }
+ response.setHeader("Access-Control-Expose-Headers", "Content-Type")
+ response.setHeader("Content-Type", "text/plain")
+ response.setHeader("Access-Control-Expose-Headers", "Content-Disposition")
+ response.setHeader("Content-Disposition", StrUtil.format("attachment; filename={}", URLUtil.encodeAll("${book.name}.txt")))
+ IoUtil.copy(builder.toString().byteInputStream(), response.outputStream)
+ }
+
data class ViewItem(
val bookId: String?,
val name: String,
diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html
index cc4e8b6..011669a 100644
--- a/src/main/resources/static/index.html
+++ b/src/main/resources/static/index.html
@@ -76,7 +76,7 @@
label: '操作',
fixed: 'right',
className: 'nowrap',
- width: 250,
+ width: 100,
buttons: [
{
type: 'action',
@@ -85,6 +85,12 @@
url: '${source}',
blank: true,
},
+ {
+ type: 'action',
+ label: '导出',
+ actionType: 'download',
+ api: '${base}/book/export/${bookId}',
+ },
{
type: 'action',
label: '编辑',