diff --git a/pom.xml b/pom.xml index bc28c14..c71883d 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ A demo project for fruit and vegetable store using Spring Boot. + UTF-8 17 1.9.10 @@ -69,6 +70,10 @@ fastjson2-kotlin 2.0.40 + + com.fasterxml.jackson.core + jackson-databind + org.mariadb.jdbc diff --git a/src/main/kotlin/team8/fruitable/FruitableApplication.kt b/src/main/kotlin/team8/fruitable/FruitableApplication.kt index d08b4f6..2608a58 100644 --- a/src/main/kotlin/team8/fruitable/FruitableApplication.kt +++ b/src/main/kotlin/team8/fruitable/FruitableApplication.kt @@ -5,14 +5,8 @@ import org.springframework.boot.runApplication import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -@RestController @SpringBootApplication -class FruitableApplication { - @RequestMapping("/") - fun home(): String { - return "Hello World!" - } -} +class FruitableApplication fun main(args: Array) { runApplication(*args) diff --git a/src/main/kotlin/team8/fruitable/controller/action/Account.kt b/src/main/kotlin/team8/fruitable/controller/action/Account.kt new file mode 100644 index 0000000..658e661 --- /dev/null +++ b/src/main/kotlin/team8/fruitable/controller/action/Account.kt @@ -0,0 +1,90 @@ +package team8.fruitable.controller.action + +import jakarta.servlet.http.Cookie +import jakarta.servlet.http.HttpServletResponse +import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.CookieValue +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.servlet.mvc.support.RedirectAttributes +import team8.fruitable.datebase.entity.User +import team8.fruitable.datebase.repository.AccountRepository + +@Controller +@RequestMapping("/action/account") +class Account(private val repository: AccountRepository) { + private fun getCurrentUser(token: String?): User? { + return token?.let(repository::findByToken) + } + + private fun hasAdminPermissions(token: String?): Boolean { + return this.getCurrentUser(token)?.isAdmin ?: false + } + + private fun updateToken(response: HttpServletResponse, token: String, path: String = "/", age: Int = 2678400) { + val cookie = Cookie("TOKEN", token) + cookie.path = path + cookie.maxAge = age + response.addCookie(cookie) + } + + fun error( + redirectAttributes: RedirectAttributes, + title: String, + message: String, + redirect: String = "redirect:/" + ): String { + redirectAttributes.addFlashAttribute("title", title) + redirectAttributes.addFlashAttribute("message", message) + redirectAttributes.addFlashAttribute("redirect", redirect) + return "redirect:/error" + } + + @PostMapping("/login") + fun login( + response: HttpServletResponse, + attributes: RedirectAttributes, + @CookieValue(value = "TOKEN", required = false) token: String?, + @RequestParam(name = "name") name: String, + @RequestParam(name = "password") password: String + ): String { + if (this.getCurrentUser(token) != null) return error(attributes, "登录", "当前已登录账户") + val user = repository.findByName(name) ?: return error(attributes, "登录", "账户不存在") + if (!user.testPassword(password)) return error(attributes, "登录", "密码错误") + user.updateToken() + user.updateLoginTime() + repository.save(user) + updateToken(response, user.token) + return "redirect:/"; + } + + @PostMapping("/register") + fun register( + response: HttpServletResponse, + attributes: RedirectAttributes, + @CookieValue(value = "TOKEN", required = false) token: String?, + @RequestParam(name = "name") name: String, + @RequestParam(name = "password") password: String, + @RequestParam(name = "age", required = false) age: Int?, + @RequestParam(name = "gender", required = false) gender: String?, + @RequestParam(name = "phone", required = false) phone: String?, + @RequestParam(name = "email", required = false) email: String?, + @RequestParam(name = "address", required = false) address: String? + ): String { + if (this.getCurrentUser(token) != null) return error(attributes, "登录", "当前已登录账户") + val user = repository.save(User(name, password, age, gender, phone, email, address)) + updateToken(response, user.token) + return "redirect:/"; + } + + @RequestMapping("/logout") + fun logout( + response: HttpServletResponse, + @RequestParam("redirect", required = false) redirect: String? + ): String { + updateToken(response, "", age = 0) + redirect?.let { return it } + return "redirect:/" + } +} \ No newline at end of file diff --git a/src/main/kotlin/team8/fruitable/controller/api/Account.kt b/src/main/kotlin/team8/fruitable/controller/api/AccountApi.kt similarity index 71% rename from src/main/kotlin/team8/fruitable/controller/api/Account.kt rename to src/main/kotlin/team8/fruitable/controller/api/AccountApi.kt index 6d36daa..59e66c1 100644 --- a/src/main/kotlin/team8/fruitable/controller/api/Account.kt +++ b/src/main/kotlin/team8/fruitable/controller/api/AccountApi.kt @@ -5,11 +5,12 @@ import jakarta.servlet.http.HttpServletResponse import org.springframework.web.bind.annotation.* import team8.fruitable.constant.UserType import team8.fruitable.datebase.entity.User -import team8.fruitable.datebase.repository.UserRepository -import team8.fruitable.util.ResultBuilder +import team8.fruitable.datebase.repository.AccountRepository +import team8.fruitable.util.ResultBuilderJson @RestController -class Account(private val repository: UserRepository) { +@RequestMapping("/api/v1/account") +class AccountApi(private val repository: AccountRepository) { private fun getCurrentUser(token: String?): User? { return token?.let(repository::findByToken) } @@ -25,50 +26,50 @@ class Account(private val repository: UserRepository) { response.addCookie(cookie) } - @GetMapping("/api/v1/account/info", produces = ["application/json"]) + @GetMapping("/info", produces = ["application/json"]) fun infoMe( @CookieValue(value = "TOKEN", required = false) token: String? ): String { - val user = this.getCurrentUser(token) ?: return ResultBuilder("账户未登录").toJson() - return ResultBuilder(user.asMap(UserType.User)).toJson() + val user = this.getCurrentUser(token) ?: return ResultBuilderJson("账户未登录").toJson() + return ResultBuilderJson(user.asMap(UserType.User)).toJson() } - @GetMapping("/api/v1/account/info/{id}", produces = ["application/json"]) + @GetMapping("/info/{id}", produces = ["application/json"]) fun info( @CookieValue(value = "TOKEN", required = false) token: String?, @PathVariable id: Long ): String { - val queryUser = repository.findById(id).orElse(null) ?: return ResultBuilder("账户不存在").toJson() + val queryUser = repository.findById(id).orElse(null) ?: return ResultBuilderJson("账户不存在").toJson() val currentUser = this.getCurrentUser(token) - return ResultBuilder( + return ResultBuilderJson( queryUser.asMap(if (currentUser != null && (currentUser.id == id || currentUser.isAdmin)) UserType.User else UserType.Guest) ).toJson() } //@PostMapping - @RequestMapping("/api/v1/account/login", produces = ["application/json"]) + @RequestMapping("/login", produces = ["application/json"]) fun login( response: HttpServletResponse, @CookieValue(value = "TOKEN", required = false) token: String?, @RequestParam(name = "name") name: String, @RequestParam(name = "password") password: String ): String { - if (this.getCurrentUser(token) != null) return ResultBuilder("当前已登录账户").toJson() - val user = repository.findByName(name) ?: return ResultBuilder("账户不存在").toJson() - if (!user.testPassword(password)) return ResultBuilder("密码错误").toJson() + if (this.getCurrentUser(token) != null) return ResultBuilderJson("当前已登录账户").toJson() + val user = repository.findByName(name) ?: return ResultBuilderJson("账户不存在").toJson() + if (!user.testPassword(password)) return ResultBuilderJson("密码错误").toJson() user.updateToken() user.updateLoginTime() repository.save(user) updateToken(response, user.token) - return ResultBuilder("success", "登录成功").toJson() + return ResultBuilderJson("success", "登录成功").toJson() } - @RequestMapping("/api/v1/account/logout", produces = ["application/json"]) + @RequestMapping("/logout", produces = ["application/json"]) fun logout(response: HttpServletResponse): String { updateToken(response, "", age = 0) - return ResultBuilder("success", "注销成功").toJson() + return ResultBuilderJson("success", "注销成功").toJson() } - @RequestMapping("/api/v1/account/register", produces = ["application/json"]) + @RequestMapping("/register", produces = ["application/json"]) fun register( response: HttpServletResponse, @CookieValue(value = "TOKEN", required = false) token: String?, @@ -80,13 +81,13 @@ class Account(private val repository: UserRepository) { @RequestParam(name = "email", required = false) email: String?, @RequestParam(name = "address", required = false) address: String? ): String { - if (this.getCurrentUser(token) != null) return ResultBuilder("当前已登录账户").toJson() + if (this.getCurrentUser(token) != null) return ResultBuilderJson("当前已登录账户").toJson() val user = repository.save(User(name, password, age, gender, phone, email, address)) updateToken(response, user.token) - return ResultBuilder("success", "注册成功").toJson() + return ResultBuilderJson("success", "注册成功").toJson() } - @RequestMapping("/api/v1/account/update", produces = ["application/json"]) + @RequestMapping("/update", produces = ["application/json"]) fun update( response: HttpServletResponse, @CookieValue(name = "TOKEN", required = false) token: String?, @@ -98,33 +99,33 @@ class Account(private val repository: UserRepository) { @RequestParam(name = "email", required = false) email: String?, @RequestParam(name = "address", required = false) address: String? ): String { - val user = this.getCurrentUser(token) ?: return ResultBuilder("账户未登录").toJson() + val user = this.getCurrentUser(token) ?: return ResultBuilderJson("账户未登录").toJson() user.update(name, password, age, gender, phone, email, address) repository.save(user) updateToken(response, user.token) - return ResultBuilder("success", "更新账户信息成功").toJson() + return ResultBuilderJson("success", "更新账户信息成功").toJson() } - @GetMapping("/api/v1/account/admin/info/{id}", produces = ["application/json"]) + @GetMapping("/admin/info/{id}", produces = ["application/json"]) fun infoAdmin( @CookieValue(value = "TOKEN", required = false) token: String?, @PathVariable id: Long, ): String { - if (this.hasAdminPermissions(token)) return ResultBuilder("无管理员权限").toJson() - return ResultBuilder(repository.findById(id)).toJson() + if (this.hasAdminPermissions(token)) return ResultBuilderJson("无管理员权限").toJson() + return ResultBuilderJson(repository.findById(id)).toJson() } - @DeleteMapping("/api/v1/account/admin/remove/{id}", produces = ["application/json"]) + @DeleteMapping("/admin/remove/{id}", produces = ["application/json"]) fun removeAdmin( @CookieValue(value = "TOKEN", required = false) token: String?, @PathVariable id: Long, ): String { - if (this.hasAdminPermissions(token)) return ResultBuilder("无管理员权限").toJson() + if (this.hasAdminPermissions(token)) return ResultBuilderJson("无管理员权限").toJson() // TODO: 完成管理员删除方法 - return ResultBuilder(repository.findById(id)).toJson() + return ResultBuilderJson(repository.findById(id)).toJson() } - @PostMapping("/api/v1/account/admin/update/{id}", produces = ["application/json"]) + @PostMapping("/admin/update/{id}", produces = ["application/json"]) fun updateAdmin( @CookieValue(value = "TOKEN", required = false) token: String?, @PathVariable id: Long, @@ -136,8 +137,8 @@ class Account(private val repository: UserRepository) { @RequestParam(name = "email", required = false) email: String?, @RequestParam(name = "address", required = false) address: String? ): String { - if (this.hasAdminPermissions(token)) return ResultBuilder("无管理员权限").toJson() + if (this.hasAdminPermissions(token)) return ResultBuilderJson("无管理员权限").toJson() // TODO: 完成管理员更新账户数据方法 - return ResultBuilder(repository.findById(id)).toJson() + return ResultBuilderJson(repository.findById(id)).toJson() } } diff --git a/src/main/kotlin/team8/fruitable/controller/api/Item.kt b/src/main/kotlin/team8/fruitable/controller/api/Item.kt index 266ac36..a6b9200 100644 --- a/src/main/kotlin/team8/fruitable/controller/api/Item.kt +++ b/src/main/kotlin/team8/fruitable/controller/api/Item.kt @@ -5,11 +5,11 @@ import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RestController import team8.fruitable.datebase.entity.User import team8.fruitable.datebase.repository.ItemRepository -import team8.fruitable.datebase.repository.UserRepository -import team8.fruitable.util.ResultBuilder +import team8.fruitable.datebase.repository.AccountRepository +import team8.fruitable.util.ResultBuilderJson @RestController -class Item(private val itemRepository: ItemRepository, private val userRepository: UserRepository) { +class Item(private val itemRepository: ItemRepository, private val userRepository: AccountRepository) { private fun getCurrentUser(token: String?): User? { return token?.let(userRepository::findByToken) } @@ -20,6 +20,6 @@ class Item(private val itemRepository: ItemRepository, private val userRepositor @GetMapping("/api/v1/item/info/{id}", produces = ["application/json"]) fun info(@PathVariable id: Long): String { - return ResultBuilder(itemRepository.findById(id).orElse(null)).toJson() + return ResultBuilderJson(itemRepository.findById(id).orElse(null)).toJson() } } diff --git a/src/main/kotlin/team8/fruitable/controller/page/Error.kt b/src/main/kotlin/team8/fruitable/controller/page/Error.kt new file mode 100644 index 0000000..58c3987 --- /dev/null +++ b/src/main/kotlin/team8/fruitable/controller/page/Error.kt @@ -0,0 +1,32 @@ +package team8.fruitable.controller.page + +import org.springframework.boot.web.servlet.error.ErrorController +import org.springframework.stereotype.Controller +import org.springframework.ui.Model +import org.springframework.ui.set +import org.springframework.web.bind.annotation.CookieValue +import org.springframework.web.bind.annotation.ModelAttribute +import org.springframework.web.bind.annotation.RequestMapping +import team8.fruitable.datebase.repository.AccountRepository + +@Controller +class Error(private val repository: AccountRepository) : ErrorController { + @RequestMapping("/error") + fun error( + model: Model, + @CookieValue("TOKEN", required = false) token: String?, + @ModelAttribute("title") title: String?, + @ModelAttribute("message") message: String?, + @ModelAttribute("redirect") redirect: String? + ): Model { + token?.let(repository::findByToken)?.let { model["user"] = it } + + model["error"] = mapOf( + "title" to title, + "message" to message, + "redirect" to redirect + ) + + return model + } +} diff --git a/src/main/kotlin/team8/fruitable/controller/page/Pages.kt b/src/main/kotlin/team8/fruitable/controller/page/Pages.kt new file mode 100644 index 0000000..2abdd83 --- /dev/null +++ b/src/main/kotlin/team8/fruitable/controller/page/Pages.kt @@ -0,0 +1,52 @@ +package team8.fruitable.controller.page + +import org.springframework.stereotype.Controller +import org.springframework.ui.Model +import org.springframework.ui.set +import org.springframework.web.bind.annotation.CookieValue +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.servlet.ModelAndView +import team8.fruitable.datebase.entity.User +import team8.fruitable.datebase.repository.AccountRepository + +@Controller +class Pages(private val repository: AccountRepository) { + private fun getCurrentUser(token: String?): User? { + return token?.let(repository::findByToken) + } + + private fun hasAdminPermissions(token: String?): Boolean { + return this.getCurrentUser(token)?.isAdmin ?: false + } + + @RequestMapping("/") + fun index( + model: Model, + @CookieValue("TOKEN", required = false) token: String? + ): String { + model["isIndex"] = true + this.getCurrentUser(token)?.let { model["user"] = it } + return "index" + } + + @RequestMapping("/login") + fun login( + model: Model, + @CookieValue("TOKEN", required = false) token: String? + ): String { + model["isLogin"] = true + this.getCurrentUser(token)?.let { model["user"] = it } + return "login" + } + + @RequestMapping("/register") + fun register( + model: Model, + @CookieValue("TOKEN", required = false) token: String? + ): String { + model["isRegister"] = true + this.getCurrentUser(token)?.let { model["user"] = it } + return "register" + } +} diff --git a/src/main/kotlin/team8/fruitable/datebase/entity/User.kt b/src/main/kotlin/team8/fruitable/datebase/entity/User.kt index 6a0c66f..2ef6a15 100644 --- a/src/main/kotlin/team8/fruitable/datebase/entity/User.kt +++ b/src/main/kotlin/team8/fruitable/datebase/entity/User.kt @@ -1,5 +1,6 @@ package team8.fruitable.datebase.entity +import com.fasterxml.jackson.annotation.JsonView import jakarta.persistence.* import team8.fruitable.constant.UserType import team8.fruitable.util.Util @@ -13,47 +14,65 @@ class User( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) + @JsonView(UserView::class) var id: Long? = null, @Column(name = "name", length = 16, nullable = false) + @JsonView(GuestView::class) var name: String? = null, @Column(name = "password", length = 64, nullable = false) + @JsonView(AdminView::class) var password: String? = null, @Column(name = "time_create", nullable = false) + @JsonView(UserView::class) var createTime: LocalDateTime = LocalDateTime.now(), @Column(name = "time_login", nullable = false) + @JsonView(UserView::class) var loginTime: LocalDateTime = LocalDateTime.now(), @Column(name = "token", length = 36) + @JsonView(AdminView::class) var token: String = UUID.randomUUID().toString(), @Column(name = "age") + @JsonView(GuestView::class) var age: Int? = null, @Column(name = "gender", length = 1) + @JsonView(GuestView::class) var gender: String? = null, @Column(name = "phone", length = 11) + @JsonView(UserView::class) var phone: String? = null, @Column(name = "email", length = 64) + @JsonView(GuestView::class) var email: String? = null, @Column(name = "address", length = 64) + @JsonView(UserView::class) var address: String? = null, @Column(name = "is_admin", nullable = false) + @JsonView(AdminView::class) var isAdmin: Boolean = false, @OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY, mappedBy = "carted") - var carts: MutableList = mutableListOf(), + @JsonView(UserView::class) + var carts: MutableList = mutableListOf(), @OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY, mappedBy = "user") - var orders: MutableList = mutableListOf() + @JsonView(UserView::class) + var orders: MutableList = mutableListOf() ) { + interface GuestView + interface UserView : GuestView + interface AdminView : UserView + constructor(name: String, password: String) : this() { this.name = name this.password = this.genPassword(password) diff --git a/src/main/kotlin/team8/fruitable/datebase/repository/UserRepository.kt b/src/main/kotlin/team8/fruitable/datebase/repository/AccountRepository.kt similarity index 79% rename from src/main/kotlin/team8/fruitable/datebase/repository/UserRepository.kt rename to src/main/kotlin/team8/fruitable/datebase/repository/AccountRepository.kt index cd9460b..314b09b 100644 --- a/src/main/kotlin/team8/fruitable/datebase/repository/UserRepository.kt +++ b/src/main/kotlin/team8/fruitable/datebase/repository/AccountRepository.kt @@ -3,7 +3,7 @@ package team8.fruitable.datebase.repository import org.springframework.data.repository.CrudRepository import team8.fruitable.datebase.entity.User -interface UserRepository : CrudRepository { +interface AccountRepository : CrudRepository { fun findByName(name: String): User? fun findByToken(token: String): User? } diff --git a/src/main/kotlin/team8/fruitable/util/ResultBuilder.kt b/src/main/kotlin/team8/fruitable/util/ResultBuilderJson.kt similarity index 97% rename from src/main/kotlin/team8/fruitable/util/ResultBuilder.kt rename to src/main/kotlin/team8/fruitable/util/ResultBuilderJson.kt index 551a190..5aff251 100644 --- a/src/main/kotlin/team8/fruitable/util/ResultBuilder.kt +++ b/src/main/kotlin/team8/fruitable/util/ResultBuilderJson.kt @@ -4,7 +4,7 @@ import com.alibaba.fastjson2.toJSONString import java.time.LocalDateTime import java.util.* -class ResultBuilder { +class ResultBuilderJson { var status: String = "auto" var message: String = "" val timestamp: LocalDateTime = LocalDateTime.now() diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 88ce562..3f3444f 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -7,3 +7,8 @@ spring: show-sql: true hibernate: ddl-auto: update +server: + servlet: + encoding: + charset: UTF-8 + force-response: true diff --git a/src/main/resources/static/images/add_to_cart.svg b/src/main/resources/static/images/add_to_cart.svg index 46d6be4..0625e09 100644 --- a/src/main/resources/static/images/add_to_cart.svg +++ b/src/main/resources/static/images/add_to_cart.svg @@ -1,7 +1,7 @@ - + - + - + - + - + diff --git a/src/main/resources/static/scripts/index.js b/src/main/resources/static/scripts/index.js index 7a36a8e..282aa99 100644 --- a/src/main/resources/static/scripts/index.js +++ b/src/main/resources/static/scripts/index.js @@ -7,7 +7,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; -import { getItems, getNextItem, hasNextItem, indexOf, loadItem, } from "./items.js"; +import {getItems, getNextItem, hasNextItem, indexOf, loadItem,} from "./items.js"; + const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); // 源数据 let rawBanners = []; @@ -135,7 +136,7 @@ function updateItems() { return __awaiter(this, void 0, void 0, function* () { let ID = this.getAttribute("item_id"); if (ID != null) { - window.location.href = `item.jsp?item_id=${ID}`; + window.location.href = `item/${ID}`; } }); }); diff --git a/src/main/resources/static/scripts/item.js b/src/main/resources/static/scripts/item.js index 55b0d89..85d9334 100644 --- a/src/main/resources/static/scripts/item.js +++ b/src/main/resources/static/scripts/item.js @@ -7,7 +7,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; -import { getItem, loadItem } from "./items.js"; +import {getItem, loadItem} from "./items.js"; + let ID; let TheItem; window.addEventListener("DOMContentLoaded", () => __awaiter(void 0, void 0, void 0, function* () { @@ -64,7 +65,7 @@ window.addEventListener("DOMContentLoaded", () => __awaiter(void 0, void 0, void }); // 为选项添加点击事件 Array.from(OptionDisplayer.children).forEach((v) => { - let contants = v.querySelector(".Contants"); + let contants = v.querySelector(".Contents"); Array.from(contants.children).forEach((v) => { v.addEventListener("click", function () { return __awaiter(this, void 0, void 0, function* () { diff --git a/src/main/resources/static/styles/clock.css b/src/main/resources/static/styles/clock.css index 8359889..41d62a5 100644 --- a/src/main/resources/static/styles/clock.css +++ b/src/main/resources/static/styles/clock.css @@ -7,7 +7,7 @@ width: 140px; color: rgb(0, 255, 90); text-align: center; - font-family: "LcdD"; + font-family: "LcdD", serif; border: 3px solid black; border-radius: 5px; background-color: rgba(0, 0, 0, 0.5); diff --git a/src/main/resources/static/styles/edit.css b/src/main/resources/static/styles/edit.css index 857a729..bd075c5 100644 --- a/src/main/resources/static/styles/edit.css +++ b/src/main/resources/static/styles/edit.css @@ -20,7 +20,7 @@ div { height: 500px; } -#Contents > .Editor { +#Contents > .Editors { flex: 4 0 0; flex-direction: column; justify-content: space-between; @@ -32,31 +32,31 @@ div { border-radius: 15px; } -#Contents > .Editor > .Title { +#Contents > .Editors > .Title { justify-content: space-between; } -#Contents > .Editor > .Result { +#Contents > .Editors > .Result { color: wheat; border: 3px solid skyblue; border-spacing: 0; border-radius: 10px; } -#Contents > .Editor > .Result td { +#Contents > .Editors > .Result td { white-space: nowrap; border: 1px solid cornflowerblue; } -#Contents > .Editor > .Result > thead { +#Contents > .Editors > .Result > thead { background-color: #044488; } -#Contents > .Editor > .Result > tbody { +#Contents > .Editors > .Result > tbody { background-color: #008080; } -#Contents > .Editor > .Pages > * { +#Contents > .Editors > .Pages > * { color: wheat; margin-right: 3px; border: 3px solid skyblue; @@ -64,15 +64,15 @@ div { background-color: cadetblue; } -#Contents > .Editor > .Pages > *:last-child { +#Contents > .Editors > .Pages > *:last-child { margin-right: inherit; } -#Contents > .Editor > .Pages > span { +#Contents > .Editors > .Pages > span { color: green; } -#Contents > .UserEditor { +#Contents > .EditPanel { flex: 1 0 0; margin: 5px; padding: 5px; @@ -81,8 +81,8 @@ div { border-radius: 15px; } -#Contents > .UserEditor > .Displayer { - border: 0px; +#Contents > .EditPanel > .Panel { + border: 0; } @media (min-width: 1280px) { diff --git a/src/main/resources/static/styles/error.css b/src/main/resources/static/styles/error.css index b0e4ccd..5dbcd3f 100644 --- a/src/main/resources/static/styles/error.css +++ b/src/main/resources/static/styles/error.css @@ -35,7 +35,7 @@ div { margin-top: 10px; margin-bottom: 10px; color: wheat; - font-family: "Normal"; + font-family: "Normal", system-ui; } .ErrorTitle { @@ -46,7 +46,7 @@ div { .ErrorRedirectButton { margin: 20px 15px; - padding: 8px 0px; + padding: 8px 0; border: 3px solid skyblue; border-radius: 10px; font-size: 18px; diff --git a/src/main/resources/static/styles/item.css b/src/main/resources/static/styles/item.css index ef2c564..14516ff 100644 --- a/src/main/resources/static/styles/item.css +++ b/src/main/resources/static/styles/item.css @@ -47,23 +47,23 @@ div { } .Item .Infos > * { - margin: 10px 0px; + margin: 10px 0; } .Item .Infos > .Title { font-size: 30px; font-weight: bold; - font-family: "Normal"; + font-family: "Normal", system-ui; } .Item .Infos .Description { font-size: 20px; - font-family: "Normal"; + font-family: "Normal", system-ui; } .Item .Infos .Prices { align-items: baseline; - font-family: "Normal"; + font-family: "Normal", system-ui; } .Item .Infos .Prices .VipPrice { @@ -79,7 +79,7 @@ div { } .Item .Infos .Prices .Price[has_vip_price] { - color: gray; + color: grey; font-size: 16px; font-weight: lighter; font-style: italic; @@ -92,32 +92,32 @@ div { .Item .Infos .Options > .Option { flex-direction: row; - margin: 5px 0px; + margin: 5px 0; border: 1px solid rgba(0, 120, 255, 0.8); border-radius: 5px; } .Item .Infos .Options > .Option .Title { font-weight: bold; - padding: 0px 10px; - border-radius: 5px 0px 0px 5px; + padding: 0 10px; + border-radius: 5px 0 0 5px; background: green; font-size: 20px; } -.Item .Infos .Options > .Option .Contants { +.Item .Infos .Options > .Option .Contents { flex-wrap: wrap; align-items: stretch; } -.Item .Infos .Options > .Option .Contants .Option { +.Item .Infos .Options > .Option .Contents .Option { font-size: 18px; - padding: 0px 5px; + padding: 0 5px; align-items: center; background-color: #355ae8; } -.Item .Infos .Options > .Option .Contants .Option[selected] { +.Item .Infos .Options > .Option .Contents .Option[selected] { font-weight: bold; background-color: #489dff; } diff --git a/src/main/resources/templates/cart.mustache b/src/main/resources/templates/cart.mustache new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/templates/edit.jsp b/src/main/resources/templates/edit.jsp index 600b39b..45b4a00 100644 --- a/src/main/resources/templates/edit.jsp +++ b/src/main/resources/templates/edit.jsp @@ -65,14 +65,14 @@
-
+
@@ -109,7 +109,7 @@ - +
首页 @@ -123,9 +123,9 @@ ">尾页
- -
- + +
+
diff --git a/src/main/resources/templates/edit.mustache b/src/main/resources/templates/edit.mustache new file mode 100644 index 0000000..f318cf6 --- /dev/null +++ b/src/main/resources/templates/edit.mustache @@ -0,0 +1,19 @@ +{{> header }} + + +
+
+ {{# userEditor }} + {{> editor/user }} + {{/ userEditor }} + {{# itemEditor }} + {{> editor/item }} + {{/ itemEditor }} +
+ +
+ +
+
+ +{{> footer }} \ No newline at end of file diff --git a/src/main/resources/templates/editor/item.mustache b/src/main/resources/templates/editor/item.mustache new file mode 100644 index 0000000..d7119e0 --- /dev/null +++ b/src/main/resources/templates/editor/item.mustache @@ -0,0 +1,37 @@ +{{> header }} + + +
+
+
+ 商品图片 +
+
+ +
+
+
+
+ +
+
+
数量
+
+
 1 
+
 2 
+
 3 
+
 4 
+
 5 
+
 6 
+
+
+
+
+ 立即购买 + 加入购物车 +
+
+
+
+ +{{> footer }} \ No newline at end of file diff --git a/src/main/resources/templates/editor/user.mustache b/src/main/resources/templates/editor/user.mustache new file mode 100644 index 0000000..5740da8 --- /dev/null +++ b/src/main/resources/templates/editor/user.mustache @@ -0,0 +1,69 @@ +
+ + + +
+ + + + + + + + + + + + + + + + + {{#Users}} + + + + + + + + + + + + + + {{/Users}} + +
ID用户名帐户创建时间最后登录时间年龄性别邮箱管理员操作
{{ Id }}{{ UserName }}{{ CreateDate }}{{ LastLoginDate }}{{ Age }}{{ Gender }}{{ Phone }}{{ Email }}{{ Address }}{{ IsAdmin }} + + +
+ +
+ 首页 + + + + + + + + + 尾页 +
diff --git a/src/main/resources/templates/error.jsp b/src/main/resources/templates/error.jsp deleted file mode 100644 index 8c72f70..0000000 --- a/src/main/resources/templates/error.jsp +++ /dev/null @@ -1,109 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" - pageEncoding="UTF-8"%> -<%@ taglib prefix="s" uri="/struts-tags" %> - - - - - - - "> - - 出错啦! - 67购物网站 - - - - - - - - - - - - - - - - - - - - -
-
-
失败
- -
原因:
- - -
将在三秒后跳转至页面:
- -
-
-
- - - - - -
- -
- -
- - diff --git a/src/main/resources/templates/error.mustache b/src/main/resources/templates/error.mustache new file mode 100644 index 0000000..0b7df97 --- /dev/null +++ b/src/main/resources/templates/error.mustache @@ -0,0 +1,42 @@ + + + + + 主页 - 在线果蔬商城 + + + + + + + + + + + + + + + + + + + + + + +{{> header }} + +
+
{{ title }} 失败
+
原因: {{ message }}
+ {{# redirect }} +
将在三秒后跳转至页面: {{ redirect }}
+ + {{/ redirect }} +
+ +{{> footer }} + + + diff --git a/src/main/resources/templates/footer.mustache b/src/main/resources/templates/footer.mustache index 713fbe7..2c6f3cf 100644 --- a/src/main/resources/templates/footer.mustache +++ b/src/main/resources/templates/footer.mustache @@ -1,7 +1,7 @@