update editor page, fix many bug
Signed-off-by: Puqns67 <me@puqns67.icu>
This commit is contained in:
parent
a5c0f4c35c
commit
ccadbd8160
Before Width: | Height: | Size: 937 B After Width: | Height: | Size: 937 B |
@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.PostMapping
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import org.springframework.web.bind.annotation.RequestParam
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
||||||
import team8.fruitable.controller.util.Util.Companion.error
|
import team8.fruitable.controller.util.error
|
||||||
import team8.fruitable.datebase.entity.User
|
import team8.fruitable.datebase.entity.User
|
||||||
import team8.fruitable.datebase.repository.AccountRepository
|
import team8.fruitable.datebase.repository.AccountRepository
|
||||||
|
|
||||||
|
@ -5,15 +5,16 @@ import org.springframework.stereotype.Controller
|
|||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import org.springframework.web.multipart.MultipartFile
|
import org.springframework.web.multipart.MultipartFile
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
||||||
import team8.fruitable.controller.util.Util.Companion.error
|
import team8.fruitable.controller.util.error
|
||||||
import team8.fruitable.datebase.entity.Item
|
import team8.fruitable.datebase.entity.Item
|
||||||
import team8.fruitable.datebase.entity.Tag
|
import team8.fruitable.datebase.entity.Tag
|
||||||
import team8.fruitable.datebase.entity.User
|
import team8.fruitable.datebase.entity.User
|
||||||
import team8.fruitable.datebase.repository.AccountRepository
|
import team8.fruitable.datebase.repository.AccountRepository
|
||||||
import team8.fruitable.datebase.repository.ItemRepository
|
import team8.fruitable.datebase.repository.ItemRepository
|
||||||
import team8.fruitable.datebase.repository.TagRepository
|
import team8.fruitable.datebase.repository.TagRepository
|
||||||
|
import team8.fruitable.util.hash
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/action/edit/item")
|
@RequestMapping("/action/edit/item")
|
||||||
@ -39,12 +40,12 @@ class ItemEditor(
|
|||||||
|
|
||||||
private fun autoCreateTag(tags: List<String>): MutableList<Tag> {
|
private fun autoCreateTag(tags: List<String>): MutableList<Tag> {
|
||||||
val result: MutableList<Tag> = mutableListOf()
|
val result: MutableList<Tag> = mutableListOf()
|
||||||
for (i in tags) {
|
tags.forEach {
|
||||||
val oldTag = tagRepository.findByName(i);
|
val oldTag = tagRepository.findByName(it)
|
||||||
if (oldTag != null) {
|
if (oldTag != null) {
|
||||||
result.add(oldTag)
|
result.add(oldTag)
|
||||||
} else {
|
} else {
|
||||||
val newTag = Tag(i)
|
val newTag = Tag(it)
|
||||||
tagRepository.save(newTag)
|
tagRepository.save(newTag)
|
||||||
result.add(newTag)
|
result.add(newTag)
|
||||||
}
|
}
|
||||||
@ -56,10 +57,11 @@ class ItemEditor(
|
|||||||
return this.autoCreateTag(tags.split(" "))
|
return this.autoCreateTag(tags.split(" "))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun uploadPicture(file: MultipartFile): String {
|
private fun uploadPicture(file: MultipartFile): String? {
|
||||||
val uuid = UUID.randomUUID().toString()
|
if (file.isEmpty) return null
|
||||||
file.transferTo(File("${pictureUploadPath}${File.separator}${uuid}"))
|
val hash = hash(file)
|
||||||
return uuid
|
file.transferTo(Path("${pictureUploadPath}${File.separator}${hash}"))
|
||||||
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
@ -74,8 +76,9 @@ class ItemEditor(
|
|||||||
@RequestParam("redirect", required = false) redirect: String?
|
@RequestParam("redirect", required = false) redirect: String?
|
||||||
): String {
|
): String {
|
||||||
if (!this.hasAdminPermissions(token)) return error(attributes, "创建商品", "账户无权限")
|
if (!this.hasAdminPermissions(token)) return error(attributes, "创建商品", "账户无权限")
|
||||||
if (name.isBlank()) return error(attributes, "更新商品", "商品名不能为空")
|
if (name.isBlank()) return error(attributes, "创建商品", "商品名不能为空")
|
||||||
if (price == 0.0) return error(attributes, "更新商品", "商品价格不能为0")
|
if (price == 0.0) return error(attributes, "创建商品", "商品价格不能为0")
|
||||||
|
if (picture.isEmpty) return error(attributes, "创建商品", "必须上传商品图片")
|
||||||
itemRepository.save(Item(name, price, description, uploadPicture(picture), tag?.let { this.autoCreateTag(it) }))
|
itemRepository.save(Item(name, price, description, uploadPicture(picture), tag?.let { this.autoCreateTag(it) }))
|
||||||
redirect?.let { return "redirect:/${it}" }
|
redirect?.let { return "redirect:/${it}" }
|
||||||
return "redirect:/editor?use=item"
|
return "redirect:/editor?use=item"
|
||||||
@ -97,7 +100,12 @@ class ItemEditor(
|
|||||||
if (name.isBlank()) return error(attributes, "更新商品", "商品名不能为空")
|
if (name.isBlank()) return error(attributes, "更新商品", "商品名不能为空")
|
||||||
if (price == 0.0) return error(attributes, "更新商品", "商品价格不能为0")
|
if (price == 0.0) return error(attributes, "更新商品", "商品价格不能为0")
|
||||||
val item = itemRepository.findById(id).orElse(null) ?: return error(attributes, "更新商品", "未找到目标商品")
|
val item = itemRepository.findById(id).orElse(null) ?: return error(attributes, "更新商品", "未找到目标商品")
|
||||||
item.update(name, price, description, picture?.let { uploadPicture(it) }, tag?.let { this.autoCreateTag(it) })
|
item.update(
|
||||||
|
name,
|
||||||
|
price,
|
||||||
|
description,
|
||||||
|
picture?.let { this.uploadPicture(it) },
|
||||||
|
tag?.let { this.autoCreateTag(it) })
|
||||||
itemRepository.save(item)
|
itemRepository.save(item)
|
||||||
redirect?.let { return "redirect:/${it}" }
|
redirect?.let { return "redirect:/${it}" }
|
||||||
return "redirect:/editor?use=item"
|
return "redirect:/editor?use=item"
|
||||||
|
@ -5,7 +5,7 @@ import jakarta.servlet.http.HttpServletResponse
|
|||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
||||||
import team8.fruitable.controller.util.Util.Companion.error
|
import team8.fruitable.controller.util.error
|
||||||
import team8.fruitable.datebase.entity.User
|
import team8.fruitable.datebase.entity.User
|
||||||
import team8.fruitable.datebase.repository.AccountRepository
|
import team8.fruitable.datebase.repository.AccountRepository
|
||||||
|
|
||||||
@ -64,8 +64,14 @@ class UserEditor(private val accountRepository: AccountRepository) {
|
|||||||
): String {
|
): String {
|
||||||
val currentUser = this.getCurrentUser(token) ?: return error(attributes, "更新用户", "账户未登录")
|
val currentUser = this.getCurrentUser(token) ?: return error(attributes, "更新用户", "账户未登录")
|
||||||
if (!this.hasAdminPermissions(currentUser)) return error(attributes, "更新用户", "账户无权限")
|
if (!this.hasAdminPermissions(currentUser)) return error(attributes, "更新用户", "账户无权限")
|
||||||
val user = accountRepository.findById(id).orElse(null) ?: return error(attributes, "更新用户", "未找到此用户", "/editor?use=user")
|
val user = accountRepository.findById(id).orElse(null) ?: return error(
|
||||||
|
attributes,
|
||||||
|
"更新用户",
|
||||||
|
"未找到此用户",
|
||||||
|
"/editor?use=user"
|
||||||
|
)
|
||||||
user.update(name, password, age, gender, phone, email, address, isAdmin)
|
user.update(name, password, age, gender, phone, email, address, isAdmin)
|
||||||
|
accountRepository.save(user)
|
||||||
if (currentUser.id == id) {
|
if (currentUser.id == id) {
|
||||||
val cookie = Cookie("TOKEN", user.token)
|
val cookie = Cookie("TOKEN", user.token)
|
||||||
cookie.path = "/"
|
cookie.path = "/"
|
||||||
@ -86,7 +92,12 @@ class UserEditor(private val accountRepository: AccountRepository) {
|
|||||||
val currentUser = this.getCurrentUser(token) ?: return error(attributes, "删除用户", "账户未登录")
|
val currentUser = this.getCurrentUser(token) ?: return error(attributes, "删除用户", "账户未登录")
|
||||||
if (!this.hasAdminPermissions(currentUser)) return error(attributes, "删除用户", "账户无权限")
|
if (!this.hasAdminPermissions(currentUser)) return error(attributes, "删除用户", "账户无权限")
|
||||||
if (currentUser.id == id) return error(attributes, "删除用户", "无法删除当前使用的账户", "/editor?use=user")
|
if (currentUser.id == id) return error(attributes, "删除用户", "无法删除当前使用的账户", "/editor?use=user")
|
||||||
val user = accountRepository.findById(id).orElse(null) ?: return error(attributes, "删除用户", "未找到此用户", "/editor?use=user")
|
val user = accountRepository.findById(id).orElse(null) ?: return error(
|
||||||
|
attributes,
|
||||||
|
"删除用户",
|
||||||
|
"未找到此用户",
|
||||||
|
"/editor?use=user"
|
||||||
|
)
|
||||||
accountRepository.delete(user)
|
accountRepository.delete(user)
|
||||||
redirect?.let { return "redirect:/${it}" }
|
redirect?.let { return "redirect:/${it}" }
|
||||||
return "redirect:/editor?use=user"
|
return "redirect:/editor?use=user"
|
||||||
|
@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.CookieValue
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import org.springframework.web.bind.annotation.RequestParam
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
||||||
import team8.fruitable.controller.util.Util
|
import team8.fruitable.controller.util.error
|
||||||
import team8.fruitable.datebase.entity.User
|
import team8.fruitable.datebase.entity.User
|
||||||
import team8.fruitable.datebase.repository.AccountRepository
|
import team8.fruitable.datebase.repository.AccountRepository
|
||||||
import team8.fruitable.datebase.repository.ItemRepository
|
import team8.fruitable.datebase.repository.ItemRepository
|
||||||
@ -20,8 +20,8 @@ import team8.fruitable.datebase.repository.PagebleItemRepository
|
|||||||
class Editor(
|
class Editor(
|
||||||
private val accountRepository: AccountRepository,
|
private val accountRepository: AccountRepository,
|
||||||
private val itemRepository: ItemRepository,
|
private val itemRepository: ItemRepository,
|
||||||
private val pagebleAccountRepository: PagebleAccountRepository,
|
private val pagingAccountRepository: PagebleAccountRepository,
|
||||||
private val pagebleItemRepository: PagebleItemRepository
|
private val pagingItemRepository: PagebleItemRepository
|
||||||
) {
|
) {
|
||||||
private fun getCurrentUser(token: String?): User? {
|
private fun getCurrentUser(token: String?): User? {
|
||||||
return token?.let(accountRepository::findByToken)
|
return token?.let(accountRepository::findByToken)
|
||||||
@ -74,15 +74,17 @@ class Editor(
|
|||||||
@RequestParam("search_content", required = false) searchContent: String?,
|
@RequestParam("search_content", required = false) searchContent: String?,
|
||||||
@RequestParam("page", required = false) page: Int?,
|
@RequestParam("page", required = false) page: Int?,
|
||||||
): String {
|
): String {
|
||||||
val user = this.getCurrentUser(token) ?: return Util.error(attributes, "更新", "账户未登录", "/login")
|
val user = this.getCurrentUser(token) ?: return error(attributes, "编辑", "账户未登录", "/login")
|
||||||
if (!this.hasAdminPermissions(user)) return Util.error(attributes, "编辑", "账户无权限编辑网站")
|
if (!this.hasAdminPermissions(user)) return error(attributes, "编辑", "账户无权限编辑网站")
|
||||||
|
model["isEditor"] = true
|
||||||
|
model["user"] = user
|
||||||
model["using"] = use
|
model["using"] = use
|
||||||
model["tabs"] = editorTabs
|
model["tabs"] = editorTabs
|
||||||
if (searchContent != null) model["searching"] = searchContent
|
if (searchContent != null) model["searching"] = searchContent
|
||||||
val pageRequested = PageRequest.of(page ?: 0, 15)
|
val pageRequested = PageRequest.of(page ?: 0, 15)
|
||||||
val data = when (use) {
|
val data = when (use) {
|
||||||
"item" -> pagebleItemRepository.findAll(pageRequested)
|
"item" -> pagingItemRepository.findAll(pageRequested)
|
||||||
else -> pagebleAccountRepository.findAll(pageRequested)
|
else -> pagingAccountRepository.findAll(pageRequested)
|
||||||
}
|
}
|
||||||
model["data"] = data
|
model["data"] = data
|
||||||
model["pages"] = (page ?: 0).let {
|
model["pages"] = (page ?: 0).let {
|
||||||
@ -115,15 +117,15 @@ class Editor(
|
|||||||
"item" -> {
|
"item" -> {
|
||||||
target?.let {
|
target?.let {
|
||||||
itemRepository.findById(it)
|
itemRepository.findById(it)
|
||||||
.orElse(null) ?: return Util.error(attributes, "编辑", "无法找到目标商品")
|
.orElse(null) ?: return error(attributes, "编辑", "无法找到目标商品")
|
||||||
} ?: return Util.error(attributes, "编辑", "目标商品为空")
|
} ?: return error(attributes, "编辑", "目标商品为空")
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val targetUser = target?.let {
|
val targetUser = target?.let {
|
||||||
accountRepository.findById(it)
|
accountRepository.findById(it)
|
||||||
.orElse(null) ?: return Util.error(attributes, "编辑", "无法找到目标用户")
|
.orElse(null) ?: return error(attributes, "编辑", "无法找到目标用户")
|
||||||
} ?: return Util.error(attributes, "编辑", "目标用户为空")
|
} ?: return error(attributes, "编辑", "目标用户为空")
|
||||||
if (action == "updating") when (targetUser.gender) {
|
if (action == "updating") when (targetUser.gender) {
|
||||||
"M" -> model["isGenderAsMale"] = true
|
"M" -> model["isGenderAsMale"] = true
|
||||||
"F" -> model["isGenderAsFemale"] = true
|
"F" -> model["isGenderAsFemale"] = true
|
||||||
|
@ -6,7 +6,7 @@ import org.springframework.ui.set
|
|||||||
import org.springframework.web.bind.annotation.CookieValue
|
import org.springframework.web.bind.annotation.CookieValue
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
||||||
import team8.fruitable.controller.util.Util.Companion.error
|
import team8.fruitable.controller.util.error
|
||||||
import team8.fruitable.datebase.entity.User
|
import team8.fruitable.datebase.entity.User
|
||||||
import team8.fruitable.datebase.repository.AccountRepository
|
import team8.fruitable.datebase.repository.AccountRepository
|
||||||
|
|
||||||
|
@ -2,18 +2,14 @@ package team8.fruitable.controller.util
|
|||||||
|
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes
|
||||||
|
|
||||||
class Util {
|
fun error(
|
||||||
companion object {
|
|
||||||
fun error(
|
|
||||||
redirectAttributes: RedirectAttributes,
|
redirectAttributes: RedirectAttributes,
|
||||||
title: String,
|
title: String,
|
||||||
message: String,
|
message: String,
|
||||||
redirect: String = "/"
|
redirect: String = "/"
|
||||||
): String {
|
): String {
|
||||||
redirectAttributes.addFlashAttribute("title", title)
|
redirectAttributes.addFlashAttribute("title", title)
|
||||||
redirectAttributes.addFlashAttribute("message", message)
|
redirectAttributes.addFlashAttribute("message", message)
|
||||||
redirectAttributes.addFlashAttribute("redirect", redirect)
|
redirectAttributes.addFlashAttribute("redirect", redirect)
|
||||||
return "redirect:/error"
|
return "redirect:/error"
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package team8.fruitable.datebase.entity
|
package team8.fruitable.datebase.entity
|
||||||
|
|
||||||
import jakarta.persistence.*
|
import jakarta.persistence.*
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -26,7 +27,7 @@ class Item(
|
|||||||
@Column(name = "description", length = 320, nullable = true)
|
@Column(name = "description", length = 320, nullable = true)
|
||||||
var description: String? = null,
|
var description: String? = null,
|
||||||
|
|
||||||
@Column(name = "picture", length = 36, nullable = true)
|
@Column(name = "picture", length = 64, nullable = true)
|
||||||
var picture: String? = null,
|
var picture: String? = null,
|
||||||
|
|
||||||
@Column(name = "is_removed", nullable = false)
|
@Column(name = "is_removed", nullable = false)
|
||||||
@ -36,11 +37,20 @@ class Item(
|
|||||||
@JoinColumn(name = "carts")
|
@JoinColumn(name = "carts")
|
||||||
var carted: User? = null,
|
var carted: User? = null,
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "items")
|
@JoinTable(
|
||||||
var ordered: Order? = null,
|
name = "orders_to_items",
|
||||||
|
joinColumns = [JoinColumn(name = "item_id", nullable = false)],
|
||||||
|
inverseJoinColumns = [JoinColumn(name = "order_id", nullable = false)]
|
||||||
|
)
|
||||||
|
var orders: MutableList<Order> = mutableListOf(),
|
||||||
|
|
||||||
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY, mappedBy = "tagedItem")
|
@ManyToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
|
||||||
|
@JoinTable(
|
||||||
|
name = "items_to_tags",
|
||||||
|
joinColumns = [JoinColumn(name = "item_id")],
|
||||||
|
inverseJoinColumns = [JoinColumn(name = "tag_id")]
|
||||||
|
)
|
||||||
var tags: MutableList<Tag> = mutableListOf(),
|
var tags: MutableList<Tag> = mutableListOf(),
|
||||||
|
|
||||||
@OneToOne(mappedBy = "item")
|
@OneToOne(mappedBy = "item")
|
||||||
@ -77,7 +87,7 @@ class Item(
|
|||||||
this.price = price?.let { if (it <= 0) null else it }
|
this.price = price?.let { if (it <= 0) null else it }
|
||||||
if (!description.isNullOrBlank()) this.description = description
|
if (!description.isNullOrBlank()) this.description = description
|
||||||
if (!picture.isNullOrBlank()) this.picture = picture
|
if (!picture.isNullOrBlank()) this.picture = picture
|
||||||
tags?.let { this.tags.addAll(it) }
|
tags?.let { this.tags = it }
|
||||||
isRemoved?.let { this.isRemoved = it }
|
isRemoved?.let { this.isRemoved = it }
|
||||||
this.updateEditTime()
|
this.updateEditTime()
|
||||||
}
|
}
|
||||||
@ -85,4 +95,14 @@ class Item(
|
|||||||
fun updateEditTime() {
|
fun updateEditTime() {
|
||||||
this.editTime = LocalDateTime.now()
|
this.editTime = LocalDateTime.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun getTagPretty(): String {
|
||||||
|
return this.tags.joinToString(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun getTagString(): String {
|
||||||
|
return this.tags.joinToString(" ")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ class Order(
|
|||||||
@Column(name = "address", length = 64, nullable = false)
|
@Column(name = "address", length = 64, nullable = false)
|
||||||
var address: String? = null,
|
var address: String? = null,
|
||||||
|
|
||||||
@ManyToOne(optional = false)
|
@ManyToOne
|
||||||
@JoinColumn(name = "pay_type")
|
@JoinColumn(name = "order_id")
|
||||||
var payType: PayType? = null,
|
var payType: PayType? = null,
|
||||||
|
|
||||||
@Column(name = "pay_status", nullable = false)
|
@Column(name = "pay_status", nullable = false)
|
||||||
@ -37,6 +37,11 @@ class Order(
|
|||||||
@Column(name = "price", nullable = false)
|
@Column(name = "price", nullable = false)
|
||||||
var priceSum: Double = .0,
|
var priceSum: Double = .0,
|
||||||
|
|
||||||
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY, mappedBy = "ordered")
|
@ManyToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
|
||||||
var items: MutableList<Item> = mutableListOf<Item>()
|
@JoinTable(
|
||||||
|
name = "orders_to_items",
|
||||||
|
joinColumns = [JoinColumn(name = "order_id", nullable = false)],
|
||||||
|
inverseJoinColumns = [JoinColumn(name = "item_id", nullable = false)]
|
||||||
|
)
|
||||||
|
var items: MutableList<Item> = mutableListOf()
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package team8.fruitable.datebase.entity
|
||||||
|
|
||||||
|
import jakarta.persistence.*
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "orders_to_items")
|
||||||
|
class OrderedItem(
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id", nullable = false)
|
||||||
|
var id: Long? = null,
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "order_id")
|
||||||
|
var order: Order? = null,
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "item_id")
|
||||||
|
var item: Item? = null,
|
||||||
|
|
||||||
|
@Column(name = "number", nullable = false)
|
||||||
|
var number: Int? = 0
|
||||||
|
)
|
@ -13,6 +13,7 @@ class PayType(
|
|||||||
@Column(name = "NAME", length = 16, nullable = false)
|
@Column(name = "NAME", length = 16, nullable = false)
|
||||||
var name: String? = null,
|
var name: String? = null,
|
||||||
|
|
||||||
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY, mappedBy = "payType")
|
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.LAZY)
|
||||||
var orders: MutableList<Order> = mutableListOf<Order>()
|
@JoinColumn(name = "pay_type", nullable = false)
|
||||||
|
var orders: MutableList<Order> = mutableListOf()
|
||||||
)
|
)
|
||||||
|
@ -13,11 +13,19 @@ class Tag(
|
|||||||
@Column(name = "name", length = 16, nullable = false)
|
@Column(name = "name", length = 16, nullable = false)
|
||||||
var name: String? = null,
|
var name: String? = null,
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToMany(cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "taged")
|
@JoinTable(
|
||||||
var tagedItem: Item? = null
|
name = "items_to_tags",
|
||||||
|
joinColumns = [JoinColumn(name = "tag_id", referencedColumnName = "id")],
|
||||||
|
inverseJoinColumns = [JoinColumn(name = "item_id", referencedColumnName = "id")]
|
||||||
|
)
|
||||||
|
var items: MutableList<Item> = mutableListOf()
|
||||||
) {
|
) {
|
||||||
constructor(name: String) : this() {
|
constructor(name: String) : this() {
|
||||||
this.name = name
|
this.name = name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return this.name ?: super.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package team8.fruitable.datebase.entity
|
package team8.fruitable.datebase.entity
|
||||||
|
|
||||||
import jakarta.persistence.*
|
import jakarta.persistence.*
|
||||||
import team8.fruitable.util.Util
|
import org.springframework.context.annotation.Bean
|
||||||
|
import team8.fruitable.util.hash
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ class User(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final fun genPassword(password: String): String {
|
private final fun genPassword(password: String): String {
|
||||||
return Util.hash(password)
|
return hash(password)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun update(
|
fun update(
|
||||||
@ -93,15 +94,16 @@ class User(
|
|||||||
if (!password.isNullOrBlank()) this.updatePassword(password)
|
if (!password.isNullOrBlank()) this.updatePassword(password)
|
||||||
this.age = age?.let { if (it <= 0) null else it }
|
this.age = age?.let { if (it <= 0) null else it }
|
||||||
if (!gender.isNullOrBlank()) this.gender = gender
|
if (!gender.isNullOrBlank()) this.gender = gender
|
||||||
if (!phone.isNullOrBlank()) this.phone = phone
|
this.phone = phone?.ifBlank { null }
|
||||||
if (!email.isNullOrBlank()) this.email = email
|
println("email: ${email?.ifBlank { "null" } ?: "null"}")
|
||||||
if (!address.isNullOrBlank()) this.address = address
|
this.email = email?.ifBlank { null }
|
||||||
|
this.address = address?.ifBlank { null }
|
||||||
isAdmin?.let { this.isAdmin = it }
|
isAdmin?.let { this.isAdmin = it }
|
||||||
this.updateToken()
|
this.updateToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testPassword(password: String): Boolean {
|
fun testPassword(password: String): Boolean {
|
||||||
return Util.hash(password) == this.password
|
return hash(password) == this.password
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePassword(password: String) {
|
fun updatePassword(password: String) {
|
||||||
@ -115,4 +117,13 @@ class User(
|
|||||||
fun updateToken() {
|
fun updateToken() {
|
||||||
this.token = UUID.randomUUID().toString()
|
this.token = UUID.randomUUID().toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun getGenderPretty(): String {
|
||||||
|
return when (this.gender) {
|
||||||
|
"F" -> "女性"
|
||||||
|
"M" -> "男性"
|
||||||
|
else -> "未知"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
package team8.fruitable.util
|
package team8.fruitable.util
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
|
||||||
class Util {
|
fun hash(bytes: ByteArray, algorithm: String = "SHA-256"): String {
|
||||||
companion object {
|
return MessageDigest.getInstance(algorithm).digest(bytes)
|
||||||
fun hash(input: String, algorithm: String = "SHA-256"): String {
|
|
||||||
return MessageDigest.getInstance(algorithm).digest(input.toByteArray())
|
|
||||||
.joinToString("") { "%02x".format(it) }
|
.joinToString("") { "%02x".format(it) }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fun hash(string: String, algorithm: String = "SHA-256"): String {
|
||||||
|
return hash(string.toByteArray(), algorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hash(multipartFile: MultipartFile, algorithm: String = "SHA-256"): String {
|
||||||
|
return hash(multipartFile.bytes, algorithm)
|
||||||
}
|
}
|
||||||
|
@ -49,10 +49,10 @@ div {
|
|||||||
|
|
||||||
> .ResultPanel {
|
> .ResultPanel {
|
||||||
flex: 2 auto;
|
flex: 2 auto;
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
td {
|
> .Result {
|
||||||
border: 1px solid cornflowerblue;
|
flex-grow: 1;
|
||||||
}
|
|
||||||
|
|
||||||
> thead {
|
> thead {
|
||||||
background-color: #044488;
|
background-color: #044488;
|
||||||
@ -61,6 +61,11 @@ div {
|
|||||||
> tbody {
|
> tbody {
|
||||||
background-color: #008080;
|
background-color: #008080;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border: 1px solid cornflowerblue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .EditPanel {
|
> .EditPanel {
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
.TabForm {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TabForm > .TabTitle {
|
|
||||||
margin: 20px 0px;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TabForm > .TabFormItems {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TabForm > .TabFormItems > .TabFormItem {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TabForm > .TabFormItems > .TabFormItem > .ItemName {
|
|
||||||
flex: 1 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TabForm > .TabFormItems > .TabFormItem > .ItemInput {
|
|
||||||
flex: 1 0 0;
|
|
||||||
margin-left: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 3px solid skyblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TabForm > .TabButtons > .TabButton {
|
|
||||||
flex: 1;
|
|
||||||
margin: 20px 15px;
|
|
||||||
padding: 8px 0px;
|
|
||||||
border: 3px solid skyblue;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 18px;
|
|
||||||
background: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TabForm > .TabButtons > .TabButton[type="reset"] {
|
|
||||||
background: cornflowerblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.TabForm > .TabButtons > .TabButton[type="submit"] {
|
|
||||||
background: aqua;
|
|
||||||
}
|
|
53
src/main/resources/static/styles/form.less
Normal file
53
src/main/resources/static/styles/form.less
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
.TabForm {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
> .TabTitle {
|
||||||
|
margin: 20px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 32px;
|
||||||
|
|
||||||
|
&[lowSize] {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .TabFormItems {
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
> .TabFormItem {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
> .ItemName {
|
||||||
|
flex: 1 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .ItemInput {
|
||||||
|
flex: 1 0 0;
|
||||||
|
margin-left: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 3px solid skyblue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .TabButtons > .TabButton {
|
||||||
|
flex: 1;
|
||||||
|
margin: 20px 15px;
|
||||||
|
padding: 8px 0;
|
||||||
|
border: 3px solid skyblue;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
background: unset;
|
||||||
|
|
||||||
|
&[type="reset"] {
|
||||||
|
background: cornflowerblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[type="submit"] {
|
||||||
|
background: aqua;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,9 +12,11 @@
|
|||||||
<link type="text/css" rel="stylesheet" href="/styles/clock.css">
|
<link type="text/css" rel="stylesheet" href="/styles/clock.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/top.css">
|
<link type="text/css" rel="stylesheet" href="/styles/top.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/footer.css">
|
<link type="text/css" rel="stylesheet" href="/styles/footer.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/form.css">
|
<link type="text/css" rel="stylesheet/less" href="/styles/form.less">
|
||||||
|
<!-- 页面特定的样式表 -->
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/user.css">
|
<link type="text/css" rel="stylesheet" href="/styles/user.css">
|
||||||
<!-- 外部小组件 -->
|
<!-- 外部小组件 -->
|
||||||
|
<script src="/scripts/lib/less.min.js"></script>
|
||||||
<script src="/scripts/lib/anime.min.js"></script>
|
<script src="/scripts/lib/anime.min.js"></script>
|
||||||
<script async src="/scripts/lib/explosion.min.js"></script>
|
<script async src="/scripts/lib/explosion.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<link type="text/css" rel="stylesheet" href="/styles/clock.css">
|
<link type="text/css" rel="stylesheet" href="/styles/clock.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/top.css">
|
<link type="text/css" rel="stylesheet" href="/styles/top.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/footer.css">
|
<link type="text/css" rel="stylesheet" href="/styles/footer.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/form.css">
|
<link type="text/css" rel="stylesheet/less" href="/styles/form.less">
|
||||||
<!-- 页面特定的样式表 -->
|
<!-- 页面特定的样式表 -->
|
||||||
<link type="text/css" rel="stylesheet/less" href="/styles/edit.less">
|
<link type="text/css" rel="stylesheet/less" href="/styles/edit.less">
|
||||||
<!-- 外部小组件 -->
|
<!-- 外部小组件 -->
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<table class="ResultPanel">
|
<div class="ResultPanel">
|
||||||
|
<table class="Result">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>ID</td>
|
<td>ID</td>
|
||||||
@ -18,7 +19,7 @@
|
|||||||
<td>{{createTime}}</td>
|
<td>{{createTime}}</td>
|
||||||
<td>{{editTime}}</td>
|
<td>{{editTime}}</td>
|
||||||
<td>{{price}}</td>
|
<td>{{price}}</td>
|
||||||
<td>{{tags}}</td>
|
<td>{{tagPretty}}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="update">编辑</button>
|
<button class="update">编辑</button>
|
||||||
<button class="remove">删除</button>
|
<button class="remove">删除</button>
|
||||||
@ -26,15 +27,18 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{{/data}}
|
{{/data}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="EditPanel">
|
<div class="EditPanel">
|
||||||
{{#isNothing}}
|
{{#isNothing}}
|
||||||
<form class="TabForm" action="/editor" method="post">
|
<form class="TabForm" action="/editor" method="post">
|
||||||
<h2>请在左侧列表中选择需要进行的操作!</h2>
|
|
||||||
<input type="hidden" name="use" value="item"/>
|
<input type="hidden" name="use" value="item"/>
|
||||||
<input type="hidden" name="action" value="creating"/>
|
<input type="hidden" name="action" value="creating"/>
|
||||||
<button type="submit">或者点击此处新建一个商品!</button>
|
<div class="TabTitle" lowSize>请在左侧列表中选择需要进行的操作!</div>
|
||||||
|
<div class="TabButtons">
|
||||||
|
<button class="TabButton" type="submit">或者点击此处新建一个项目!</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{{/isNothing}}
|
{{/isNothing}}
|
||||||
|
|
||||||
@ -50,7 +54,8 @@
|
|||||||
|
|
||||||
<div class="TabFormItem">
|
<div class="TabFormItem">
|
||||||
<label class="ItemName" for="price">价格</label>
|
<label class="ItemName" for="price">价格</label>
|
||||||
<input class="ItemInput" id="price" type="number" name="price" placeholder="请输入价格" required/>
|
<input class="ItemInput" id="price" type="number" name="price" step="0.01" placeholder="请输入价格"
|
||||||
|
required/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="TabFormItem">
|
<div class="TabFormItem">
|
||||||
@ -77,7 +82,8 @@
|
|||||||
{{/isCreating}}
|
{{/isCreating}}
|
||||||
|
|
||||||
{{#isUpdating}}
|
{{#isUpdating}}
|
||||||
<form class="TabForm" action="/action/edit/item/update/{{target.id}}" method="post" enctype="multipart/form-data">
|
<form class="TabForm" action="/action/edit/item/update/{{target.id}}" method="post"
|
||||||
|
enctype="multipart/form-data">
|
||||||
<div class="TabTitle">更新商品详情</div>
|
<div class="TabTitle">更新商品详情</div>
|
||||||
|
|
||||||
<div class="TabFormItems">
|
<div class="TabFormItems">
|
||||||
@ -89,7 +95,7 @@
|
|||||||
|
|
||||||
<div class="TabFormItem">
|
<div class="TabFormItem">
|
||||||
<label class="ItemName" for="price">价格</label>
|
<label class="ItemName" for="price">价格</label>
|
||||||
<input class="ItemInput" id="price" type="number" name="price" placeholder="请输入价格"
|
<input class="ItemInput" id="price" type="number" name="price" step="0.01" placeholder="请输入价格"
|
||||||
{{#target.price}}value="{{target.price}}"{{/target.price}} required/>
|
{{#target.price}}value="{{target.price}}"{{/target.price}} required/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -102,7 +108,7 @@
|
|||||||
<div class="TabFormItem">
|
<div class="TabFormItem">
|
||||||
<label class="ItemName" for="tag">标签</label>
|
<label class="ItemName" for="tag">标签</label>
|
||||||
<input class="ItemInput" id="tag" type="text" name="tag" placeholder="请输入标签,以空格隔开!"
|
<input class="ItemInput" id="tag" type="text" name="tag" placeholder="请输入标签,以空格隔开!"
|
||||||
{{#target.tags}}value="{{target.tags}}"{{/target.tags}}/>
|
{{#target.tags}}value="{{target.tagString}}"{{/target.tags}}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="TabFormItem">
|
<div class="TabFormItem">
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<table class="ResultPanel">
|
<div class="ResultPanel">
|
||||||
|
<table class="Result">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>ID</td>
|
<td>ID</td>
|
||||||
@ -22,7 +23,7 @@
|
|||||||
<td>{{createTime}}</td>
|
<td>{{createTime}}</td>
|
||||||
<td>{{loginTime}}</td>
|
<td>{{loginTime}}</td>
|
||||||
<td>{{#age}}{{age}}{{/age}}</td>
|
<td>{{#age}}{{age}}{{/age}}</td>
|
||||||
<td>{{#gender}}{{gender}}{{/gender}}</td>
|
<td>{{genderPretty}}</td>
|
||||||
<td>{{#phone}}{{phone}}{{/phone}}</td>
|
<td>{{#phone}}{{phone}}{{/phone}}</td>
|
||||||
<td>{{#email}}{{email}}{{/email}}</td>
|
<td>{{#email}}{{email}}{{/email}}</td>
|
||||||
<td>{{#address}}{{address}}{{/address}}</td>
|
<td>{{#address}}{{address}}{{/address}}</td>
|
||||||
@ -34,15 +35,18 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{{/data}}
|
{{/data}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="EditPanel">
|
<div class="EditPanel">
|
||||||
{{#isNothing}}
|
{{#isNothing}}
|
||||||
<form class="TabForm" action="/editor" method="post">
|
<form class="TabForm" action="/editor" method="post">
|
||||||
<h2>请在左侧列表中选择需要进行的操作!</h2>
|
|
||||||
<input type="hidden" name="use" value="user"/>
|
<input type="hidden" name="use" value="user"/>
|
||||||
<input type="hidden" name="action" value="creating"/>
|
<input type="hidden" name="action" value="creating"/>
|
||||||
<button type="submit">或者点击此处新建一个用户!</button>
|
<div class="TabTitle" lowSize>请在左侧列表中选择需要进行的操作!</div>
|
||||||
|
<div class="TabButtons">
|
||||||
|
<button class="TabButton" type="submit">或者点击此处新建一个项目!</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{{/isNothing}}
|
{{/isNothing}}
|
||||||
|
|
||||||
|
@ -15,11 +15,12 @@
|
|||||||
<link type="text/css" rel="stylesheet" href="/styles/clock.css">
|
<link type="text/css" rel="stylesheet" href="/styles/clock.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/top.css">
|
<link type="text/css" rel="stylesheet" href="/styles/top.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/footer.css">
|
<link type="text/css" rel="stylesheet" href="/styles/footer.css">
|
||||||
<link type="text/css" rel="stylesheet" href=/"styles/loading.css">
|
<link type="text/css" rel="stylesheet" href="/styles/loading.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/form.css">
|
<link type="text/css" rel="stylesheet/less" href="/styles/form.less">
|
||||||
<!-- 页面特定的样式表 -->
|
<!-- 页面特定的样式表 -->
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/user.css">
|
<link type="text/css" rel="stylesheet" href="/styles/user.css">
|
||||||
<!-- 外部小组件 -->
|
<!-- 外部小组件 -->
|
||||||
|
<script src="/scripts/lib/less.min.js"></script>
|
||||||
<script src="/scripts/lib/anime.min.js"></script>
|
<script src="/scripts/lib/anime.min.js"></script>
|
||||||
<script async src="/scripts/lib/explosion.min.js"></script>
|
<script async src="/scripts/lib/explosion.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -9,17 +9,18 @@
|
|||||||
<script type="module" src="/scripts/resources.js"></script>
|
<script type="module" src="/scripts/resources.js"></script>
|
||||||
<script type="module" src="/scripts/items.js"></script>
|
<script type="module" src="/scripts/items.js"></script>
|
||||||
<script type="module" src="/scripts/index.js"></script>
|
<script type="module" src="/scripts/index.js"></script>
|
||||||
<link type="image/x-icon" rel="icon" href="images/favicon.ico">
|
<link type="image/x-icon" rel="icon" href="/images/favicon.ico">
|
||||||
<!-- 页面公共的样式表 -->
|
<!-- 页面公共的样式表 -->
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/header.css">
|
<link type="text/css" rel="stylesheet" href="/styles/header.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/clock.css">
|
<link type="text/css" rel="stylesheet" href="/styles/clock.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/top.css">
|
<link type="text/css" rel="stylesheet" href="/styles/top.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/footer.css">
|
<link type="text/css" rel="stylesheet" href="/styles/footer.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/loading.css">
|
<link type="text/css" rel="stylesheet" href="/styles/loading.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/form.css">
|
<link type="text/css" rel="stylesheet/less" href="/styles/form.less">
|
||||||
<!-- 页面特定的样式表 -->
|
<!-- 页面特定的样式表 -->
|
||||||
<link type="text/css" rel="stylesheet" href="/styles/user.css">
|
<link type="text/css" rel="stylesheet" href="/styles/user.css">
|
||||||
<!-- 外部小组件 -->
|
<!-- 外部小组件 -->
|
||||||
|
<script src="/scripts/lib/less.min.js"></script>
|
||||||
<script src="/scripts/lib/anime.min.js"></script>
|
<script src="/scripts/lib/anime.min.js"></script>
|
||||||
<script async src="/scripts/lib/explosion.min.js"></script>
|
<script async src="/scripts/lib/explosion.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user