java -fullversion openjdk full version "17.0.5+1"
安装
提前配置
安装以下:
-
Java (JDK-17或JDK-19)
-
git
可选安装以下:
-
Solr-9.6.x (用于内联网中的搜索栏)
-
Postgresql
Java版本
使用以下命令查看版本号:
Mac系统可使用brew命令安装Jdk-17:
brew install openjdk@17
克隆内联网模板项目
git clone https://github.com/Taack/intranet.git
该模板项目为精简版的内联网,您可以在其基础上构建更多应用。目前已包含人员管理系统“Crew”应用以及其安全依赖项:Spring Security Plugin。
运行您的内联网
进入克隆完成的内联网项目中,以下述命令运行其服务器:
./gradlew server:bootRun
如果您希望Gradle能实时监测文件改动以热重启服务器,请在运行服务器时添加 -Dgrails.run.active=true
选项:
./gradlew -Dgrails.run.active=true server:bootRun
短暂时间后,您应当能在控制台中看见:
Grails application running at http://localhost:9442
此时您就可以通过浏览器访问该网址以进入内联网,并用默认账户登陆 (用户名:admin;密码:ChangeIt)。密码可在 server/grails-app/conf/application.yml
中更改。
配置持久化数据库
如果您希望长久保存数据,请至 server/grails-app/conf/application.yml
文件中更改 dataSource
项。
例如处于开发者模式时我们希望使用H2存储数据:
environments:
development:
dataSource:
dbCreate: update (1)
url: jdbc:h2:./prodDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE (2)
1 | 模式:创建 (具体请见Grails GORM文档) |
2 | prodDb : 用于存储数据的文件的根目录 |
为Tomcat生成Jar文件
在这一步中,取代 bootRun
使用的是 assemble
:
./gradlew server:assemble
Jar文件将生成至 server/build/libs
文件夹。利用下述命令将其导入至您的Tomcat中:
cd server/build/libs
java -jar server-[version].jar
该次执行会更快, 并且静态文件将被压缩与串联。
请确保此时您尚未运行服务器 |
配置您的IDE
我们强烈建议您使用最新版IntelliJ Ultimate Edition,基于它对Groovy和Grails的全面支持。此外,我们也在探索更多可供选择的IDE,例如Eclipse或Visual Studio Code,又例如支持非Ultimate Edition版的IntelliJ。
IntelliJ Ultimate Edition
我们建议一并安装Taack插件,可从Intellij市场TaackUiASTAutocomplete或直接从源码库安装。
如何打开项目: intranet/settings.gradle
。
请确保Gradle使用的JDK版本为17 (或19):
Visual Studio Code
如果您强烈希望使用Visual Studio Code,我们建议以下扩展包以此使得Taack框架正常工作:
请谨记,即便VSCode无法识别Grails和Taack框架的大部分功能,但其仍然可正常使用。此外,VSCode也不会帮助您进行导入、代码导航等诸多便捷功能,因为其对Groovy的支持始终不如Intellij全面。
创建第一个应用 (app)
应用是如何与Taack框架协作的
您的内联网项目分为2个部分:
-
子应用 (app plugins):位于
app
文件夹中。每个子应用都能看成是一个微型项目,它们有自己的依赖、自己的build文件等等,例如Crew
就是其中一个子应用。 点击此处以获取更多如何向服务器声明子应用的深入解释。 -
主应用 (main server):通过
build.gradle
文件装载所有子应用,并将其呈现在内联网主页面上。
创建一个新的子应用 (app plugin)
要创建新的子应用 (基于Gradle模块),请先确保已经从我们的github库中拉取了最新版的内联网项目 (见安装)。
若已完成,您可于内联网根目录中执行以下命令:
./gradlew -PmodName=myToto server:generateTaackAppTask
一个新创建的子应用将出现在 app
文件夹下。
请重新加载Gradle (如下图所示点击目标按钮),使得IDE成功检测到该子应用:
未来的更新
大多数时候,更新内联网也就意味着会更新 buildSrc
文件夹以及 gradle.properties
文件,偶尔也会更新子应用的 build.gradle
文件。
事实上,这在长周期的软件开发项目中很常见,不应当让新手感到害怕。 |
创建子应用的菜单
首先,让我们着眼于导航栏。 第一步学习如何构建,基本代码如下:
static private UiMenuSpecifier buildMenu(String q = null) {
new UiMenuSpecifier().ui {
menu SomeController.&action as MC
menu this.&anotherAction as MC
// import org.codehaus.groovy.runtime.MethodClosure as MC
}
}
创建一个UiMenuSpecifier实例,并写入导航栏所需内容:
menu TurboController.&action as MC
以该行代码为例,menu
将在导航栏中创建一个简单链接,Action
为该链接显示的文本,TurboController.&action
(类型为MethodClosure) 则定义了该链接导向的目的地。
导航栏中可写入更多其他类型,详情请见Menu DSL。
创建表格
现在再来看看如何在我们的页面中呈现一张表格,样板如下:
UiTableSpecifier tableSpecifier = new UiTableSpecifier()
tableSpecifier.ui {
//Add table content inside the closure here
}
我们打算做一张表格,其内列出多个Book实例。 于是第一步应当先定义好表头:
new UiTableSpecifier().ui {
// -- Header --
header {
column {
fieldHeader object.title_
}
}
}
如图所示,表格的各项内容全都定义在一个闭包中,因此条件判断、循环等等的操作都可以实现。举个例子,我们想要某列只有 ROLE_ADMIN
用户才能看见:
// Look if the current user has the Role "ROLE_ADMIN"
User cu = springSecurityService.currentUser as User
boolean isAdmin = cu.authorities*.authority.contains("ROLE_ADMIN")
new UiTableSpecifier().ui {
header {
column {
label object.title_
}
// Column only shown to admin
if (isAdmin) {
column {
label "Delete book"
}
}
}
}
“Delete book”这列只会在用户是admin时出现。
接下来我们要填充内容进我们的表格,使用 iterate
遍历数据库中所有的Book实例:
User cu = springSecurityService.currentUser as User
boolean isAdmin = cu.authorities*.authority.contains("ROLE_ADMIN")
new UiTableSpecifier().ui {
header {
column {
label object.title_
}
// Column only shown to admin
if (isAdmin) {
column {
label "Delete book"
}
}
}
iterate(taackFilterService.getBuilder(Book)
.setMaxNumberOfLine(8)
.setSortOrder(TaackFilter.Order.DESC, object.title_)
.build()) { Book book ->
rowColumn {
rowField book.title_ //The underscore is needed here
}
// If the user is an admin display a column with a button link
// to redirect towards the book deletion action
if (isAdmin) {
rowColumn {
rowAction ActionIcon.DELETE,
this.&index as MC, book.id
}
}
}
}
我们会为每一个Book创建一条新行,第一列为书名,若用户是admin则第二列为 删除 按钮 (由于我们还未定义删除函数,因此我们暂时使该按钮导向至 index
)。
表格,即UiTableSpecifier,现已完成,接下来只需将其呈现在页面,请使用 taackUiService
(它应当已被 create-taack-app
命令导入至Controller控制器中)。
代码如下:
taackUiService.show(new UiBlockSpecifier().ui {
table tableSpecifier, BlockSpec.Width.MAX
}, buildMenu())
taackUiService.show(UiBlockSpecifier block, UiMenuSpecifier menu)
负责将我们指定的内容呈现出来。
在本例中,我们想要显示一个 ajaxBlock
, 其内包含着名为“Book”的 table
,因此我们将创建好的 tableSpecifier
作为传递参数,并设定表格宽度为 MAX
以占据整个页宽。我们还将之前完成的静态 buildMenu()
作为 show()
的第二个传递参数,以此使得该页面自带一条导航栏。
现在您可以启动服务器并进入新子应用中。表格应当已正常呈现,只不过暂无数据,因为此时数据库未有任何book。 于是下一步我们会学习如何创建一个表单,并保存数据对象至数据库中。
为表格添加按钮
我们将为您的Book表格添加一个按钮,点击后会跳出弹窗 (使用AJAX技术) 用于创建新book。 在代码中,我们只需给table添加一个闭包,如下图:
taackUiService.show(new UiBlockSpecifier().ui {
table 'Book table', tableSpecifier, BlockSpec.Width.MAX, {
//Added Closure here
if (isAdmin())
action ActionIcon.CREATE, this.&bookForm as MC
}
}, buildMenu())
仅需如此,admin用户就能在表格的右上角看见一个 创建 按钮。
该 action
方法由以下参数构成:
-
图标:必须为 ActionIcon 枚举类。
-
action:该按钮的重定向
创建表单并保存数据对象
接下来我们将要制作一个既能创建又可以更新数据对象的表单。
首先需要定义一个 bookForm
方法,并初始化一个实例:要么新建实例,要么根据传递而来的id参数直接读取已有实例。
def bookForm(Book book) {
book ?= new Book(params)
}
然后创建 FormSpecifier
以填充我们的表单内容。
UiFormSpecifier form = new UiFormSpecifier()
form.ui book, {
//Section of fields
section "Book details", {
field book.title_
field book.author_
}
//Save button
formAction this.&saveBook as MC
}
内容填充完成后,使用 taackUiService.show()
将其呈现在页面上。
UiBlockSpecifier b = new UiBlockSpecifier()
b.ui {
modal {
form form, BlockSpec.Width.MAX
}
}
taackUiService.show(b)
这一次我们不打算传递 buildMenu
进入show中,因为我们并不希望又一条导航栏出现在当前弹窗中!
也记得创建一个 saveBook
方法:
@Secured("ROLE_ADMIN")
@Transactional
def saveBook(String redirectAction) {
taackSaveService.saveThenReloadOrRenderErrors(Book)
}
备注:保存后不需要 redirectAction
,见Close Modal and reload page。
由于我们希望只有admin才能创建book,因此我们在方法上方添加了 @Secured
声明,更多安全验证类信息请查阅grails-spring-security-core文档。
显示数据对象详情
目前我们已经能够创建数据对象,并将所有对象显示在一个表格中。接下来我们将学习如何在弹窗中显示某个具体对象的详细信息。
我们要再次定义一个specifier,并且最后同样要使用 taackUiService.show()
将其呈现出来:
def showUser(Book book) {
// Define the show displayed fields
UiShowSpecifier showSpec = new UiShowSpecifier().ui(book, {
fieldLabeled book.title_
fieldLabeled book.author_
})
taackUiService.show(new UiBlockSpecifier().ui {
modal {
show showSpec
}
})
}
为了能够打开该弹窗,我们需要为表格的每行book都添加一个链接。请在每行的目标rowColumn中添加下列代码:
rowAction
ActionIcon.SHOW * StyleModifier.SCALE_DOWN, (1)
TurboController.&showBook as MC, book.id
1 | 这里我们使用了乘法运算符以减小图标的尺寸 |
它将在表格的单元格中创建一个小型图标,点击后会打开弹窗,其内显示该book的详细信息。
请注意这里我们让 ActionIcon
被一个 IconStyle
乘算,使得图标的尺寸被改变。
删除一个数据对象
记得我们在表格里添加过一个 删除 按钮吗?
现在就来让它发挥作用:将方法名改为"&deleteBook
",然后在控制器中创建一个相同名字的方法:
@Transactional
@Secured(['ROLE_ADMIN'])
def deleteBook(Book book) {
book.delete()
redirect action: 'index'
}
备注:有些时候,给数据添加一个 enable
属性来决定遮盖或显示该数据,会比直接删除它更好。
完毕!
我们使用了Grails的 delete
方法来从数据库删除一个book对象,并重定向至 index
方法以此回到表格页面。
现在您就拥有了一个针对book的完整CRUD流程,而无需任何HTML或GSP文件!
您已经做好了充分准备来探索Taack Ui框架的更多高级功能。
欢迎!