feat: 完成面板设置、完成国际化

This commit is contained in:
ssongliu 2022-09-13 18:45:03 +08:00 committed by ssongliu
parent 5342b758cb
commit d28697b63a
31 changed files with 292 additions and 343 deletions

BIN
backend/__debug_bin Executable file

Binary file not shown.

View File

@ -5,6 +5,7 @@ import (
"github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/ntp"
"github.com/gin-gonic/gin"
)
@ -52,3 +53,20 @@ func (b *BaseApi) UpdatePassword(c *gin.Context) {
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) SyncTime(c *gin.Context) {
var timeLayoutStr = "2006-01-02 15:04:05"
ntime, err := ntp.Getremotetime()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
ts := ntime.Format(timeLayoutStr)
if err := ntp.UpdateSystemDate(ts); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, ts)
}

View File

@ -40,7 +40,7 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
if err := json.Unmarshal(arr, &info); err != nil {
return nil, err
}
info.LocalTime = time.Now().Format("2006.01.02 15:04:05")
info.LocalTime = time.Now().Format("2006-01-02 15:04:05")
return &info, err
}

View File

@ -11,6 +11,7 @@ require (
github.com/gin-gonic/gin v1.8.1
github.com/go-gormigrate/gormigrate/v2 v2.0.2
github.com/go-playground/validator/v10 v10.11.0
github.com/gogf/gf v1.16.9
github.com/golang-jwt/jwt/v4 v4.4.2
github.com/gorilla/csrf v1.7.1
github.com/gorilla/websocket v1.5.0
@ -49,6 +50,7 @@ require (
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
@ -77,6 +79,7 @@ require (
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
@ -97,6 +100,8 @@ require (
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/otel v1.0.0 // indirect
go.opentelemetry.io/otel/trace v1.0.0 // indirect
golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect

View File

@ -59,6 +59,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4=
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@ -90,8 +92,12 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc=
@ -141,6 +147,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gogf/gf v1.16.9 h1:Q803UmmRo59+Ws08sMVFOcd8oNpkSWL9vS33hlo/Cyk=
github.com/gogf/gf v1.16.9/go.mod h1:8Q/kw05nlVRp+4vv7XASBsMe9L1tsVKiGoeP2AHnlkk=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
@ -183,6 +191,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
@ -198,6 +208,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@ -223,8 +234,11 @@ github.com/gorilla/csrf v1.7.1 h1:Ir3o2c1/Uzj6FBxMlAUB6SivgVMy1ONXwYgXn+/aHPE=
github.com/gorilla/csrf v1.7.1/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
github.com/gwatts/gin-adapter v1.0.0 h1:TsmmhYTR79/RMTsfYJ2IQvI1F5KZ3ZFJxuQSYEOpyIA=
github.com/gwatts/gin-adapter v1.0.0/go.mod h1:44AEV+938HsS0mjfXtBDCUZS9vONlF2gwvh8wu4sRYc=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -287,9 +301,15 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@ -314,6 +334,8 @@ github.com/nicksnyder/go-i18n/v2 v2.1.2/go.mod h1:d++QJC9ZVf7pa48qrsRWhMJ5pSHIPm
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
@ -424,6 +446,10 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/otel v1.0.0 h1:qTTn6x71GVBvoafHK/yaRUmFzI4LcONZD0/kXxl5PHI=
go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
go.opentelemetry.io/otel/trace v1.0.0 h1:TSBr8GTEtKevYMG/2d21M989r5WJYVimhTHBKVEZuh4=
go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -506,6 +532,7 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -543,6 +570,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -574,6 +602,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -64,7 +64,7 @@ var AddTableSetting = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "PanelName", Value: "1Panel"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "Language", Value: "ch"}).Error; err != nil {
if err := tx.Create(&model.Setting{Key: "Language", Value: "zh"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "Theme", Value: "auto"}).Error; err != nil {

View File

@ -10,10 +10,12 @@ type SettingRouter struct{}
func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
settingRouter := Router.Group("settings").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
withRecordRouter := Router.Group("settings").Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.OperationRecord())
baseApi := v1.ApiGroupApp.BaseApi
{
settingRouter.POST("/search", baseApi.GetSettingInfo)
settingRouter.PUT("", baseApi.UpdateSetting)
withRecordRouter.PUT("", baseApi.UpdateSetting)
settingRouter.PUT("/password", baseApi.UpdatePassword)
settingRouter.POST("/time/sync", baseApi.SyncTime)
}
}

View File

@ -18,7 +18,7 @@ func TestStringEncrypt(t *testing.T) {
func TestStringDecrypt(t *testing.T) {
viper.Init()
p, err := StringDecrypt("5WYEZ4XcitdomVvAyimt9WwJwBJJSbTTHncZoqyOraQ=")
p, err := StringDecrypt("Jmg4EUACGznt3dEQTJ+0ZRxwLaVNsNg7R5RcZ0V7ElQ=")
if err != nil {
t.Fatal(err)
}

76
backend/utils/ntp/ntp.go Normal file
View File

@ -0,0 +1,76 @@
package ntp
import (
"encoding/binary"
"flag"
"fmt"
"net"
"runtime"
"time"
"github.com/gogf/gf/os/gproc"
)
const ntpEpochOffset = 2208988800
type packet struct {
Settings uint8
Stratum uint8
Poll int8
Precision int8
RootDelay uint32
RootDispersion uint32
ReferenceID uint32
RefTimeSec uint32
RefTimeFrac uint32
OrigTimeSec uint32
OrigTimeFrac uint32
RxTimeSec uint32
RxTimeFrac uint32
TxTimeSec uint32
TxTimeFrac uint32
}
func Getremotetime() (*time.Time, error) {
var host string
flag.StringVar(&host, "e", "ntp1.aliyun.com:123", "NTP host")
flag.Parse()
conn, err := net.Dial("udp", host)
if err != nil {
return nil, fmt.Errorf("failed to connect: %v", err)
}
defer conn.Close()
if err := conn.SetDeadline(time.Now().Add(15 * time.Second)); err != nil {
return nil, fmt.Errorf("failed to set deadline: %v", err)
}
req := &packet{Settings: 0x1B}
if err := binary.Write(conn, binary.BigEndian, req); err != nil {
return nil, fmt.Errorf("failed to set request: %v", err)
}
rsp := &packet{}
if err := binary.Read(conn, binary.BigEndian, rsp); err != nil {
return nil, fmt.Errorf("failed to read server response: %v", err)
}
secs := float64(rsp.TxTimeSec) - ntpEpochOffset
nanos := (int64(rsp.TxTimeFrac) * 1e9) >> 32
showtime := time.Unix(int64(secs), nanos)
return &showtime, nil
}
func UpdateSystemDate(dateTime string) error {
system := runtime.GOOS
if system == "linux" {
if _, err := gproc.ShellExec(`date -s "` + dateTime + `"`); err != nil {
return fmt.Errorf("update system date failed, err: %v", err)
}
return nil
}
return fmt.Errorf("The current system architecture does not support synchronization")
}

View File

@ -7,27 +7,22 @@
<script setup lang="ts">
import { reactive, computed } from 'vue';
import { GlobalStore } from '@/store';
// element
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import en from 'element-plus/es/locale/lang/en';
// 使
import { useTheme } from '@/hooks/use-theme';
useTheme();
const globalStore = GlobalStore();
// element
const config = reactive({
autoInsertSpace: false,
});
// element
const i18nLocale = computed((): any => {
if (globalStore.language && globalStore.language == 'zh') return zhCn;
if (globalStore.language == 'en') return en;
return '';
});
// (small/default(medium)/large)
const assemblySize = computed((): string => globalStore.assemblySize);
</script>

View File

@ -12,3 +12,7 @@ export const updateSetting = (param: Setting.SettingUpdate) => {
export const updatePassword = (param: Setting.PasswordUpdate) => {
return http.put(`/settings/password`, param);
};
export const syncTime = () => {
return http.post(`/settings/time/sync`, {});
};

View File

@ -1,22 +0,0 @@
<template>
<el-breadcrumb :separator-icon="ArrowRight">
<transition-group name="breadcrumb" mode="out-in">
<el-breadcrumb-item :to="{ path: HOME_URL }" key="/home">{{ $t('menu.home') }}</el-breadcrumb-item>
<el-breadcrumb-item v-for="item in matched" :key="item.path" :to="{ path: item.path }">
{{ $t(item.meta?.title as string) }}
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { ArrowRight } from '@element-plus/icons-vue';
import { HOME_URL } from '@/config/config';
const route = useRoute();
const matched = computed(() =>
route.matched.filter((item) => item.meta && item.meta.title && item.meta.title !== 'menu.home'),
);
</script>

View File

@ -1,20 +0,0 @@
<template>
<el-icon :size="25" class="collapse-icon" @click="menuStore.setCollapse()">
<component :is="isCollapse ? 'expand' : 'fold'"></component>
</el-icon>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { MenuStore } from '@/store/modules/menu';
const menuStore = MenuStore();
const isCollapse = computed((): boolean => menuStore.isCollapse);
</script>
<style scoped lang="scss">
// @import '../index.scss';
.collapse-icon {
cursor: pointer;
}
</style>

View File

@ -1,63 +0,0 @@
<template>
<div>
<el-tooltip effect="dark" :content="$t('commons.header.theme')" placement="bottom">
<i :class="'panel p-theme'" class="icon-style" @click="openDrawer"></i>
</el-tooltip>
<el-drawer v-model="drawerVisible" :title="$t('commons.header.theme')" size="300px">
<el-divider class="divider" content-position="center">
<el-icon><ColdDrink /></el-icon>
{{ $t('commons.header.globalTheme') }}
</el-divider>
<div class="theme-item">
<span>{{ $t('commons.header.themeColor') }}</span>
<el-color-picker
v-model="themeConfig.primary"
:predefine="colorList"
@change="changePrimary"
></el-color-picker>
</div>
<div class="theme-item">
<span>{{ $t('commons.header.darkTheme') }}</span>
<SwitchDark></SwitchDark>
</div>
<br />
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useTheme } from '@/hooks/use-theme';
import SwitchDark from '@/components/switch-dark/index.vue';
import { GlobalStore } from '@/store';
//
const colorList = [
'#409EFF',
'#DAA96E',
'#0C819F',
'#009688',
'#27ae60',
'#ff5c93',
'#e74c3c',
'#fd726d',
'#f39c12',
'#9b59b6',
];
//
const globalStore = GlobalStore();
const themeConfig = computed(() => globalStore.themeConfig);
const { changePrimary } = useTheme();
//
const drawerVisible = ref(false);
const openDrawer = () => {
drawerVisible.value = true;
};
</script>
<style scoped lang="scss">
@import '../index.scss';
</style>

View File

@ -1,101 +0,0 @@
.header {
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
height: 55px;
padding: 0 15px;
background-color: #ffffff;
border-bottom: 1px solid #f6f6f6;
.header-lf {
.collapse-icon {
margin-right: 20px;
font-size: 22px;
cursor: pointer;
}
}
.header-ri {
margin: 0 30px;
.header-icon {
display: flex;
align-items: center;
justify-content: space-between;
width: 66px;
margin-right: 22px;
.icon-style {
font-size: 20px;
color: rgb(0 0 0 / 75%);
cursor: pointer;
}
}
.username {
margin: 0 20px 0 0;
font-size: 15px;
color: rgb(0 0 0 / 75%);
}
.avatar {
width: 40px;
height: 40px;
overflow: hidden;
cursor: pointer;
border-radius: 50%;
img {
width: 100%;
height: 100%;
}
}
.theme-item {
display: flex;
align-items: center;
justify-content: space-between;
margin: 15px 0;
span {
font-size: 14px;
}
}
.divider {
margin-top: 20px;
.el-icon {
position: relative;
top: 2px;
right: 5px;
font-size: 15px;
}
}
}
.theme-switch {
display: flex;
justify-content: center;
margin-top: 35px;
}
}
/* 菜单搜索样式 */
.layout-search-dialog {
:deep(.el-dialog) {
background: rgb(0 0 0 / 50%);
border-radius: 0 !important;
box-shadow: unset !important;
.el-dialog__header {
border-bottom: none !important;
}
}
:deep(.el-autocomplete) {
position: absolute;
top: 100px;
left: 50%;
width: 560px;
transform: translateX(-50%);
}
}
.el-autocomplete__popper {
.el-icon {
position: relative;
top: 2px;
font-size: 16px;
}
span {
margin: 0 0 0 10px;
font-size: 14px;
}
}

View File

@ -1,21 +0,0 @@
<template>
<div class="header">
<div class="header-lf flx-center">
<CollapseIcon id="collapseIcon"></CollapseIcon>
<Breadcrumb id="breadcrumb" v-if="themeConfig.breadcrumb"></Breadcrumb>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import CollapseIcon from './components/collapseicon.vue';
import Breadcrumb from './components/breadcrumb.vue';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const themeConfig = computed(() => globalStore.themeConfig);
</script>
<style scoped lang="scss">
@import './index.scss';
</style>

View File

@ -1,10 +1,7 @@
<template>
<Layout>
<template #menu>
<Menu></Menu>
</template>
<template #header>
<Header></Header>
<Menu :panelName="themeConfig.panelName"></Menu>
</template>
<template #footer>
<Footer></Footer>
@ -13,7 +10,28 @@
</template>
<script setup lang="ts">
import Layout from '@/layout/index.vue';
import Header from './header/index.vue';
import Footer from './footer/index.vue';
import Menu from './menu/index.vue';
import { onMounted, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { GlobalStore } from '@/store';
import { useTheme } from '@/hooks/use-theme';
import { getSettingInfo } from '@/api/modules/setting';
const i18n = useI18n();
const globalStore = GlobalStore();
const themeConfig = computed(() => globalStore.themeConfig);
const { switchDark } = useTheme();
const loadDataFromDB = async () => {
const res = await getSettingInfo();
i18n.locale.value = res.data.language;
globalStore.updateLanguage(res.data.language);
globalStore.setThemeConfig({ ...themeConfig.value, theme: res.data.theme });
globalStore.setThemeConfig({ ...themeConfig.value, panelName: res.data.panelName });
switchDark();
};
onMounted(() => {
loadDataFromDB();
});
</script>

View File

@ -1,12 +1,11 @@
<template>
<div class="logo flx-center">
<img src="@/assets/images/logo.svg" alt="logo" />
<span v-show="!isCollapse">1Panel</span>
<span v-show="!isCollapse">{{ panelName }}</span>
</div>
</template>
<script setup lang="ts">
defineProps<{ isCollapse: boolean }>();
defineProps<{ isCollapse: boolean; panelName: string }>();
</script>
<style scoped lang="scss">

View File

@ -7,7 +7,7 @@
element-loading-svg-view-box="-10, -10, 50, 50"
element-loading-background="rgba(122, 122, 122, 0.01)"
>
<Logo :isCollapse="isCollapse"></Logo>
<Logo :panelName="panelName" :isCollapse="isCollapse"></Logo>
<el-scrollbar>
<el-menu
:default-active="activeMenu"
@ -48,11 +48,6 @@ import { GlobalStore } from '@/store';
const route = useRoute();
const menuStore = MenuStore();
const globalStore = GlobalStore();
onMounted(async () => {
menuStore.setMenuList(menuList);
});
// const activeMenu = computed((): string => route.path);
const activeMenu = computed(() => {
const { meta, path } = route;
@ -62,6 +57,9 @@ const activeMenu = computed(() => {
return path;
});
const isCollapse = computed((): boolean => menuStore.isCollapse);
const panelName = computed(() => {
return globalStore.themeConfig.panelName;
});
const routerMenus = computed((): RouteRecordRaw[] => menuStore.menuList);
const screenWidth = ref<number>(0);
@ -95,6 +93,9 @@ const logout = () => {
const systemLogOut = async () => {
await logOutApi();
};
onMounted(async () => {
menuStore.setMenuList(menuList);
});
</script>
<style scoped lang="scss">

View File

@ -3,34 +3,27 @@ import { getLightColor, getDarkColor } from '@/utils/theme/tool';
import { GlobalStore } from '@/store';
import { ElMessage } from 'element-plus';
/**
* @description 切换主题
* */
export const useTheme = () => {
const globalStore = GlobalStore();
const themeConfig = computed(() => globalStore.themeConfig);
// 切换暗黑模式
const switchDark = () => {
const body = document.documentElement as HTMLElement;
if (themeConfig.value.isDark) body.setAttribute('class', 'dark');
if (themeConfig.value.theme === 'dark') body.setAttribute('class', 'dark');
else body.setAttribute('class', '');
};
// 修改主题颜色
const changePrimary = (val: string) => {
if (!val) {
val = '#409EFF';
ElMessage({ type: 'success', message: '主题颜色已重置为 #409EFF' });
}
globalStore.setThemeConfig({ ...themeConfig.value, primary: val });
// 颜色加深
document.documentElement.style.setProperty(
'--el-color-primary-dark-2',
`${getDarkColor(themeConfig.value.primary, 0.1)}`,
);
document.documentElement.style.setProperty('--el-color-primary', themeConfig.value.primary);
// 颜色变浅
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(
`--el-color-primary-light-${i}`,

View File

@ -3,6 +3,9 @@ export default {
button: {
create: 'Create',
add: 'Add',
save: 'Save',
set: 'Reset',
sync: 'Sync',
delete: 'Delete',
edit: 'Edit',
confirm: 'Confirm',
@ -74,12 +77,10 @@ export default {
logout: 'Logout',
},
},
business: {
user: {
username: 'Username',
email: 'Email',
password: 'Password',
},
auth: {
username: 'Username',
email: 'Email',
password: 'Password',
},
menu: {
home: 'Overview',
@ -249,4 +250,23 @@ export default {
downloadProcess: 'Download Process',
downloading: 'Downloading...',
},
setting: {
panel: 'Panel',
emailHelper: 'For password retrieval',
title: 'Panel alias',
theme: 'Theme',
dark: 'Dark',
light: 'Light',
language: 'Language',
languageHelper:
'By default, it follows the browser language. This parameter takes effect only on the current browser',
sessionTimeout: 'Timeout',
sessionTimeoutHelper:
'If you do not operate the panel for more than {0} seconds, the panel automatically logs out',
syncTime: 'Synchronization time',
changePassword: 'Password change',
oldPassword: 'Original password',
newPassword: 'New password',
retryPassword: 'Confirm password',
},
};

View File

@ -3,6 +3,9 @@ export default {
button: {
create: '新建',
add: '添加',
save: '保存',
set: '设置',
sync: '同步',
delete: '删除',
edit: '编辑',
confirm: '确认',
@ -85,12 +88,10 @@ export default {
logout: '退出登录',
},
},
business: {
user: {
username: '用户名',
email: '邮箱',
password: '密码',
},
auth: {
username: '用户名',
email: '邮箱',
password: '密码',
},
menu: {
home: '概览',
@ -259,4 +260,22 @@ export default {
downloadProcess: '下载进度',
downloading: '正在下载...',
},
setting: {
panel: '面板',
emailHelper: '用于密码找回',
title: '面板别名',
theme: '主题色',
componentSize: '组件大小',
dark: '黑金',
light: '白金',
language: '系统语言',
languageHelper: '默认跟随浏览器语言设置后只对当前浏览器生效更换浏览器后需要重新设置',
sessionTimeout: '超时时间',
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板面板将自动退出登录',
syncTime: '同步时间',
changePassword: '密码修改',
oldPassword: '原密码',
newPassword: '新密码',
retryPassword: '确认密码',
},
};

View File

@ -8,17 +8,13 @@ export const GlobalStore = defineStore({
id: 'GlobalState',
state: (): GlobalState => ({
isLogin: false,
userInfo: '',
csrfToken: '',
assemblySize: 'default',
assemblySize: 'small',
language: '',
themeConfig: {
panelName: '',
primary: '#409EFF',
isDark: false,
isGrey: false,
isWeak: false,
breadcrumb: true,
tabs: false,
theme: 'bright',
footer: true,
},
}),
@ -27,13 +23,10 @@ export const GlobalStore = defineStore({
setLogStatus(login: boolean) {
this.isLogin = login;
},
setUserInfo(userInfo: any) {
this.userInfo = userInfo;
},
setCsrfToken(token: string) {
this.csrfToken = token;
},
setAssemblySizeSize(assemblySize: string) {
setAssemblySize(assemblySize: string) {
this.assemblySize = assemblySize;
},
updateLanguage(language: string) {
@ -46,7 +39,6 @@ export const GlobalStore = defineStore({
persist: piniaPersistConfig('GlobalState'),
});
// piniaPersist(持久化)
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

View File

@ -2,22 +2,18 @@ import { RouteRecordRaw } from 'vue-router';
/* themeConfigProp */
export interface ThemeConfigProp {
panelName: string;
primary: string;
isDark: boolean;
isGrey: boolean;
isWeak: boolean;
breadcrumb: boolean;
tabs: boolean;
theme: string; // dark | bright auto
footer: boolean;
}
/* GlobalState */
export interface GlobalState {
isLogin: boolean;
userInfo: any;
csrfToken: string;
assemblySize: string;
language: string;
language: string; // zh | en
assemblySize: string; // small | default | large
themeConfig: ThemeConfigProp;
}

View File

@ -7,9 +7,7 @@ const whiteList = ['/login', '/error'];
export const MenuStore = defineStore({
id: 'MenuState',
state: (): MenuState => ({
// menu collapse
isCollapse: false,
// menu List
menuList: [],
}),
getters: {},

View File

@ -2,13 +2,13 @@
<LayoutContent :header="$t('commons.button.' + op)" :back-name="'Table'">
<template #form>
<el-form ref="ruleFormRef" label-position="left" :model="demoForm" :rules="rules" label-width="140px">
<el-form-item :label="$t('business.user.username')" prop="name">
<el-form-item :label="$t('auth.username')" prop="name">
<el-input v-model="demoForm.name" />
</el-form-item>
<el-form-item :label="$t('business.user.email')" prop="email">
<el-form-item :label="$t('auth.email')" prop="email">
<el-input v-model="demoForm.email" />
</el-form-item>
<el-form-item :label="$t('business.user.password')" prop="password">
<el-form-item :label="$t('auth.password')" prop="password">
<el-input type="password" v-model="demoForm.password" />
</el-form-item>
</el-form>

View File

@ -65,7 +65,7 @@ const loginRules = reactive({
//
const loginForm = reactive<Login.ReqLoginForm>({
name: 'admin',
password: 'Songliu123++',
password: 'Calong@2015',
captcha: '',
captchaID: '',
authMethod: '',
@ -93,8 +93,7 @@ const login = (formEl: FormInstance | undefined) => {
captchaID: captcha.captchaID,
authMethod: '',
};
const res = await loginApi(requestLoginForm);
globalStore.setUserInfo(res.data.name);
await loginApi(requestLoginForm);
globalStore.setLogStatus(true);
menuStore.setMenuList([]);
ElMessage.success(i18n.global.t('commons.msg.loginSuccess'));

View File

@ -7,7 +7,7 @@
</template>
<el-row>
<el-col :span="1"><br /></el-col>
<el-col :span="8"> </el-col>
<el-col :span="8"></el-col>
</el-row>
</el-card>
</template>

View File

@ -30,7 +30,7 @@
</el-input>
</el-form-item>
<el-form-item>
<el-button icon="Delete"> 清空监控记录 </el-button>
<el-button icon="Delete">清空监控记录</el-button>
</el-form-item>
</el-col>
</el-row>

View File

@ -1,109 +1,111 @@
<template>
<el-form size="small" :model="form" label-position="left" label-width="120px">
<el-form size="small" :model="form" label-position="left" label-width="160px">
<el-card style="margin-top: 20px">
<template #header>
<div class="card-header">
<span>面板</span>
<span>{{ $t('setting.panel') }}</span>
</div>
</template>
<el-row>
<el-col :span="1"><br /></el-col>
<el-col :span="8">
<el-form-item label="用户名">
<el-col :span="10">
<el-form-item :label="$t('auth.username')">
<el-input clearable v-model="form.settingInfo.userName">
<template #append>
<el-button
@click="SaveSetting('UserName', form.settingInfo.userName)"
icon="Collection"
>
保存
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="密码">
<el-form-item :label="$t('auth.password')">
<el-input type="password" clearable disabled v-model="form.settingInfo.password">
<template #append>
<el-button icon="Setting" @click="passwordVisiable = true">设置</el-button>
<el-button icon="Setting" @click="passwordVisiable = true">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="邮箱">
<el-form-item :label="$t('auth.email')">
<el-input clearable v-model="form.settingInfo.email">
<template #append>
<el-button @click="SaveSetting('Email', form.settingInfo.email)" icon="Collection">
保存
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
<div><span class="input-help">用于密码找回</span></div>
<div>
<span class="input-help">{{ $t('setting.emailHelper') }}</span>
</div>
</el-form-item>
<el-form-item label="面板别名">
<el-form-item :label="$t('setting.title')">
<el-input clearable v-model="form.settingInfo.panelName">
<template #append>
<el-button
@click="SaveSetting('PanelName', form.settingInfo.panelName)"
icon="Collection"
>
保存
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="主题色">
<el-form-item :label="$t('setting.theme')">
<el-radio-group
@change="SaveSetting('Theme', form.settingInfo.theme)"
v-model="form.settingInfo.theme"
>
<el-radio-button label="black">
<el-icon><Moon /></el-icon>
<el-radio-button label="dark">
<el-icon><Moon /></el-icon>
{{ $t('setting.dark') }}
</el-radio-button>
<el-radio-button label="auto" icon="Sunny">
<el-icon><MagicStick /></el-icon>
</el-radio-button>
<el-radio-button label="write">
<el-icon><Sunny /></el-icon>
<el-radio-button label="light">
<el-icon><Sunny /></el-icon>
{{ $t('setting.light') }}
</el-radio-button>
</el-radio-group>
<div>
<span class="input-help">
选择自动设置将会在晚 6 点到次日早 6 点间自动切换到黑金主题
</span>
</div>
</el-form-item>
<el-form-item label="系统语言">
<el-form-item :label="$t('setting.language')">
<el-radio-group
@change="SaveSetting('Language', form.settingInfo.language)"
v-model="form.settingInfo.language"
>
<el-radio-button label="ch">中文 </el-radio-button>
<el-radio-button label="en">English </el-radio-button>
<el-radio-button label="zh">中文</el-radio-button>
<el-radio-button label="en">English</el-radio-button>
</el-radio-group>
<div>
<span class="input-help">
默认跟随浏览器语言设置后只对当前浏览器生效更换浏览器后需要重新设置
{{ $t('setting.languageHelper') }}
</span>
</div>
</el-form-item>
<el-form-item label="超时时间">
<el-form-item :label="$t('setting.sessionTimeout')">
<el-input v-model="form.settingInfo.sessionTimeout">
<template #append>
<el-button
@click="SaveSetting('SessionTimeout', form.settingInfo.sessionTimeout)"
icon="Collection"
>
保存
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
<div>
<span class="input-help">如果用户超过 86400秒未操作面板面板将自动退出登录 </span>
<span class="input-help">
{{ $t('setting.sessionTimeoutHelper', [form.settingInfo.sessionTimeout]) }}
</span>
</div>
</el-form-item>
<el-form-item label="同步时间">
<el-input v-model="form.settingInfo.localTime">
<el-form-item :label="$t('setting.syncTime')">
<el-input disabled v-model="form.settingInfo.localTime">
<template #append>
<el-button icon="Refresh">同步</el-button>
<el-button @click="onSyncTime" icon="Refresh">
{{ $t('commons.button.sync') }}
</el-button>
</template>
</el-input>
</el-form-item>
@ -111,7 +113,7 @@
</el-row>
</el-card>
</el-form>
<el-dialog v-model="passwordVisiable" title="密码修改" width="30%">
<el-dialog v-model="passwordVisiable" :title="$t('setting.changePassword')" width="30%">
<el-form
size="small"
ref="passFormRef"
@ -120,13 +122,13 @@
:model="passForm"
:rules="passRules"
>
<el-form-item label="原密码" prop="oldPassword">
<el-form-item :label="$t('setting.oldPassword')" prop="oldPassword">
<el-input type="password" show-password clearable v-model="passForm.oldPassword" />
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-form-item :label="$t('setting.newPassword')" prop="newPassword">
<el-input type="password" show-password clearable v-model="passForm.newPassword" />
</el-form-item>
<el-form-item label="确认密码" prop="retryPassword">
<el-form-item :label="$t('setting.retryPassword')" prop="retryPassword">
<el-input type="password" show-password clearable v-model="passForm.retryPassword" />
</el-form-item>
</el-form>
@ -142,9 +144,9 @@
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { ref, reactive, computed } from 'vue';
import { ElMessage, ElForm } from 'element-plus';
import { updateSetting, updatePassword } from '@/api/modules/setting';
import { updateSetting, updatePassword, syncTime } from '@/api/modules/setting';
import { Setting } from '@/api/interface/setting';
import { useI18n } from 'vue-i18n';
import { GlobalStore } from '@/store';
@ -154,6 +156,7 @@ import router from '@/routers/router';
const i18n = useI18n();
const globalStore = GlobalStore();
const themeConfig = computed(() => globalStore.themeConfig);
type FormInstance = InstanceType<typeof ElForm>;
const passFormRef = ref<FormInstance>();
@ -193,9 +196,13 @@ const SaveSetting = async (key: string, val: string) => {
i18n.locale.value = val;
globalStore.updateLanguage(val);
break;
case 'theme':
case 'Theme':
globalStore.setThemeConfig({ ...themeConfig.value, theme: val });
switchDark();
break;
case 'PanelName':
globalStore.setThemeConfig({ ...themeConfig.value, panelName: val });
break;
}
let param = {
key: key,
@ -218,4 +225,9 @@ const submitChangePassword = async () => {
router.push({ name: 'login' });
globalStore.setLogStatus(false);
};
const onSyncTime = async () => {
const res = await syncTime();
form.settingInfo.localTime = res.data;
ElMessage.success(i18n.t('commons.msg.operationSuccess'));
};
</script>

View File

@ -63,7 +63,7 @@
</template>
</el-input>
<div>
<span class="input-help"> 为面板密码设置过期时间过期后需要重新设置密码 </span>
<span class="input-help">为面板密码设置过期时间过期后需要重新设置密码</span>
</div>
</el-form-item>
<el-form-item label="密码复杂度验证">