diff --git a/.gitignore b/.gitignore
index 5465356..579ea1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,14 +91,14 @@ replay_pid*
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
-# .idea/artifacts
-# .idea/compiler.xml
-# .idea/jarRepositories.xml
-# .idea/modules.xml
-# .idea/*.iml
-# .idea/modules
-# *.iml
-# *.ipr
+.idea/artifacts
+.idea/compiler.xml
+.idea/jarRepositories.xml
+.idea/modules.xml
+.idea/*.iml
+.idea/modules
+*.iml
+*.ipr
# CMake
cmake-build-*/
diff --git a/.idea/jpa.xml b/.idea/jpa.xml
new file mode 100644
index 0000000..898e07a
--- /dev/null
+++ b/.idea/jpa.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index fae6c8e..c72dd77 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -7,4 +7,7 @@
+
+
+
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..a4b76b9
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 50d231e..0a9a744 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -33,3 +33,14 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
username: bookstore
password: EzSn+RZ*x2&fHFh9kC+H
+
+---
+spring:
+ config:
+ activate:
+ on-profile: mysql-remote
+ datasource:
+ url: jdbc:mysql://frp-air.top:43458/bookstore
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ username: bookstore
+ password: EzSn+RZ*x2&fHFh9kC+H
diff --git a/src/main/resources/static/components/book.js b/src/main/resources/static/components/book.js
index 8ecc107..400fd4b 100644
--- a/src/main/resources/static/components/book.js
+++ b/src/main/resources/static/components/book.js
@@ -1,88 +1,208 @@
function bookForm() {
- return {
- type: 'form',
- ...horizontalFormOptions(),
- body: [
- {
- type: 'hidden',
- name: 'id',
- },
- {
- type: 'input-text',
- name: 'name',
- label: '名称',
- required: true,
- ...formInputClearable(),
- },
- {
- type: 'input-text',
- name: 'author',
- label: '作者',
- ...formInputClearable(),
- },
- {
- type: 'input-text',
- name: 'source',
- label: '来源',
- ...formInputClearable(),
- validations: {
- isUrl: true,
- },
- },
- {
- type: 'input-tag',
- name: 'tags',
- label: '标签',
- clearable: true,
- joinValues: false,
- extractValue: true,
- max: 10,
- maxTagLength: 10,
- source: '${base}/book/tags'
- },
- {
- type: 'textarea',
- name: 'description',
- label: '简介',
- required: true,
- ...formInputClearable(),
- showCounter: true,
- trimContents: true,
- minRows: 2,
- maxRows: 2,
- maxLength: 100,
- },
- ],
- }
+ return {
+ type: 'form',
+ ...horizontalFormOptions(),
+ body: [
+ {
+ type: 'hidden',
+ name: 'id',
+ },
+ {
+ type: 'input-text',
+ name: 'name',
+ label: '名称',
+ required: true,
+ ...formInputClearable(),
+ },
+ {
+ type: 'input-text',
+ name: 'author',
+ label: '作者',
+ ...formInputClearable(),
+ },
+ {
+ type: 'input-text',
+ name: 'source',
+ label: '来源',
+ ...formInputClearable(),
+ validations: {
+ isUrl: true,
+ },
+ },
+ {
+ type: 'input-tag',
+ name: 'tags',
+ label: '标签',
+ clearable: true,
+ joinValues: false,
+ extractValue: true,
+ max: 10,
+ maxTagLength: 10,
+ source: '${base}/book/tags'
+ },
+ {
+ type: 'textarea',
+ name: 'description',
+ label: '简介',
+ required: true,
+ ...formInputClearable(),
+ showCounter: true,
+ trimContents: true,
+ minRows: 2,
+ maxRows: 2,
+ maxLength: 100,
+ },
+ ],
+ }
}
function bookAddDialog() {
- return {
- type: 'action',
- actionType: 'dialog',
- dialog: {
- title: '新增书籍',
- size: 'md',
- body: {
- ...bookForm(),
- api: '${base}/book/save',
- },
- },
- }
+ return {
+ type: 'action',
+ actionType: 'dialog',
+ dialog: {
+ title: '新增书籍',
+ size: 'md',
+ body: {
+ ...bookForm(),
+ api: '${base}/book/save',
+ },
+ },
+ }
}
function bookDetailDialog() {
- return {
- type: 'action',
- actionType: 'dialog',
- dialog: {
- title: '编辑书籍',
- size: 'md',
- body: {
- ...bookForm(),
- initApi: '${base}/book/detail/${bookId}',
- api: '${base}/book/save',
- },
+ return {
+ type: 'action',
+ actionType: 'dialog',
+ dialog: {
+ title: '编辑书籍',
+ size: 'lg',
+ actions: [],
+ body: [
+ {
+ id: 'book-detail-dialog-form',
+ ...bookForm(),
+ initApi: '${base}/book/detail/${bookId}',
+ api: '${base}/book/save',
},
- }
+ {
+ type: 'flex',
+ className: 'mt-5',
+ justify: 'flex-end',
+ items: [
+ {
+ type: 'action',
+ label: '保存',
+ level: 'primary',
+ onEvent: {
+ click: {
+ actions: [
+ {
+ actionType: 'submit',
+ componentId: 'book-detail-dialog-form',
+ },
+ {
+ actionType: 'reload',
+ componentId: 'book-list',
+ }
+ ]
+ }
+ }
+ }
+ ]
+ },
+ {
+ id: 'chapter-list',
+ type: 'crud',
+ ...crudCommonOptions(),
+ api: {
+ method: 'get',
+ url: '${base}/chapter/list/${bookId}',
+ data: {
+ page: '${page|default:1}',
+ size: '${size|default:10}'
+ }
+ },
+ headerToolbar: [
+ 'reload',
+ {
+ label: '',
+ icon: 'fa fa-add',
+ ...chapterAddDialog(),
+ },
+ ],
+ footerToolbar: [
+ paginationOption(),
+ ],
+ columns: [
+ {
+ name: 'sequence',
+ label: '章节数',
+ width: 50,
+ },
+ {
+ name: 'name',
+ label: '名称',
+ width: 150,
+ },
+ {
+ name: 'description',
+ label: '简介',
+ },
+ {
+ type: 'operation',
+ label: '操作',
+ fixed: 'right',
+ className: 'nowrap',
+ width: 200,
+ buttons: [
+ {
+ type: 'action',
+ label: '编辑',
+ ...chapterDetailDialog(),
+ },
+ {
+ type: 'action',
+ label: '阅读',
+ actionType: 'dialog',
+ dialog: {
+ title: '阅读',
+ size: 'lg',
+ actions: [],
+ body: {
+ type: 'service',
+ className: 'p-10',
+ api: '${base}/chapter/show/${chapterId}',
+ silentPolling: true,
+ body: {
+ type: 'tpl',
+ className: 'font-serif',
+ tpl: '${item|raw}'
+ }
+ }
+ }
+ },
+ {
+ type: 'action',
+ label: '导出',
+ actionType: 'download',
+ api: '${base}/chapter/export/${chapterId}',
+ },
+ {
+ type: 'action',
+ label: '删除',
+ level: 'danger',
+ confirmTitle: '确认删除',
+ confirmText: '确认删除名称为「${name}」的章节吗?',
+ actionType: 'ajax',
+ api: 'get:${base}/chapter/remove/${chapterId}',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ }
}
diff --git a/src/main/resources/static/components/chapter.js b/src/main/resources/static/components/chapter.js
index 42a2c01..7aeed4b 100644
--- a/src/main/resources/static/components/chapter.js
+++ b/src/main/resources/static/components/chapter.js
@@ -1,68 +1,230 @@
function chapterForm() {
- return {
- type: 'form',
- ...horizontalFormOptions(),
- body: [
- {
- type: 'hidden',
- name: 'id',
- },
- {
- type: 'input-number',
- name: 'sequence',
- label: '章节数',
- required: true,
- step: 1,
- precision: 0,
- },
- {
- type: 'input-text',
- name: 'name',
- label: '名称',
- ...formInputClearable(),
- },
- {
- type: 'textarea',
- name: 'description',
- label: '简介',
- ...formInputClearable(),
- showCounter: true,
- trimContents: true,
- minRows: 2,
- maxRows: 2,
- maxLength: 100,
- },
- ],
- }
+ return {
+ type: 'form',
+ ...horizontalFormOptions(),
+ body: [
+ {
+ type: 'hidden',
+ name: 'id',
+ },
+ {
+ type: 'input-number',
+ name: 'sequence',
+ label: '章节数',
+ required: true,
+ step: 1,
+ precision: 0,
+ },
+ {
+ type: 'input-text',
+ name: 'name',
+ label: '名称',
+ ...formInputClearable(),
+ },
+ {
+ type: 'textarea',
+ name: 'description',
+ label: '简介',
+ ...formInputClearable(),
+ showCounter: true,
+ trimContents: true,
+ minRows: 2,
+ maxRows: 2,
+ maxLength: 100,
+ },
+ ],
+ }
}
function chapterAddDialog() {
- return {
- type: 'action',
- actionType: 'dialog',
- dialog: {
- title: '新增章节',
- size: 'md',
- body: {
- ...chapterForm(),
- api: '${base}/chapter/save/${bookId}',
- },
- },
- }
+ return {
+ type: 'action',
+ actionType: 'dialog',
+ dialog: {
+ title: '新增章节',
+ size: 'md',
+ body: {
+ ...chapterForm(),
+ api: '${base}/chapter/save/${bookId}',
+ },
+ },
+ }
}
function chapterDetailDialog() {
- return {
- type: 'action',
- actionType: 'dialog',
- dialog: {
- title: '编辑章节',
- size: 'md',
- body: {
- ...chapterForm(),
- initApi: '${base}/chapter/detail/${chapterId}',
- api: '${base}/chapter/save/${bookId}',
- },
+ return {
+ type: 'action',
+ actionType: 'dialog',
+ dialog: {
+ title: '编辑章节',
+ size: 'lg',
+ actions: [],
+ body: [
+ {
+ id: 'chapter-detail-dialog-form',
+ ...chapterForm(),
+ initApi: '${base}/chapter/detail/${chapterId}',
+ api: '${base}/chapter/save/${bookId}',
},
- }
+ {
+ type: 'flex',
+ className: 'mt-5',
+ justify: 'flex-end',
+ items: [
+ {
+ type: 'action',
+ label: '保存',
+ level: 'primary',
+ onEvent: {
+ click: {
+ actions: [
+ {
+ actionType: 'submit',
+ componentId: 'chapter-detail-dialog-form',
+ },
+ {
+ actionType: 'reload',
+ componentId: 'chapter-list',
+ }
+ ]
+ }
+ }
+ }
+ ]
+ },
+ {
+ type: 'crud',
+ ...crudCommonOptions(),
+ api: {
+ method: 'get',
+ url: '${base}/line/list/${chapterId}',
+ data: {
+ page: '${page|default:1}',
+ size: '${size|default:10}'
+ }
+ },
+ quickSaveItemApi: '${base}/line/update/${lineId}',
+ headerToolbar: [
+ 'reload',
+ {
+ type: 'action',
+ label: '',
+ icon: 'fa fa-upload',
+ actionType: 'dialog',
+ dialog: {
+ title: '导入正文',
+ size: 'lg',
+ body: {
+ type: 'form',
+ api: '${base}/chapter/import/${chapterId}',
+ mode: 'normal',
+ body: [
+ {
+ type: 'switch',
+ name: 'override',
+ label: '覆盖导入',
+ },
+ {
+ type: 'textarea',
+ name: 'text',
+ label: '正文',
+ required: true,
+ ...formInputClearable(),
+ showCounter: true,
+ trimContents: true,
+ minRows: 10,
+ maxRows: 10,
+ },
+ ],
+ },
+ },
+ },
+ ],
+ footerToolbar: [
+ paginationOption(),
+ ],
+ columns: [
+ {
+ name: 'sequence',
+ label: '行号',
+ width: 50,
+ },
+ {
+ name: 'text',
+ label: '内容',
+ quickEdit: {
+ saveImmediately: true,
+ resetOnFailed: true,
+ type: 'textarea',
+ showCounter: true,
+ trimContents: true,
+ minRows: 10,
+ maxRows: 10,
+ },
+ },
+ {
+ type: 'operation',
+ label: '操作',
+ fixed: 'right',
+ className: 'nowrap',
+ width: 100,
+ buttons: [
+ {
+ type: 'action',
+ label: '处理',
+ actionType: 'dialog',
+ dialog: {
+ title: '文本处理',
+ size: 'lg',
+ body: {
+ debug: true,
+ type: 'form',
+ canAccessSuperData: false,
+ initApi: '${base}/line/detail/${lineId}',
+ api: '${base}/line/save/${chapterId}',
+ body: [
+ {
+ type: 'hidden',
+ name: 'lineId',
+ },
+ {
+ type: 'textarea',
+ name: 'text',
+ label: '正文',
+ ...formInputClearable(),
+ showCounter: true,
+ trimContents: true,
+ minRows: 5,
+ maxRows: 5,
+ },
+ {
+ type: 'textarea',
+ name: 'newText',
+ label: '优化后正文',
+ ...formInputClearable(),
+ showCounter: true,
+ trimContents: true,
+ minRows: 5,
+ maxRows: 5,
+ },
+ ]
+ }
+ }
+ },
+ {
+ type: 'action',
+ label: '删除',
+ level: 'danger',
+ confirmTitle: '确认删除',
+ confirmText: '确认删除当前行吗?',
+ actionType: 'ajax',
+ api: 'get:${base}/line/remove/${lineId}',
+ },
+ ]
+ }
+ ],
+ },
+ ]
+ },
+ }
}
\ No newline at end of file
diff --git a/src/main/resources/static/components/helper.js b/src/main/resources/static/components/helper.js
index e2861e3..eec46d9 100644
--- a/src/main/resources/static/components/helper.js
+++ b/src/main/resources/static/components/helper.js
@@ -1,37 +1,38 @@
function crudCommonOptions() {
- return {
- affixHeader: false,
- stopAutoRefreshWhenModalIsOpen: true,
- resizable: false,
- syncLocation: false,
- silentPolling: true,
- }
+ return {
+ affixHeader: false,
+ stopAutoRefreshWhenModalIsOpen: true,
+ resizable: false,
+ syncLocation: false,
+ silentPolling: true,
+ }
}
-function horizontalFormOptions() {
- return {
- mode: 'horizontal',
- canAccessSuperData: false,
- horizontal: {
- left: 1,
- },
- }
+function horizontalFormOptions(left = 1) {
+ return {
+ mode: 'horizontal',
+ canAccessSuperData: false,
+ horizontal: {
+ left: left,
+ justify: true,
+ },
+ }
}
function formInputClearable() {
- return {
- clearable: true,
- clearValueOnEmpty: true,
- }
+ return {
+ clearable: true,
+ clearValueOnEmpty: true,
+ }
}
function paginationOption() {
- return {
- type: 'pagination',
- mode: 'normal',
- layout: 'total,perPage,pager',
- maxButtons: 5,
- showPageInput: false,
- total: '${total}'
- }
+ return {
+ type: 'pagination',
+ mode: 'normal',
+ layout: 'total,perPage,pager',
+ maxButtons: 5,
+ showPageInput: false,
+ total: '${total}'
+ }
}
diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html
index f2a77c1..e74dde6 100644
--- a/src/main/resources/static/index.html
+++ b/src/main/resources/static/index.html
@@ -31,345 +31,103 @@