Commit c0f46fb4 authored by 健杭 徐's avatar 健杭 徐
Browse files

finish all

parent e9920f5a
server:
port: 8080
timeout: 5s
database:
dsn: "user:password@tcp(localhost:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
module comment-system
go 1.24.3
require (
github.com/gin-gonic/gin v1.10.1
github.com/lib/pq v1.10.9
)
require (
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
package handlers
import (
"database/sql"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
var db *sql.DB
func InitDB(database *sql.DB) {
db = database
}
type Comment struct {
ID int `json:"id"`
Name string `json:"name"`
Content string `json:"content"`
CreatedAt string `json:"created_at"`
}
type Response struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
// 获取评论
func GetCommentsHandler(c *gin.Context) {
// 参数验证
page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
if err != nil || page < 1 {
page = 1
}
size, err := strconv.Atoi(c.DefaultQuery("size", "10"))
if err != nil || size < -1 {
size = 10
}
// 查询总数
var total int
countQuery := "SELECT COUNT(*) FROM comments"
err = db.QueryRow(countQuery).Scan(&total)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"msg": "查询总数失败: " + err.Error(),
})
return
}
// 构建查询
query := "SELECT id, name, content, TO_CHAR(created_at, 'YYYY-MM-DD HH24:MI') AS created_at FROM comments"
args := []interface{}{}
// 始终排序
query += " ORDER BY created_at DESC"
// 分页处理
if size != -1 {
offset := (page - 1) * size
query += " LIMIT $1 OFFSET $2"
args = append(args, size, offset)
}
// 执行查询
rows, err := db.Query(query, args...)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"msg": "查询评论失败: " + err.Error(),
})
return
}
defer rows.Close()
// 处理结果
var comments []Comment
for rows.Next() {
var comment Comment
if err := rows.Scan(
&comment.ID,
&comment.Name,
&comment.Content,
&comment.CreatedAt,
); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"msg": "解析评论失败: " + err.Error(),
})
return
}
comments = append(comments, comment)
}
// 返回标准响应结构
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "success",
"data": gin.H{
"total": total,
"comments": comments,
},
})
}
// 添加评论
func AddCommentHandler(c *gin.Context) {
var newComment Comment
if err := c.ShouldBindJSON(&newComment); err != nil {
c.JSON(http.StatusOK, Response{Code: 400, Msg: "Invalid request"})
return
}
// 插入数据库
var id int
err := db.QueryRow(
"INSERT INTO comments (name, content, created_at) VALUES ($1, $2, $3) RETURNING id",
newComment.Name, newComment.Content, newComment.CreatedAt,
).Scan(&id)
if err != nil {
c.JSON(http.StatusOK, Response{Code: 500, Msg: err.Error()})
return
}
newComment.ID = id
c.JSON(http.StatusOK, Response{
Code: 0,
Msg: "Comment added",
Data: newComment,
})
}
// 删除评论
func DeleteCommentHandler(c *gin.Context) {
id, err := strconv.Atoi(c.Query("id"))
if err != nil || id <= 0 {
c.JSON(http.StatusOK, Response{Code: 400, Msg: "Invalid ID"})
return
}
result, err := db.Exec("DELETE FROM comments WHERE id = $1", id)
if err != nil {
c.JSON(http.StatusOK, Response{Code: 500, Msg: err.Error()})
return
}
rowsAffected, _ := result.RowsAffected()
if rowsAffected == 0 {
c.JSON(http.StatusOK, Response{Code: 404, Msg: "Comment not found"})
return
}
c.JSON(http.StatusOK, Response{
Code: 0,
Msg: "Comment deleted",
Data: nil,
})
}
package main
import (
"comment-system/handlers"
"database/sql"
"fmt"
"log"
"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
)
const (
host = "localhost"
port = 5432
user = "comments_user"
password = "secure_password"
dbname = "comments_db"
)
var db *sql.DB
func main() {
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
host, port, user, password, dbname)
var err error
db, err = sql.Open("postgres", psqlInfo)
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
log.Fatal(err)
}
handlers.InitDB(db)
createTable()
var tableExists bool
err = db.QueryRow(`
SELECT EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_name = 'comments'
)
`).Scan(&tableExists)
if err != nil {
log.Fatal("Table check failed: ", err)
}
if !tableExists {
log.Println("Table 'comments' not found, creating...")
createTable()
} else {
log.Println("Table 'comments' already exists")
}
router := gin.Default()
router.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
router.GET("/comment/get", handlers.GetCommentsHandler)
router.POST("/comment/add", handlers.AddCommentHandler)
router.POST("/comment/delete", handlers.DeleteCommentHandler)
log.Println("Server started on :8080")
log.Fatal(router.Run(":8080"))
}
// 新增表创建函数
func createTable() {
query := `
CREATE TABLE IF NOT EXISTS comments (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`
_, err := db.Exec(query)
if err != nil {
log.Fatalf("创建表失败: %v\n请执行以下命令修复:\nGRANT USAGE, CREATE ON SCHEMA public TO comments_user;", err)
}
log.Println("表 'comments' 创建成功")
}
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
```
cd comment
npm run dev
```
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/public/icon.jpg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Xanadu's Comment</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script src="/src/load.js"></script>
</body>
</html>
This diff is collapsed.
{
"name": "my-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"date-fns": "^4.1.0",
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@eslint/js": "^9.25.0",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@vitejs/plugin-react-swc": "^3.9.0",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.30.1",
"vite": "^6.3.5"
}
}
import { format } from "date-fns";
const currentTime = new Date();
const formattedTime = format(currentTime, 'yyyy-MM-dd HH:mm');
async function AddComment({name, profilePhoto}:{name: string, profilePhoto:string}) {
const textInput = document.getElementById('textInput') as HTMLInputElement;
const content = textInput.value;
if (content === '')
{
alert('Text input cannot be empty')
return
}
if (name === '')
{
name = '小黑子'
}
try {
const response = await fetch('http://localhost:8080/comment/add', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, content, created_at: formattedTime})
});
const result = await response.json();
if (result.code !== 0)
{
alert(`操作失败: ${result.msg}`);
throw new Error(result.msg);
}
const newComment = result.data;
const NewCommentPart = document.createElement('div')
const UserPhoto = document.createElement('img')
UserPhoto.className = 'comment-avatar'
UserPhoto.src = profilePhoto
UserPhoto.alt = name
UserPhoto.style.width = '30px'
UserPhoto.style.height = '30px'
UserPhoto.style.borderRadius = '50%'
NewCommentPart.appendChild(UserPhoto)
const UserCommentContent = document.createElement('div')
UserCommentContent.className = 'comment-content'
const UserName = document.createElement('span')
UserName.textContent = name
UserName.className = 'comment-meta'
UserCommentContent.appendChild(UserName)
const UserDate = document.createElement('span')
UserDate.textContent = formattedTime
UserDate.className = 'comment-date'
UserCommentContent.appendChild(UserDate)
const UserComment = document.createElement('p')
UserComment.textContent = textInput.value
UserComment.style.marginLeft = '10px'
textInput.value = '';
(document.getElementById('nameInput') as HTMLInputElement).value = '';
UserCommentContent.appendChild(UserComment)
const DeleteButtonArea = document.createElement('div')
DeleteButtonArea.className = 'comment-delete'
const DeleteButton = document.createElement('button')
DeleteButton.textContent = 'Delete'
DeleteButton.id = 'deleteButton'
DeleteButton.onclick = async () => {
const commentItem = DeleteButton.closest('.comment-item')
if (commentItem) {
commentItem.remove()
try {
await fetch(`http://localhost:8080/comment/delete?id=${newComment.id}`, {
method: 'POST'
});
commentItem.remove();
} catch {
alert('删除失败:');
}
}
}
DeleteButtonArea.appendChild(DeleteButton)
UserCommentContent.appendChild(DeleteButtonArea)
NewCommentPart.appendChild(UserCommentContent)
const NewComment = document.createElement('li')
NewComment.className = 'comment-item'
NewComment.style.listStyle = 'none'
NewComment.appendChild(NewCommentPart)
const CommentList = document.getElementById('commentlist') as HTMLUListElement
CommentList.appendChild(NewComment)
}
catch (error) {
console.error('添加评论失败:', error);
};
}
export default AddComment
\ No newline at end of file
async function AddComment({name, profilePhoto}:{name: string, profilePhoto:string}) {
const textInput = document.getElementById('textInput') as HTMLInputElement;
const content = textInput.value;
if (content === '')
{
alert('Text input cannot be empty')
return
}
if (name === '')
{
name = '小黑子'
}
try {
const NewCommentPart = document.createElement('div')
const UserPhoto = document.createElement('img')
UserPhoto.className = 'comment-avatar'
UserPhoto.src = profilePhoto
UserPhoto.alt = name
UserPhoto.style.width = '30px'
UserPhoto.style.height = '30px'
UserPhoto.style.borderRadius = '50%'
NewCommentPart.appendChild(UserPhoto)
const UserCommentContent = document.createElement('div')
UserCommentContent.className = 'comment-content'
const UserName = document.createElement('span')
UserName.textContent = name
UserName.className = 'comment-meta'
UserCommentContent.appendChild(UserName)
const UserComment = document.createElement('p')
UserComment.textContent = textInput.value
UserComment.style.marginLeft = '10px'
textInput.value = '';
(document.getElementById('nameInput') as HTMLInputElement).value = '';
UserCommentContent.appendChild(UserComment)
const DeleteButtonArea = document.createElement('div')
DeleteButtonArea.className = 'comment-delete'
const DeleteButton = document.createElement('button')
DeleteButton.textContent = 'Delete'
DeleteButton.id = 'deleteButton'
DeleteButton.onclick = async () => {
const commentItem = DeleteButton.closest('.comment-item')
if (commentItem) {
commentItem.remove()
}
}
DeleteButtonArea.appendChild(DeleteButton)
UserCommentContent.appendChild(DeleteButtonArea)
NewCommentPart.appendChild(UserCommentContent)
const NewComment = document.createElement('li')
NewComment.className = 'comment-item'
NewComment.style.listStyle = 'none'
NewComment.appendChild(NewCommentPart)
const CommentList = document.getElementById('commentlist') as HTMLUListElement
CommentList.appendChild(NewComment)
}
catch {
alert('添加评论失败:');
};
}
export default AddComment
\ No newline at end of file
import { useState, useEffect } from 'react';
export default function DateTimeDisplay() {
return (
<div className="card">
<Clock></Clock>
<DateDisplay></DateDisplay>
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" stroke-width="0" fill="currentColor" stroke="currentColor" className="moon"><path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"></path><path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"></path></svg>
</div>
)
}
function Clock() {
const [time, setTime] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(timer);
}, []);
// 创建时间格式化器
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: true
});
// 将时间拆分为各个部分
const parts = formatter.formatToParts(time);
// 提取时间部分和AM/PM部分
const timeParts = [];
let period = '';
for (const part of parts) {
if (part.type === 'dayPeriod') {
period = part.value.toUpperCase(); // 转换为大写
} else {
timeParts.push(part.value);
}
}
// 合并时间部分(小时、分钟、秒)
const timeString = timeParts.join('');
return (
<p className='time-text'>
<span className='time-text'>{timeString}</span>
<span className='time-sub-text'>{period}</span>
</p>
);
}
function DateDisplay() {
const [date, setDate] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
return () => clearInterval(timer);
}, []);
// 获取星期名称
const weekday = date.toLocaleDateString('en-US', { weekday: 'long' });
// 获取月份名称
const month = date.toLocaleDateString('en-US', { month: 'long' });
// 获取日期数字
const day = date.getDate();
// 添加日期序数后缀
interface GetDayWithSuffix {
(day: number): string;
}
const getDayWithSuffix: GetDayWithSuffix = (day: number): string => {
if (day > 3 && day < 21) return `${day}th`; // 11th-13th例外规则
switch (day % 10) {
case 1: return `${day}st`;
case 2: return `${day}nd`;
case 3: return `${day}rd`;
default: return `${day}th`;
}
};
return <p className='day-text'>{weekday}, {month} {getDayWithSuffix(day)}</p>;
}
\ No newline at end of file
:root {
/* Enhanced color palette */
--primary-color: #6366f1;
--primary-hover: #4f46e5;
--primary-light: #a5b4fc;
--secondary-color: #06b6d4;
--accent-color: #f59e0b;
--success-color: #10b981;
/* Text colors */
--text-primary: #0f172a;
--text-secondary: #475569;
--text-muted: #94a3b8;
/* Background colors */
--bg-primary: #ffffff;
--bg-secondary: #f8fafc;
--bg-accent: #f1f5f9;
--bg-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
/* Border and shadows */
--border-color: #e2e8f0;
--border-radius: 16px;
--border-radius-sm: 8px;
--shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.08);
--shadow-md: 0 4px 16px rgba(15, 23, 42, 0.1);
--shadow-lg: 0 8px 32px rgba(15, 23, 42, 0.12);
--shadow-xl: 0 20px 64px rgba(15, 23, 42, 0.15);
/* Transitions */
--transition-fast: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
--transition-normal: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
--transition-slow: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Global styles */
* {
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-secondary);
background-image:
radial-gradient(circle at 25% 25%, rgba(99, 102, 241, 0.05) 0%, transparent 50%),
radial-gradient(circle at 75% 75%, rgba(6, 182, 212, 0.05) 0%, transparent 50%);
min-height: 100vh;
margin: 0;
padding: 0;
line-height: 1.6;
color: var(--text-primary);
}
/* Header styling */
h1 {
background: var(--bg-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-size: 2.5rem;
font-weight: 800;
text-align: center;
margin: 2rem 0;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
animation: fadeInDown 0.8s ease-out;
}
h3 {
color: var(--text-secondary);
font-size: 1.5rem;
font-weight: 600;
text-align: center;
margin: 1.5rem 0;
position: relative;
}
h3::after {
content: '';
position: absolute;
bottom: -8px;
left: 50%;
transform: translateX(-50%);
width: 60px;
height: 3px;
background: var(--bg-gradient);
border-radius: 2px;
}
/* Input area styling */
.inputarea {
background: var(--bg-primary);
padding: 2rem;
border-radius: var(--border-radius);
box-shadow: var(--shadow-lg);
width: min(95%, 700px);
margin: 2rem auto;
border: 1px solid var(--border-color);
position: relative;
backdrop-filter: blur(10px);
animation: slideInUp 0.6s ease-out;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.inputarea::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: var(--bg-gradient);
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
.inputarea::after {
content: '';
position: absolute;
bottom: -2rem;
left: 50%;
transform: translateX(-50%);
width: 60%;
height: 6px;
background: linear-gradient(90deg, transparent, var(--primary-light), transparent);
opacity: 0.4;
border-radius: 3px;
}
/* Profile section in input area */
.inputarea > div:first-child {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.inputarea img {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
border: 3px solid var(--primary-light);
box-shadow: var(--shadow-md);
transition: var(--transition-normal);
}
.inputarea img:hover {
transform: scale(1.05);
border-color: var(--primary-color);
}
.card {
width: 280px;
height: 60px;
background: rgb(17, 4, 134);
border-radius: 15px;
box-shadow: rgb(0,0,0,0.7) 5px 5px 10px ,rgb(0,0,0,0.7) -5px 0px 10px;
display: flex;
color: white;
justify-content: center;
position: absolute; right: 30px; top: 21px;
flex-direction: column;
background: linear-gradient(to right, rgb(20, 30, 48), rgb(36, 59, 85));
cursor: pointer;
transition: all 0.3s ease-in-out;
overflow: hidden;
}
.card:hover {
box-shadow: rgb(0,0,0) 5px 5px 10px ,rgb(0,0,0) -5px 0px 10px;
}
.time-text {
font-size: 20px;
margin-bottom: 0px;
margin-left: 8px;
font-weight: 600;
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
.time-sub-text {
font-size: 15px;
margin-left: 5px;
}
.day-text {
font-size: 15px;
margin-top: 0px;
margin-left: 15px;
font-weight: 500;
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
.moon {
font-size: 20px;
position: absolute;
right: 15px;
top: 15px;
transition: all 0.3s ease-in-out;
}
.card:hover > .moon {
font-size: 23px;
}
/* Input field styling */
.inputarea input {
width: 100%;
padding: 1rem 1.25rem;
border: 2px solid var(--border-color);
border-radius: var(--border-radius-sm);
font-size: 1rem;
font-family: inherit;
transition: var(--transition-normal);
background: var(--bg-secondary);
outline: none;
resize: none;
}
.inputarea input::placeholder {
color: var(--text-muted);
font-style: italic;
}
.inputarea input:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1);
background: var(--bg-primary);
transform: translateY(-1px);
}
/* Submit button styling */
#submitButton {
background: var(--bg-gradient);
color: white;
padding: 0.875rem 2rem;
border: none;
border-radius: var(--border-radius-sm);
font-weight: 600;
font-size: 1rem;
cursor: pointer;
transition: var(--transition-normal);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
position: relative;
overflow: hidden;
text-transform: uppercase;
letter-spacing: 0.5px;
align-self: flex-start;
}
#submitButton::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: var(--transition-normal);
}
#submitButton:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
#submitButton:hover::before {
left: 100%;
}
#submitButton:active {
transform: translateY(0);
}
/* Comment list container */
.comment {
width: min(95%, 700px);
margin: 0 auto 2rem;
}
.no-comments {
text-align: center;
color: var(--text-muted);
font-size: 1.25rem;
margin-top: 2rem;
animation: fadeInDown 0.5s ease-out;
}
#commentlist {
padding: 0;
margin: 0;
list-style: none;
}
/* Individual comment item styling */
.comment-item {
background: var(--bg-primary);
padding: 1.5rem;
border-radius: var(--border-radius);
box-shadow: var(--shadow-md);
margin-bottom: 1.5rem;
position: relative;
transition: var(--transition-normal);
border-left: 4px solid var(--primary-light);
animation: fadeInLeft 0.5s ease-out;
}
.comment-item:hover {
transform: translateY(-3px) translateX(5px);
box-shadow: var(--shadow-xl);
border-left-color: var(--primary-color);
}
.comment-item::before {
content: '';
position: absolute;
top: 1.5rem;
left: -2px;
width: 4px;
height: 24px;
background: var(--accent-color);
border-radius: 2px;
opacity: 0;
transition: var(--transition-normal);
}
.comment-item:hover::before {
opacity: 1;
}
/* Comment content layout */
.comment-item > div {
display: flex;
gap: 1rem;
align-items: flex-start;
}
/* Comment avatar */
.comment-avatar {
width: 40px !important;
height: 40px !important;
border-radius: 50% !important;
object-fit: cover;
border: 2px solid var(--primary-light) !important;
box-shadow: var(--shadow-sm) !important;
transition: var(--transition-normal) !important;
flex-shrink: 0;
}
.comment-item:hover .comment-avatar {
transform: scale(1.1);
border-color: var(--primary-color) !important;
}
/* Comment content area */
.comment-content {
flex: 1;
margin-left: 0 !important;
}
/* Comment meta (username) */
.comment-meta {
color: var(--text-secondary) !important;
font-size: 0.875rem !important;
font-weight: 600 !important;
margin-bottom: 0.5rem !important;
display: flex !important;
align-items: center !important;
gap: 0.5rem !important;
}
.comment-meta::after {
content: '•';
color: var(--text-muted);
font-size: 0.75rem;
}
.comment-date {
color: var(--text-secondary) !important;
font-size: 0.875rem !important;
font-weight: 600 !important;
margin-bottom: 0.5rem !important;
display: flex !important;
align-items: center !important;
gap: 0.5rem !important;
}
.comment-date::after {
content: '•';
color: var(--text-muted);
font-size: 0.75rem;
}
/* Comment text */
.comment-content p {
color: var(--text-primary) !important;
margin: 0 !important;
line-height: 1.7 !important;
padding: 1rem !important;
background: var(--bg-accent) !important;
border-radius: var(--border-radius-sm) !important;
position: relative !important;
border: 1px solid var(--border-color);
margin-left: 0 !important;
}
.comment-content p::before {
content: '' !important;
position: absolute !important;
left: -6px !important;
top: 1rem !important;
width: 0 !important;
height: 0 !important;
border-style: solid !important;
border-width: 6px 6px 6px 0 !important;
border-color: transparent var(--bg-accent) transparent transparent !important;
}
#deleteButton {
background: transparent;
color: var(--text-muted);
border: none;
cursor: pointer;
font-size: 0.875rem;
font-weight: 600;
transition: var(--transition-normal);
}
/* Animations */
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInLeft {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
:root {
--text-primary: #f1f5f9;
--text-secondary: #cbd5e1;
--text-muted: #64748b;
--bg-primary: #1e293b;
--bg-secondary: #0f172a;
--bg-accent: #334155;
--border-color: #475569;
--bg-gradient: linear-gradient(135deg, #4f46e5 0%, #06b6d4 100%);
}
body {
background-image:
radial-gradient(circle at 25% 25%, rgba(79, 70, 229, 0.1) 0%, transparent 50%),
radial-gradient(circle at 75% 75%, rgba(6, 182, 212, 0.1) 0%, transparent 50%);
}
.inputarea input {
background: var(--bg-accent);
}
.inputarea input:focus {
background: var(--bg-primary);
}
}
/* Responsive design */
@media (max-width: 768px) {
h1 {
font-size: 2rem;
}
.inputarea {
padding: 1.5rem;
margin: 1rem auto;
width: min(95%, 100%);
}
.comment {
width: min(95%, 100%);
}
.comment-item {
padding: 1rem;
}
.comment-item > div {
flex-direction: column;
gap: 0.75rem;
}
.comment-avatar {
align-self: flex-start;
}
}
/* Scrollbar styling */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-secondary);
}
::-webkit-scrollbar-thumb {
background: var(--primary-light);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--primary-color);
}
\ No newline at end of file
async function loadComments() {
try {
const response = await fetch('http://localhost:8080/comment/get');
const result = await response.json();
// 检查业务状态码
if (result.code !== 0) {
throw new Error(result.msg || '未知错误');
}
// 正确的数据结构访问
displayComments(result.data.comments);
} catch (error) {
console.error('加载评论错误:', error);
alert('加载评论失败: ' + error.message);
}
}
function displayComments(comments) {
const commentsContainer = document.getElementById('commentlist');
commentsContainer.innerHTML = '';
if (!comments || comments.length === 0) {
commentsContainer.innerHTML = '<div class="no-comments">No Comments Yet</div>';
return;
}
comments.forEach(comment => {
const commentElement = document.createElement('li');
commentElement.className = 'comment-item';
commentElement.innerHTML = `
<div>
<img src='https://pic4.zhimg.com/v2-bf5f58e7b583cd69ac228db9fdff377f_r.jpg'
class='comment-avatar' alt='${comment.name}' />
<div class='comment-content'>
<div>
<span class='comment-meta'>${comment.name}</span>
<span class='comment-date'>${comment.created_at}</span>
</div>
<p>${comment.content}</p>
<div class='comment-delete'>
<button id = 'deleteButton' onclick="deleteComment(${comment.id})">Delete</button>
</div>
</div>
</div>
`;
commentsContainer.appendChild(commentElement);
});
}
function deleteComment(commentId) {
if (!confirm('确定要删除这条评论吗?')) {
return;
}
fetch(`http://localhost:8080/comment/delete?id=${commentId}`, {
method: 'POST',
})
.then(response => response.json())
.then(result => {
if (result.code !== 0) {
throw new Error(result.msg || '删除失败');
}
alert('评论已删除');
loadComments(); // 重新加载评论列表
})
.catch(error => {
console.error('删除评论错误:', error);
alert('删除评论失败: ' + error.message);
});
}
// 页面加载时调用
document.addEventListener('DOMContentLoaded', () => {
loadComments();
});
\ No newline at end of file
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import AddComment from './App.tsx'
import DateTimeDisplay from './date.tsx'
import './index.css'
const profilePhoto:string = 'https://pic4.zhimg.com/v2-bf5f58e7b583cd69ac228db9fdff377f_r.jpg'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<div>
<h1 style={{textAlign: 'center'}}>Welcome To Xanadu`s Comment</h1>
</div>
<Input_Area />
<div className='comment'>
<div>
<h3 style={{textAlign: 'center'}}>
All Comment
</h3>
</div>
<ul id='commentlist'></ul>
</div>
</StrictMode>
)
function Input_Area() {
return (
<div className='inputarea'>
<img src={profilePhoto} alt={'小黑子'}
style={{width: '40px', height: '40px', borderRadius: '50%'}} />
<DateTimeDisplay />
<input type="text" placeholder="Please enter your name" id="nameInput" />
<input type="text" placeholder="Please follow the Community Guidelines" id="textInput" onKeyUpCapture={
(e) => {
const name:string = (document.getElementById('nameInput') as HTMLInputElement).value;
if (e.key === 'Enter') {
AddComment({name, profilePhoto});
}
}
}/>
<button
id="submitButton"
onClick={() => AddComment({
name: (document.getElementById('nameInput') as HTMLInputElement).value,
profilePhoto: profilePhoto
})}
>
Submit
</button>
</div>
)
}
// Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
\ No newline at end of file
class girlFriend {
name:string;
age:number;
character:string[];
height:number;
weight:number;
constructor(name:string, age:number, character:string[], height:number, weight:number) {
this.name = name;
this.age = age;
this.character = character;
this.height = height;
this.weight = weight;
}
}
const MyGirlFriend = new girlFriend("小红", 18, ["温柔", "善良"], 160, 50);
console.log(MyGirlFriend);
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment