1
0

feat: 优化书籍和章节编辑界面

This commit is contained in:
2024-12-23 09:47:24 +08:00
parent 13590cbe9c
commit e0653d7400
9 changed files with 571 additions and 509 deletions

16
.gitignore vendored
View File

@@ -91,14 +91,14 @@ replay_pid*
# When using Gradle or Maven with auto-import, you should exclude module files, # 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 # since they will be recreated, and may cause churn. Uncomment if using
# auto-import. # auto-import.
# .idea/artifacts .idea/artifacts
# .idea/compiler.xml .idea/compiler.xml
# .idea/jarRepositories.xml .idea/jarRepositories.xml
# .idea/modules.xml .idea/modules.xml
# .idea/*.iml .idea/*.iml
# .idea/modules .idea/modules
# *.iml *.iml
# *.ipr *.ipr
# CMake # CMake
cmake-build-*/ cmake-build-*/

7
.idea/jpa.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JpaBuddyIdeaProjectConfig">
<option name="defaultUnitInitialized" value="true" />
<option name="renamerInitialized" value="true" />
</component>
</project>

3
.idea/misc.xml generated
View File

@@ -7,4 +7,7 @@
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
<component name="ProjectType">
<option name="id" value="jpab" />
</component>
</project> </project>

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -33,3 +33,14 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
username: bookstore username: bookstore
password: EzSn+RZ*x2&fHFh9kC+H 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

View File

@@ -1,88 +1,208 @@
function bookForm() { function bookForm() {
return { return {
type: 'form', type: 'form',
...horizontalFormOptions(), ...horizontalFormOptions(),
body: [ body: [
{ {
type: 'hidden', type: 'hidden',
name: 'id', name: 'id',
}, },
{ {
type: 'input-text', type: 'input-text',
name: 'name', name: 'name',
label: '名称', label: '名称',
required: true, required: true,
...formInputClearable(), ...formInputClearable(),
}, },
{ {
type: 'input-text', type: 'input-text',
name: 'author', name: 'author',
label: '作者', label: '作者',
...formInputClearable(), ...formInputClearable(),
}, },
{ {
type: 'input-text', type: 'input-text',
name: 'source', name: 'source',
label: '来源', label: '来源',
...formInputClearable(), ...formInputClearable(),
validations: { validations: {
isUrl: true, isUrl: true,
}, },
}, },
{ {
type: 'input-tag', type: 'input-tag',
name: 'tags', name: 'tags',
label: '标签', label: '标签',
clearable: true, clearable: true,
joinValues: false, joinValues: false,
extractValue: true, extractValue: true,
max: 10, max: 10,
maxTagLength: 10, maxTagLength: 10,
source: '${base}/book/tags' source: '${base}/book/tags'
}, },
{ {
type: 'textarea', type: 'textarea',
name: 'description', name: 'description',
label: '简介', label: '简介',
required: true, required: true,
...formInputClearable(), ...formInputClearable(),
showCounter: true, showCounter: true,
trimContents: true, trimContents: true,
minRows: 2, minRows: 2,
maxRows: 2, maxRows: 2,
maxLength: 100, maxLength: 100,
}, },
], ],
} }
} }
function bookAddDialog() { function bookAddDialog() {
return { return {
type: 'action', type: 'action',
actionType: 'dialog', actionType: 'dialog',
dialog: { dialog: {
title: '新增书籍', title: '新增书籍',
size: 'md', size: 'md',
body: { body: {
...bookForm(), ...bookForm(),
api: '${base}/book/save', api: '${base}/book/save',
}, },
}, },
} }
} }
function bookDetailDialog() { function bookDetailDialog() {
return { return {
type: 'action', type: 'action',
actionType: 'dialog', actionType: 'dialog',
dialog: { dialog: {
title: '编辑书籍', title: '编辑书籍',
size: 'md', size: 'lg',
body: { actions: [],
...bookForm(), body: [
initApi: '${base}/book/detail/${bookId}', {
api: '${base}/book/save', 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}',
},
],
},
],
},
],
},
}
} }

View File

@@ -1,68 +1,230 @@
function chapterForm() { function chapterForm() {
return { return {
type: 'form', type: 'form',
...horizontalFormOptions(), ...horizontalFormOptions(),
body: [ body: [
{ {
type: 'hidden', type: 'hidden',
name: 'id', name: 'id',
}, },
{ {
type: 'input-number', type: 'input-number',
name: 'sequence', name: 'sequence',
label: '章节数', label: '章节数',
required: true, required: true,
step: 1, step: 1,
precision: 0, precision: 0,
}, },
{ {
type: 'input-text', type: 'input-text',
name: 'name', name: 'name',
label: '名称', label: '名称',
...formInputClearable(), ...formInputClearable(),
}, },
{ {
type: 'textarea', type: 'textarea',
name: 'description', name: 'description',
label: '简介', label: '简介',
...formInputClearable(), ...formInputClearable(),
showCounter: true, showCounter: true,
trimContents: true, trimContents: true,
minRows: 2, minRows: 2,
maxRows: 2, maxRows: 2,
maxLength: 100, maxLength: 100,
}, },
], ],
} }
} }
function chapterAddDialog() { function chapterAddDialog() {
return { return {
type: 'action', type: 'action',
actionType: 'dialog', actionType: 'dialog',
dialog: { dialog: {
title: '新增章节', title: '新增章节',
size: 'md', size: 'md',
body: { body: {
...chapterForm(), ...chapterForm(),
api: '${base}/chapter/save/${bookId}', api: '${base}/chapter/save/${bookId}',
}, },
}, },
} }
} }
function chapterDetailDialog() { function chapterDetailDialog() {
return { return {
type: 'action', type: 'action',
actionType: 'dialog', actionType: 'dialog',
dialog: { dialog: {
title: '编辑章节', title: '编辑章节',
size: 'md', size: 'lg',
body: { actions: [],
...chapterForm(), body: [
initApi: '${base}/chapter/detail/${chapterId}', {
api: '${base}/chapter/save/${bookId}', 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}',
},
]
}
],
},
]
},
}
} }

View File

@@ -1,37 +1,38 @@
function crudCommonOptions() { function crudCommonOptions() {
return { return {
affixHeader: false, affixHeader: false,
stopAutoRefreshWhenModalIsOpen: true, stopAutoRefreshWhenModalIsOpen: true,
resizable: false, resizable: false,
syncLocation: false, syncLocation: false,
silentPolling: true, silentPolling: true,
} }
} }
function horizontalFormOptions() { function horizontalFormOptions(left = 1) {
return { return {
mode: 'horizontal', mode: 'horizontal',
canAccessSuperData: false, canAccessSuperData: false,
horizontal: { horizontal: {
left: 1, left: left,
}, justify: true,
} },
}
} }
function formInputClearable() { function formInputClearable() {
return { return {
clearable: true, clearable: true,
clearValueOnEmpty: true, clearValueOnEmpty: true,
} }
} }
function paginationOption() { function paginationOption() {
return { return {
type: 'pagination', type: 'pagination',
mode: 'normal', mode: 'normal',
layout: 'total,perPage,pager', layout: 'total,perPage,pager',
maxButtons: 5, maxButtons: 5,
showPageInput: false, showPageInput: false,
total: '${total}' total: '${total}'
} }
} }

View File

@@ -31,345 +31,103 @@
<script src="components/book.js"></script> <script src="components/book.js"></script>
<script src="components/chapter.js"></script> <script src="components/chapter.js"></script>
<script> <script>
(function () { (function () {
let debug = false let debug = false
let amis = amisRequire('amis/embed') let amis = amisRequire('amis/embed')
let amisJSON = { let amisJSON = {
type: 'page', type: 'page',
title: '书籍中心', title: '书籍中心',
subTitle: '网络书籍精排版工具', subTitle: '网络书籍精排版工具',
body: [ body: [
{
id: 'book-list',
type: 'crud',
api: {
method: 'get',
url: '${base}/book/list',
data: {
page: '${page|default:1}',
size: '${size|default:10}'
}
},
...crudCommonOptions(),
headerToolbar: [
'reload',
{
label: '',
icon: 'fa fa-add',
...bookAddDialog(),
},
],
footerToolbar: [
paginationOption(),
],
columns: [
{
name: 'name',
label: '名称',
width: 150,
},
{
name: 'description',
label: '描述',
},
{
type: 'operation',
label: '操作',
fixed: 'right',
className: 'nowrap',
width: 100,
buttons: [
{ {
type: 'crud', type: 'action',
api: { label: '跳转',
method: 'get', actionType: 'url',
url: '${base}/book/list', url: '${source}',
data: { blank: true,
page: '${page|default:1}',
size: '${size|default:10}'
}
},
...crudCommonOptions(),
headerToolbar: [
'reload',
{
label: '',
icon: 'fa fa-add',
...bookAddDialog(),
},
],
footerToolbar: [
paginationOption(),
],
columns: [
{
name: 'name',
label: '名称',
width: 150,
},
{
name: 'description',
label: '描述',
},
{
type: 'operation',
label: '操作',
fixed: 'right',
className: 'nowrap',
width: 100,
buttons: [
{
type: 'action',
label: '跳转',
actionType: 'url',
url: '${source}',
blank: true,
},
{
type: 'action',
label: '导出',
actionType: 'download',
api: '${base}/book/export/${bookId}',
},
{
type: 'action',
label: '编辑',
...bookDetailDialog(),
},
{
type: 'action',
label: '编辑章节',
actionType: 'dialog',
dialog: {
title: '章节',
size: 'lg',
actions: [],
body: [
{
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: 'dialog',
dialog: {
title: '导入正文',
size: 'lg',
body: {
debug: '${debug}',
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,
},
],
},
},
},
{
type: 'action',
label: '导出',
actionType: 'download',
api: '${base}/chapter/export/${chapterId}',
},
{
type: 'action',
label: '编辑正文',
actionType: 'dialog',
dialog: {
title: '编辑正文',
size: 'lg',
actions: [],
body: {
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',
],
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}',
},
]
}
],
},
},
},
{
type: 'action',
label: '删除',
level: 'danger',
confirmTitle: '确认删除',
confirmText: '确认删除名称为「${name}」的章节吗?',
actionType: 'ajax',
api: 'get:${base}/chapter/remove/${chapterId}',
},
],
},
],
},
],
},
},
{
type: 'action',
label: '删除',
level: 'danger',
confirmTitle: '确认删除',
confirmText: '确认删除名称为「${name}」的书籍吗?',
actionType: 'ajax',
api: 'get:${base}/book/remove/${bookId}',
},
],
},
],
}, },
], {
} type: 'action',
amis.embed( label: '导出',
'#root', actionType: 'download',
amisJSON, api: '${base}/book/export/${bookId}',
{
data: {
base: 'http://127.0.0.1:23890',
}, },
{
type: 'action',
label: '编辑',
...bookDetailDialog(),
},
{
type: 'action',
label: '删除',
level: 'danger',
confirmTitle: '确认删除',
confirmText: '确认删除名称为「${name}」的书籍吗?',
actionType: 'ajax',
api: 'get:${base}/book/remove/${bookId}',
},
],
}, },
{ ],
theme: 'antd', },
enableAMISDebug: debug, ],
}, }
); amis.embed(
if (debug) { '#root',
console.log('Source', amisJSON) amisJSON,
} {
})() data: {
base: 'http://127.0.0.1:23890',
},
},
{
theme: 'antd',
enableAMISDebug: debug,
},
);
if (debug) {
console.log('Source', amisJSON)
}
})()
</script> </script>
</html> </html>