| @@ -0,0 +1,15 @@ | |||
| FROM golang:latest | |||
| WORKDIR /app | |||
| COPY . . | |||
| RUN #go env -w GO111MODULE=off | |||
| RUN apt-get update && apt-get install -y tzdata | |||
| ENV TZ=Asia/Shanghai | |||
| RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone | |||
| RUN go env -w GOPROXY=https://goproxy.cn,direct | |||
| RUN go build -o staff-sync | |||
| EXPOSE 18080 | |||
| CMD ["./staff-sync"] | |||
| @@ -0,0 +1,6 @@ | |||
| runtime: go120 | |||
| api_version: go1 | |||
| handlers: | |||
| - url: /.* | |||
| script: _go_app | |||
| @@ -0,0 +1,8 @@ | |||
| #!/bin/bash | |||
| # 重新构建docker镜像 | |||
| sudo docker build -t staff-sync . | |||
| sudo mkdir -p /home/digimeta/staff-sync | |||
| # 运行生成的docker镜像 | |||
| sudo docker run --add-host='oa.dfwytech.net:10.1.31.231' -v /home/digimeta/staff-sync/logs:/app/logs -v /home/digimeta/staff-sync/config.json:/app/config.json --restart=always -p 48480:18080 --name staff-sync -d staff-sync | |||
| sudo tail -f -n 500 /home/digimeta/logs/web.log | |||
| @@ -0,0 +1,896 @@ | |||
| package business | |||
| /** | |||
| * @Author: yk | |||
| * @Date: 2024/01/30 15:04 | |||
| * @Desc: 外部接口实现,关于人员的接口 | |||
| */ | |||
| import ( | |||
| "bytes" | |||
| "digimetastaffsync/common" | |||
| "digimetastaffsync/mydatabase" | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| "io" | |||
| "mime/multipart" | |||
| "net/http" | |||
| "os" | |||
| "sort" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| ) | |||
| var timeStep int | |||
| func init() { | |||
| common.LoadConfig() | |||
| //初始化时间步长时间步长 | |||
| timeStep = common.ConfigData.Timer | |||
| } | |||
| type FeatureResp struct { | |||
| Status int `json:"status"` | |||
| Message string `json:"message"` | |||
| Result []interface{} `json:"result"` | |||
| } | |||
| type Staff struct { | |||
| ID string `json:"id"` | |||
| Name string `json:"name"` | |||
| NickName string `json:"nickName"` | |||
| Phone string `json:"phone"` | |||
| Type int `json:"type"` | |||
| Url string `json:"Url"` | |||
| Image string `json:"image"` | |||
| } | |||
| type Req struct { | |||
| Code string `json:"code"` | |||
| StaffName string `json:"staffName"` | |||
| StaffType int `json:"staffType"` | |||
| Gender int `json:"gender"` | |||
| DevId string `json:"devId"` | |||
| FaceFeature string `json:"faceFeature"` | |||
| HireDate string `json:"hireDate"` | |||
| Birthday string `json:"birthDate"` | |||
| Phone string `json:"phone"` | |||
| StaffBase64Img string `json:"staffBase64Img"` | |||
| DelFlag int `json:"status"` | |||
| Modified string `json:"modified"` | |||
| } | |||
| func RegStaff(param Req) (*common.Response, error) { | |||
| url := common.ConfigData.PushDataUrl | |||
| common.Info(common.GenLogLine()+"reg staff url:%s\n", url) | |||
| method := "POST" | |||
| param.DevId = common.ConfigData.DevId | |||
| if len(param.StaffBase64Img) < 20000 { | |||
| param.StaffBase64Img = "" | |||
| } | |||
| fmt.Printf("code:%s, staffName:%s, staffType:%d, gender:%d, devId:%s, faceFeature:%s, hireDate:%s, birthday:%s, phone:%s, DelFlag:%d, modified:%s\n", | |||
| param.Code, param.StaffName, param.StaffType, param.Gender, param.DevId, param.FaceFeature, param.HireDate, param.Birthday, param.Phone, param.DelFlag, param.Modified) | |||
| common.Info(common.GenLogLine()+"exec reg code:%s, staffName:%s, staffType:%d, gender:%d, devId:%s, faceFeature:%s, hireDate:%s, birthday:%s, phone:%s, DelFlag:%d, modified:%s\n", | |||
| param.Code, param.StaffName, param.StaffType, param.Gender, param.DevId, param.FaceFeature, param.HireDate, param.Birthday, param.Phone, param.DelFlag, param.Modified) | |||
| fmt.Println() | |||
| jsonData, err := json.Marshal(param) | |||
| if err != nil { | |||
| fmt.Println("Error marshaling JSON:", err) | |||
| } | |||
| req, err := http.NewRequest(method, url, bytes.NewBuffer(jsonData)) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil, err | |||
| } | |||
| req.Header.Add("Content-Type", "application/json") | |||
| client := &http.Client{} | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil, err | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil, err | |||
| } | |||
| var response *common.Response | |||
| err = json.Unmarshal([]byte(string(body)), &response) | |||
| if err != nil { | |||
| fmt.Println("Error:", err) | |||
| return nil, err | |||
| } | |||
| common.Error(common.GenLogLine()+"reg result %s", string(body)) | |||
| return response, nil | |||
| } | |||
| func RegOnlineStaff(param Req) error { | |||
| url := common.ConfigData.PushDataUrl | |||
| fmt.Printf("url:%s\n", url) | |||
| method := "POST" | |||
| param.DevId = common.ConfigData.DevId | |||
| fmt.Printf("====param:%v\n", param) | |||
| jsonData, err := json.Marshal(param) | |||
| if err != nil { | |||
| fmt.Println("Error marshaling JSON:", err) | |||
| } | |||
| req, err := http.NewRequest(method, url, bytes.NewBuffer(jsonData)) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err | |||
| } | |||
| req.Header.Add("Content-Type", "application/json") | |||
| client := &http.Client{} | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err | |||
| } | |||
| fmt.Println(string(body)) | |||
| return nil | |||
| } | |||
| func HandleStaff(emp Req) error { | |||
| if emp.StaffBase64Img != "" { | |||
| //feature = FetchStaffFaceFeature(emp.StaffBase64Img) | |||
| //emp.FaceFeature = LocalFetchStaffFaceFeature(emp.StaffBase64Img) | |||
| } | |||
| var sType int | |||
| if emp.StaffType > 70 { | |||
| sType = 4 | |||
| } else { | |||
| sType = 5 | |||
| } | |||
| emp.StaffType = sType | |||
| var sDels = []int{5, 6, 7, 8} | |||
| if common.AryContainInt(sDels, emp.DelFlag) { | |||
| emp.DelFlag = 1 | |||
| } else { | |||
| emp.DelFlag = 0 | |||
| } | |||
| RegStaff(emp) | |||
| return nil | |||
| } | |||
| // Fetch2 获取员工信息,模拟获取saas的人员数据 | |||
| func Fetch2() []Staff { | |||
| _token, err := common.GetToken() | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil | |||
| } | |||
| url := common.ConfigData.FetchDataUrl | |||
| method := "GET" | |||
| payload := strings.NewReader(``) | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, payload) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil | |||
| } | |||
| token := fmt.Sprintf(`Bearer %s`, _token) | |||
| req.Header.Add("Authorization", token) | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil | |||
| } | |||
| var response *common.Response | |||
| err = json.Unmarshal([]byte(string(body)), &response) | |||
| if err != nil { | |||
| fmt.Println("Error:", err) | |||
| } | |||
| var result []Staff | |||
| switch v := response.Data["items"].(type) { | |||
| case []interface{}: | |||
| for _, item := range v { | |||
| var s Staff | |||
| //fmt.Println(item) | |||
| if item.(map[string]interface{})["id"] != nil { | |||
| s.ID = (item.(map[string]interface{})["id"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["name"] != nil { | |||
| s.Name = (item.(map[string]interface{})["name"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["nickName"] != nil { | |||
| s.NickName = (item.(map[string]interface{})["nickName"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["phone"] != nil { | |||
| s.Phone = (item.(map[string]interface{})["phone"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["type"] != nil { | |||
| s.Type = (item.(map[string]interface{})["type"]).(int) | |||
| } | |||
| if item.(map[string]interface{})["resource"] != nil { | |||
| if item.(map[string]interface{})["resource"].(map[string]interface{})["url"] != nil { | |||
| s.Url = (item.(map[string]interface{})["resource"].(map[string]interface{})["url"]).(string) | |||
| } | |||
| } | |||
| result = append(result, s) | |||
| } | |||
| default: | |||
| fmt.Println("无法解析的类型") | |||
| } | |||
| return result | |||
| } | |||
| // AbleSendCode 发送验证码 | |||
| func AbleSendCode(phone string) string { | |||
| url := common.ConfigData.CheckSendCodeUrl + "/" + phone | |||
| method := "GET" | |||
| payload := strings.NewReader("") | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, payload) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err.Error() | |||
| } | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err.Error() | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err.Error() | |||
| } | |||
| fmt.Println("response Body:", string(body)) | |||
| return string(body) | |||
| } | |||
| func SendCode(phone string) string { | |||
| ableSend := AbleSendCode(phone) | |||
| fmt.Println("ableSend:", ableSend) | |||
| url := common.ConfigData.SendCodeUrl | |||
| method := "POST" | |||
| payload := strings.NewReader(`{ | |||
| "phone":"` + phone + `"}`) | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, payload) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err.Error() | |||
| } | |||
| req.Header.Add("Content-Type", "application/json") | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err.Error() | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err.Error() | |||
| } | |||
| fmt.Println("response Body:", string(body)) | |||
| var result map[string]interface{} | |||
| err = json.Unmarshal([]byte(string(body)), &result) | |||
| if err != nil { | |||
| fmt.Println("解析JSON出错:", err) | |||
| return err.Error() | |||
| } | |||
| return string(body) | |||
| } | |||
| func ValidCode(phone string, code string) (string, error) { | |||
| ableSend := AbleSendCode(phone) | |||
| fmt.Println("ableSend:", ableSend) | |||
| url := common.ConfigData.ValidCodeUrl + phone + "/" + code | |||
| method := "GET" | |||
| payload := strings.NewReader(``) | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, payload) | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return "", err | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return "", err | |||
| } | |||
| fmt.Println("response Body:", string(body)) | |||
| common.Error(common.GenLogLine()+"reg result %s", string(body)) | |||
| return string(body), nil | |||
| } | |||
| func StaffInfo(staffId string) (map[string]interface{}, error) { | |||
| fmt.Println("staffId:", staffId) | |||
| url := common.ConfigData.StaffInfoUrl | |||
| method := "GET" | |||
| token, err := common.GetToken() | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| payload := &bytes.Buffer{} | |||
| writer := multipart.NewWriter(payload) | |||
| _ = writer.WriteField("id", staffId) | |||
| err = writer.Close() | |||
| if err != nil { | |||
| fmt.Println(err) | |||
| return nil, err | |||
| } | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, payload) | |||
| if err != nil { | |||
| fmt.Println(err) | |||
| return nil, err | |||
| } | |||
| req.Header.Add("Authorization", "Bearer "+token) | |||
| req.Header.Set("Content-Type", writer.FormDataContentType()) | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Println(err) | |||
| return nil, err | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Println(err) | |||
| return nil, err | |||
| } | |||
| fmt.Println(string(body)) | |||
| var result map[string]interface{} | |||
| err = json.Unmarshal([]byte(string(body)), &result) | |||
| if err != nil { | |||
| fmt.Println("解析JSON出错:", err) | |||
| return nil, err | |||
| } | |||
| items, ok := result["data"].(map[string]interface{})["items"].([]interface{}) | |||
| if !ok { | |||
| fmt.Println("无法获取items字段") | |||
| return nil, err | |||
| } | |||
| if len(items) == 0 { | |||
| return nil, errors.New("未找到员工信息") | |||
| } | |||
| firstItem := items[0].(map[string]interface{}) | |||
| common.Error(common.GenLogLine()+"info result %v", firstItem) | |||
| return firstItem, nil | |||
| } | |||
| func MgrLogin(userName string, password string) string { | |||
| url := common.ConfigData.MgrLoginUrl | |||
| fmt.Println("url:", url) | |||
| enterpriseName := common.ConfigData.EnterpriseName | |||
| method := "POST" | |||
| payload := strings.NewReader(`{ | |||
| "enterpriseName":"` + enterpriseName + `", | |||
| "userName":"` + userName + `", | |||
| "password":"` + password + `"}`) | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, payload) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err.Error() | |||
| } | |||
| req.Header.Add("Content-Type", "application/json") | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err.Error() | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return err.Error() | |||
| } | |||
| fmt.Println("response Body:", string(body)) | |||
| return string(body) | |||
| } | |||
| func PhoneLogin(phone string) (map[string]interface{}, error) { | |||
| url := common.ConfigData.PhoneLoginUrl | |||
| method := "POST" | |||
| payload := strings.NewReader(`{ | |||
| "phone":"` + phone + `"}`) | |||
| client := &http.Client{} | |||
| req, _ := http.NewRequest(method, url, payload) | |||
| req.Header.Add("Content-Type", "application/json") | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil, err | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil, err | |||
| } | |||
| fmt.Println("response Body:", string(body)) | |||
| var result map[string]interface{} | |||
| err = json.Unmarshal([]byte(string(body)), &result) | |||
| if err != nil { | |||
| fmt.Println("解析JSON出错:", err) | |||
| return nil, err | |||
| } | |||
| return result, nil | |||
| } | |||
| func Fetch() []Req { | |||
| url := common.ConfigData.FetchData2Url | |||
| method := "POST" | |||
| payload := strings.NewReader("appId=dfwycustomer2u8&ygbh") | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, payload) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil | |||
| } | |||
| req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return nil | |||
| } | |||
| var response *common.Resp | |||
| err = json.Unmarshal([]byte(string(body)), &response) | |||
| if err != nil { | |||
| fmt.Println("Error:", err) | |||
| } | |||
| var result []Req | |||
| fmt.Printf("====%#v====datassss\n", response) | |||
| for _, item := range response.Data { | |||
| var s Req | |||
| //fmt.Println(item) | |||
| if item.(map[string]interface{})["workcode"] != nil { | |||
| s.Code = (item.(map[string]interface{})["workcode"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["name"] != nil { | |||
| s.StaffName = (item.(map[string]interface{})["name"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["mobile"] != nil { | |||
| s.Phone = (item.(map[string]interface{})["mobile"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["level"] != nil { | |||
| s.StaffType, _ = strconv.Atoi((item.(map[string]interface{})["level"]).(string)) | |||
| } | |||
| if item.(map[string]interface{})["birthday"] != nil { | |||
| s.Birthday = (item.(map[string]interface{})["birthday"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["companystartdate"] != nil { | |||
| s.HireDate = (item.(map[string]interface{})["companystartdate"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["status"] != nil { | |||
| s.DelFlag, _ = strconv.Atoi((item.(map[string]interface{})["status"]).(string)) | |||
| } | |||
| if item.(map[string]interface{})["image"] != nil { | |||
| s.StaffBase64Img = (item.(map[string]interface{})["image"]).(string) | |||
| } | |||
| if item.(map[string]interface{})["modified"] != nil { | |||
| s.Modified = (item.(map[string]interface{})["modified"]).(string) | |||
| } | |||
| result = append(result, s) | |||
| } | |||
| if len(result) > 0 { | |||
| sort.Slice(result, func(i, j int) bool { | |||
| a, err := time.Parse("2006-01-02 15:04:05", result[i].Modified) | |||
| if err != nil { | |||
| common.Error(common.GenLogLine() + err.Error()) | |||
| } | |||
| b, err2 := time.Parse("2006-01-02 15:04:05", result[j].Modified) | |||
| if err2 != nil { | |||
| common.Error(common.GenLogLine() + err2.Error()) | |||
| } | |||
| return b.Before(a) | |||
| }) | |||
| } | |||
| return result | |||
| } | |||
| func FetchStaffImage(ygbh string) string { | |||
| url := common.ConfigData.FetchData2Url | |||
| method := "POST" | |||
| payload := strings.NewReader("appId=dfwycustomer2u8&ygbh=" + fmt.Sprintf("%s", ygbh)) | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, payload) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return "" | |||
| } | |||
| req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | |||
| res, _ := client.Do(req) | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return "" | |||
| } | |||
| var response *common.Resp | |||
| err = json.Unmarshal([]byte(string(body)), &response) | |||
| if err != nil { | |||
| fmt.Println("Error:", err) | |||
| } | |||
| var result string = "" | |||
| for _, item := range response.Data { | |||
| var s string | |||
| //fmt.Println(item) | |||
| if item.(map[string]interface{})["image"] != nil { | |||
| s = (item.(map[string]interface{})["image"]).(string) | |||
| } | |||
| result = s | |||
| } | |||
| return strings.Replace(result, "\n", "", -1) | |||
| } | |||
| func FetchStaffFaceFeature(base64 string) string { | |||
| url := "http://39.105.51.226:5000/cv/feature-extraction-service/1.7" | |||
| method := "POST" | |||
| err := common.SaveBase64ImageToFile(base64, "images/"+fmt.Sprintf("%d", time.Now().Unix())+"test.jpg") | |||
| fmt.Printf("%v\n", err) | |||
| payload := strings.NewReader(`{ | |||
| "detect":true, | |||
| "base64Data":"` + base64 + `"}`) | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, payload) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return "" | |||
| } | |||
| req.Header.Add("Authorization", "Bearer ") | |||
| req.Header.Add("Content-Type", "application/json") | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return "" | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return "" | |||
| } | |||
| fmt.Println(string(body)) | |||
| var response *FeatureResp | |||
| err = json.Unmarshal([]byte(string(body)), &response) | |||
| if err != nil { | |||
| fmt.Println("Error:", err) | |||
| } | |||
| var result string | |||
| result, err = extractFeature(response.Result[0]) | |||
| fmt.Printf(" success data : %+v\n", result) | |||
| return result | |||
| } | |||
| func extractFeature(data interface{}) (string, error) { | |||
| // 将 interface{} 类型转换为 map[string]interface{} | |||
| resultMap, ok := data.(map[string]interface{}) | |||
| if !ok { | |||
| return "", fmt.Errorf("Invalid data format") | |||
| } | |||
| // 取出 "faces" 字段 | |||
| faces, ok := resultMap["faces"].([]interface{}) | |||
| if !ok || len(faces) == 0 { | |||
| return "", fmt.Errorf("No faces found") | |||
| } | |||
| // 取出 "feature" 字段 | |||
| feature, ok := faces[0].(map[string]interface{})["feature"].([]interface{}) | |||
| if !ok || len(feature) == 0 { | |||
| return "", fmt.Errorf("No feature found") | |||
| } | |||
| // 转换 feature 字段为 []float64 类型 | |||
| var featureValues []float64 | |||
| for _, value := range feature { | |||
| if floatValue, ok := value.(float64); ok { | |||
| featureValues = append(featureValues, floatValue) | |||
| } else { | |||
| return "", fmt.Errorf("Invalid feature value type") | |||
| } | |||
| } | |||
| str := common.FloatSliceToString(featureValues) | |||
| return str, nil | |||
| } | |||
| func LocalFetchStaffFaceFeature(base64 string) (string, string, error) { | |||
| url := common.ConfigData.LocalDetectUrl | |||
| method := "POST" | |||
| imgName := "static/faces/" + fmt.Sprintf("%d", time.Now().Unix()) + "test.jpg" | |||
| err := common.SaveBase64ImageToFile(base64, imgName) | |||
| payload := strings.NewReader(`{ | |||
| "imgbase64":"` + base64 + `"}`) | |||
| client := &http.Client{} | |||
| req, _ := http.NewRequest(method, url, payload) | |||
| //req.Header.Add("Authorization", "Bearer ") | |||
| req.Header.Add("Content-Type", "application/json") | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return "", imgName, err | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(common.GenLogLine()+"%s", err) | |||
| common.Error(common.GenLogLine()+"%s", err) | |||
| return "", imgName, err | |||
| } | |||
| fmt.Println(string(body)) | |||
| var response map[string]interface{} | |||
| err = json.Unmarshal([]byte(string(body)), &response) | |||
| if err != nil { | |||
| fmt.Println("Error:", err) | |||
| return "", imgName, err | |||
| } | |||
| faces, ok := response["data"].(map[string]interface{}) | |||
| if !ok { | |||
| fmt.Errorf("No faces found") | |||
| } | |||
| feature, ok1 := faces["faceData"].(map[string]interface{})["theFeature"].([]interface{}) | |||
| if !ok1 || len(feature) == 0 { | |||
| return "", imgName, err | |||
| } | |||
| // 转换 feature 字段为 []float64 类型 | |||
| var featureValues []float64 | |||
| for _, value := range feature { | |||
| if floatValue, ok := value.(float64); ok { | |||
| featureValues = append(featureValues, floatValue) | |||
| } else { | |||
| return "", imgName, err | |||
| } | |||
| } | |||
| str := common.FloatSliceToString(featureValues) | |||
| return str, imgName, nil | |||
| } | |||
| func GetMaxId(dataList []Staff) string { | |||
| maxID := dataList[0].ID | |||
| common.Info(common.GenLogLine()+"maxID:", maxID) | |||
| for _, data := range dataList { | |||
| if data.ID > maxID { | |||
| maxID = data.ID | |||
| common.Info(common.GenLogLine()+"maxID:%s", maxID) | |||
| } | |||
| } | |||
| return maxID | |||
| } | |||
| func GetNewestModified(dataList []Req) string { | |||
| if len(dataList) == 0 { | |||
| return "" | |||
| } | |||
| maxID := dataList[0].Modified | |||
| return maxID | |||
| } | |||
| func TimerHandle() { | |||
| fmt.Printf("TimerHandle start %d\n", timeStep) | |||
| ticker := time.NewTicker(time.Second * time.Duration(timeStep)) | |||
| defer ticker.Stop() | |||
| for range ticker.C { //十秒执行一次 | |||
| common.Info(common.GenLogLine() + "=====执行timerHandle开始=====") | |||
| now := time.Now() | |||
| // 将时间设置为23点59分59秒 | |||
| t := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 55, 0, now.Location()) | |||
| common.Info(common.GenLogLine() + "--1,执行日志分割 START--") | |||
| duration := t.Sub(now) | |||
| isWithin10Seconds := duration > 0*time.Second && duration <= time.Duration(timeStep)*time.Second | |||
| fmt.Fprintln(os.Stdout, isWithin10Seconds) | |||
| if isWithin10Seconds { | |||
| common.Info(common.GenLogLine() + "--1.1执行RenameLogFile--") | |||
| common.RenameLogFile() | |||
| } | |||
| common.Info(common.GenLogLine() + "--1,执行日志分割 END--") | |||
| common.Info(common.GenLogLine() + "--2,数据同步 START--") | |||
| //获取接口数据 | |||
| staffs := Fetch() | |||
| cachedTime := common.GetCache("maxTime") | |||
| if len(staffs) == 0 { | |||
| common.Info(common.GenLogLine() + "数据为空") | |||
| } else { | |||
| //获取最新更新时间 | |||
| maxTime := GetNewestModified(staffs) | |||
| var t1 time.Time | |||
| var t2 time.Time | |||
| var t3 time.Time | |||
| if cachedTime == "" { | |||
| cachedTime = common.ReadFile("di.txt") | |||
| common.SetCacheWithExpire("maxTime", cachedTime, 0) | |||
| } | |||
| t1, _ = time.Parse("2006-01-02 15:04:05", cachedTime) | |||
| t2, _ = time.Parse("2006-01-02 15:04:05", maxTime) | |||
| if !t2.After(t1) { | |||
| common.Info(common.GenLogLine() + "--2.1, 数据已被同步") | |||
| } else { | |||
| common.SetCacheWithExpire("maxTime", maxTime, 0) | |||
| for _, staff := range staffs { | |||
| time.Sleep(1 * time.Second) // 暂停1秒 | |||
| t3, _ = time.Parse("2006-01-02 15:04:05", staff.Modified) | |||
| if t3.After(t1) { | |||
| staff.StaffBase64Img = FetchStaffImage(staff.Code) | |||
| common.Info(common.GenLogLine()+"----%v", "" == staff.StaffBase64Img) | |||
| str := HandleStaff(staff) | |||
| common.Info(common.GenLogLine()+"--2.2注册人员 姓名:%s 编号:%s 更新时间:%s--\n", staff.StaffName, staff.Code, staff.Modified) | |||
| //str := RegOnlineStaff(staff) | |||
| ss := "" | |||
| if str == nil { | |||
| ss = "保存成功" | |||
| } else { | |||
| ss = str.Error() | |||
| } | |||
| fmt.Printf("注册人员:%v\n", ss) | |||
| common.Info(common.GenLogLine()+"--2.3注册人员:%v--\n", ss) | |||
| } else { | |||
| break | |||
| } | |||
| } | |||
| common.WriteFile("di.txt", maxTime) | |||
| common.Info(common.GenLogLine()+"--2.4,数据同步,缓存数据maxID-- :%s", common.GetCache("maxTime")) | |||
| } | |||
| } | |||
| common.Info(common.GenLogLine() + "--2,数据同步 END--") | |||
| common.Info(common.GenLogLine() + "=====执行timerHandle结束=====") | |||
| } | |||
| } | |||
| func EmpToReq(emp mydatabase.Emp) Req { | |||
| var req Req | |||
| req.StaffName = emp.Name | |||
| req.StaffType = 5 //普通员工 | |||
| req.Phone = emp.Phone | |||
| req.StaffBase64Img = emp.Avatar | |||
| req.FaceFeature = emp.Features | |||
| return req | |||
| } | |||
| @@ -0,0 +1,125 @@ | |||
| package common | |||
| /** | |||
| * @Author:yk | |||
| * @Date: 2024/01/19 | |||
| * @Desc: 缓存方法,文件读取写入方法 | |||
| */ | |||
| import ( | |||
| "encoding/base64" | |||
| "fmt" | |||
| "github.com/coocood/freecache" | |||
| "os" | |||
| "path/filepath" | |||
| "runtime/debug" | |||
| "strings" | |||
| "sync" | |||
| ) | |||
| var ( | |||
| instance *freecache.Cache | |||
| mu sync.Mutex | |||
| ) | |||
| // GetInstance 返回单例缓存对象 | |||
| func GetInstance() *freecache.Cache { | |||
| if instance == nil { | |||
| mu.Lock() | |||
| defer mu.Unlock() | |||
| if instance == nil { | |||
| cacheSize := 100 * 1024 * 1024 | |||
| cache := freecache.NewCache(cacheSize) | |||
| instance = cache | |||
| } | |||
| } | |||
| return instance | |||
| } | |||
| // SetCache 设置缓存,默认过期时间为60秒 | |||
| func SetCache(k string, v string) { | |||
| SetCacheWithExpire(k, v, 60) | |||
| } | |||
| // SetCacheWithExpire 设置缓存 | |||
| func SetCacheWithExpire(k string, v string, t int) { | |||
| debug.SetGCPercent(20) | |||
| key := []byte(k) | |||
| val := []byte(v) | |||
| GetInstance().Set(key, val, t) | |||
| } | |||
| // GetCache 获取缓存 | |||
| func GetCache(k string) string { | |||
| key := []byte(k) | |||
| got, err := GetInstance().Get(key) | |||
| if err != nil { | |||
| fmt.Printf(GenLogLine()+"%s", err) | |||
| Error(GenLogLine()+"%s", err) | |||
| return "" | |||
| } else { | |||
| return string(got) | |||
| } | |||
| } | |||
| // ReadFile 读取文件内容 | |||
| func ReadFile(filePath string) string { | |||
| content, err := os.ReadFile(filePath) // 读取文件内容 | |||
| if err != nil { | |||
| fmt.Printf("读取文件失败:%v\n", err) | |||
| Error("读取文件失败:%v\n", err) | |||
| return "" | |||
| } | |||
| fmt.Println(string(content)) | |||
| return string(content) | |||
| } | |||
| // WriteFile 写入文件内容 | |||
| func WriteFile(filePath string, content string) error { | |||
| file, err := os.Create(filePath) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| defer file.Close() | |||
| _, err = file.WriteString(content) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func SaveBase64ImageToFile(base64Image string, outputFileName string) error { | |||
| // 解码 base64 编码的图片数据 | |||
| imageData, err := base64.StdEncoding.DecodeString(extractBase64Data(base64Image)) | |||
| if err != nil { | |||
| return fmt.Errorf("Error decoding base64 image data: %v", err) | |||
| } | |||
| if len(imageData) == 0 { | |||
| return fmt.Errorf("Decoded image data is empty") | |||
| } | |||
| // 确保目录存在 | |||
| outputDir := filepath.Dir(outputFileName) | |||
| if err := os.MkdirAll(outputDir, 0755); err != nil { | |||
| return fmt.Errorf("Error creating directories: %v", err) | |||
| } | |||
| // 保存图片数据为文件 | |||
| err = os.WriteFile(outputFileName, imageData, 0644) | |||
| if err != nil { | |||
| return fmt.Errorf("Error saving image data to file: %v", err) | |||
| } | |||
| fmt.Printf("Image saved to %s\n", outputFileName) | |||
| return nil | |||
| } | |||
| // extractBase64Data 从 base64 编码的图片数据中提取实际的 base64 数据部分 | |||
| func extractBase64Data(base64Image string) string { | |||
| // 图片数据格式为 "data:image/png;base64,...",我们需要提取逗号后的部分 | |||
| parts := strings.SplitN(base64Image, ",", 2) | |||
| if len(parts) != 2 { | |||
| return base64Image | |||
| } | |||
| return parts[1] | |||
| } | |||
| @@ -0,0 +1,203 @@ | |||
| package common | |||
| import ( | |||
| "bytes" | |||
| "encoding/json" | |||
| "fmt" | |||
| "io" | |||
| "net/http" | |||
| "runtime" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| ) | |||
| type Response struct { | |||
| Msg string `json:"msg"` | |||
| Code int `json:"code"` | |||
| Data map[string]interface{} `json:"data"` | |||
| Type string `json:"type"` | |||
| } | |||
| type Resp struct { | |||
| Code int `json:"code"` | |||
| Message string `json:"message"` | |||
| Data []interface{} `json:"data"` | |||
| } | |||
| type InterfaceResp struct { | |||
| Status int `json:"code"` | |||
| Err string `json:"message"` | |||
| Data interface{} `json:"data"` | |||
| } | |||
| func GenLogLine() string { | |||
| pc, _, _, ok := runtime.Caller(1) | |||
| if !ok { | |||
| return "unknown" | |||
| } | |||
| // 获取函数信息 | |||
| f := runtime.FuncForPC(pc) | |||
| if f == nil { | |||
| return "unknown" | |||
| } | |||
| // 获取文件名和行号 | |||
| file, line := f.FileLine(pc) | |||
| // 获取函数名 | |||
| funcName := f.Name() | |||
| return fmt.Sprintf("file:%s; func:%s %d行:", TruncateLastSlash(file), TruncateLastSlash(funcName), line) | |||
| } | |||
| func FloatSliceToString(floats []float64) string { | |||
| var strSlice []string | |||
| for _, f := range floats { | |||
| strSlice = append(strSlice, strconv.FormatFloat(f, 'f', -1, 64)) | |||
| } | |||
| return "[" + strings.Join(strSlice, ", ") + "]" | |||
| } | |||
| func TruncateLastSlash(inputString string) string { | |||
| lastIndex := strings.LastIndex(inputString, "/") | |||
| if lastIndex == -1 { | |||
| // 没有反斜线,返回原始字符串 | |||
| return inputString | |||
| } | |||
| // 截断字符串并返回 | |||
| return inputString[lastIndex+1:] | |||
| } | |||
| func GetToken() (string, error) { | |||
| url := ConfigData.FetchTokenUrl | |||
| method := "POST" | |||
| Info(GenLogLine() + "获取token") | |||
| token := GetCache("token") | |||
| if "" != token { | |||
| Info(GenLogLine() + "====get token from cache====") | |||
| return token, nil | |||
| } | |||
| payload := fmt.Sprintf(`{"deviceId":"%s"}`, ConfigData.DevId) | |||
| input := strings.NewReader(payload) | |||
| client := &http.Client{} | |||
| req, err := http.NewRequest(method, url, input) | |||
| if err != nil { | |||
| return "", fmt.Errorf("create request fail: %w", err) | |||
| } | |||
| req.Header.Add("Content-Type", "application/json") | |||
| res, err := client.Do(req) | |||
| if err != nil { | |||
| fmt.Errorf(GenLogLine()+"%s", err) | |||
| Error(GenLogLine()+"%s", err) | |||
| return "", fmt.Errorf("request fail: %w", err) | |||
| } | |||
| defer res.Body.Close() | |||
| body, err := io.ReadAll(res.Body) | |||
| if err != nil { | |||
| fmt.Errorf(GenLogLine()+"%s", err) | |||
| Error(GenLogLine()+"%s", err) | |||
| return "", fmt.Errorf("handle resp fail: %w", err) | |||
| } | |||
| var response Response | |||
| err = json.Unmarshal([]byte(string(body)), &response) | |||
| if err != nil { | |||
| Error("Error:", err) | |||
| } | |||
| Info(GenLogLine()+"token:====", response.Data["access_token"]) | |||
| result := response.Data["access_token"].(string) | |||
| SetCacheWithExpire("token", result, 3600) | |||
| return result, nil | |||
| } | |||
| func CovertTime(timeStr string) string { | |||
| location, err := time.LoadLocation("Asia/Shanghai") | |||
| if err != nil { | |||
| fmt.Println("Error loading location:", err) | |||
| return "" | |||
| } | |||
| // 解析日期字符串 | |||
| parsedDate, err := time.ParseInLocation("2006-01-02", timeStr, location) | |||
| if err != nil { | |||
| fmt.Println("Error parsing date:", err) | |||
| return "" | |||
| } | |||
| // 输出日期 | |||
| fmt.Println("Parsed Date:", parsedDate) | |||
| // 将日期格式化为字符串 | |||
| formattedDate := parsedDate.Format("2006-01-02") | |||
| fmt.Println("Formatted Date:", formattedDate) | |||
| // 传递给 Java 的字符串日期(带时区信息) | |||
| javaDateString := parsedDate.Format("2006-01-02T15:04:05Z07:00") | |||
| return javaDateString | |||
| } | |||
| func exec() { | |||
| ticker := time.NewTicker(5 * time.Minute) | |||
| defer ticker.Stop() | |||
| for range ticker.C { | |||
| // 请求接口A | |||
| respA, err := http.Get("http://localhost:8080/api/interfaceA") | |||
| if err != nil { | |||
| fmt.Println("请求接口A失败:", err) | |||
| continue | |||
| } | |||
| defer respA.Body.Close() | |||
| // 读取接口A返回的数据 | |||
| dataA, err := io.ReadAll(respA.Body) | |||
| if err != nil { | |||
| fmt.Println("读取接口A返回数据失败:", err) | |||
| continue | |||
| } | |||
| // 将接口A返回的数据通过接口B发送到某个系统 | |||
| respB, err := http.Post("http://localhost:8080/api/interfaceB", "application/json", bytes.NewBuffer(dataA)) | |||
| if err != nil { | |||
| Error("发送数据到接口B失败:", err) | |||
| continue | |||
| } | |||
| defer respB.Body.Close() | |||
| // 检查接口B的响应状态 | |||
| if respB.StatusCode == http.StatusOK { | |||
| Info(GenLogLine() + "数据发送成功") | |||
| } else { | |||
| Error("数据发送失败,状态码:", respB.StatusCode) | |||
| } | |||
| } | |||
| } | |||
| func AryContainInt(s []int, str int) bool { | |||
| found := false | |||
| for _, value := range s { | |||
| if value == str { | |||
| found = true | |||
| break | |||
| } | |||
| } | |||
| return found | |||
| } | |||
| func AryContainStr(s []string, str string) bool { | |||
| found := false | |||
| for _, value := range s { | |||
| if value == str { | |||
| found = true | |||
| break | |||
| } | |||
| } | |||
| return found | |||
| } | |||
| @@ -0,0 +1,121 @@ | |||
| package common | |||
| import ( | |||
| "encoding/json" | |||
| "os" | |||
| ) | |||
| type Config struct { | |||
| FetchTokenUrl string `json:"fetchTokenUrl"` | |||
| Phone string `json:"phone"` | |||
| FetchDataUrl string `json:"fetchDataUrl"` | |||
| FetchData2Url string `json:"fetchData2Url"` | |||
| SendCodeUrl string `json:"sendCodeUrl"` | |||
| CheckSendCodeUrl string `json:"checkSendCodeUrl"` | |||
| PhoneLoginUrl string `json:"phoneLoginUrl"` | |||
| MgrLoginUrl string `json:"mgrLoginUrl"` | |||
| ValidCodeUrl string `json:"validCodeUrl"` | |||
| StaffInfoUrl string `json:"staffInfoUrl"` | |||
| EnterpriseName string `json:"enterpriseName"` | |||
| Port string `json:"port"` | |||
| PushDataUrl string `json:"pushDataUrl"` | |||
| LocalDetectUrl string `json:"localDetectUrl"` | |||
| DevId string `json:"devId"` | |||
| Timer int `json:"timer"` | |||
| Mode string `json:"mode"` | |||
| } | |||
| var ConfigData Config | |||
| func LoadConfig() interface{} { | |||
| // 读取配置文件 | |||
| data, err := os.ReadFile("config.json") | |||
| if err != nil { | |||
| return nil | |||
| } | |||
| // 解析配置文件内容到全局变量中 | |||
| err = json.Unmarshal(data, &ConfigData) | |||
| if err != nil { | |||
| return nil | |||
| } | |||
| return ConfigData | |||
| } | |||
| func UpdateConfig(updateValues map[string]interface{}) string { | |||
| config := ConfigData | |||
| for key, value := range updateValues { | |||
| switch key { | |||
| case "port": | |||
| config.Port = value.(string) | |||
| case "phone": | |||
| config.Phone = value.(string) | |||
| case "devId": | |||
| config.DevId = value.(string) | |||
| case "mode": | |||
| config.Mode = value.(string) | |||
| case "enterpriseName": | |||
| config.EnterpriseName = value.(string) | |||
| case "fetchTokenUrl": | |||
| config.FetchTokenUrl = value.(string) | |||
| case "fetchDataUrl": | |||
| config.FetchDataUrl = value.(string) | |||
| case "fetchData2Url": | |||
| config.FetchData2Url = value.(string) | |||
| case "pushDataUrl": | |||
| config.PushDataUrl = value.(string) | |||
| case "sendCodeUrl": | |||
| config.SendCodeUrl = value.(string) | |||
| case "checkSendCodeUrl": | |||
| config.CheckSendCodeUrl = value.(string) | |||
| case "mgrLoginUrl": | |||
| config.MgrLoginUrl = value.(string) | |||
| case "phoneLoginUrl": | |||
| config.PhoneLoginUrl = value.(string) | |||
| case "localDetectUrl": | |||
| config.LocalDetectUrl = value.(string) | |||
| case "validCodeUrl": | |||
| config.ValidCodeUrl = value.(string) | |||
| case "staffInfoUrl": | |||
| config.StaffInfoUrl = value.(string) | |||
| case "timer": | |||
| config.Timer = int(value.(float64)) | |||
| default: | |||
| return "Unknown config key: " + key | |||
| } | |||
| } | |||
| if err := writeConfigToFile(&config, "config.json"); err != nil { | |||
| return "Failed to write config to file: " + err.Error() | |||
| } | |||
| return "" | |||
| } | |||
| // 从文件中读取配置 | |||
| func readConfigFromFile(filename string) (*Config, error) { | |||
| fileContent, err := os.ReadFile(filename) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| var config Config | |||
| if err := json.Unmarshal(fileContent, &config); err != nil { | |||
| return nil, err | |||
| } | |||
| return &config, nil | |||
| } | |||
| // 将配置写入文件 | |||
| func writeConfigToFile(config *Config, filename string) error { | |||
| configJSON, err := json.MarshalIndent(config, "", " ") | |||
| if err != nil { | |||
| return err | |||
| } | |||
| return os.WriteFile(filename, configJSON, 0644) | |||
| } | |||
| @@ -0,0 +1,101 @@ | |||
| package common | |||
| import ( | |||
| "io" | |||
| "log" | |||
| "os" | |||
| "time" | |||
| ) | |||
| // flags and prefix | |||
| const ( | |||
| flag = log.Ldate | log.Ltime | log.Lshortfile | |||
| preDebug = "[DEBUG] " | |||
| preInfo = "[INFO] " | |||
| preWarning = "[WARNING] " | |||
| preError = "[ERROR] " | |||
| ) | |||
| // diff logger and output log file | |||
| var ( | |||
| logFile io.Writer | |||
| debugLogger *log.Logger | |||
| infoLogger *log.Logger | |||
| warningLogger *log.Logger | |||
| errorLogger *log.Logger | |||
| defaultLogFile = "./logs/web.log" | |||
| ) | |||
| func createFile() { | |||
| var err error | |||
| logFile, err = os.OpenFile(defaultLogFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) | |||
| if err != nil { | |||
| logFile, err = os.OpenFile(defaultLogFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) | |||
| if err != nil { | |||
| log.Fatalf("create log file err %+v", err) | |||
| } | |||
| } | |||
| debugLogger = log.New(io.MultiWriter(os.Stdout, logFile), preDebug, flag) | |||
| infoLogger = log.New(logFile, preInfo, flag) | |||
| warningLogger = log.New(logFile, preWarning, flag) | |||
| errorLogger = log.New(logFile, preError, flag) | |||
| } | |||
| // init for logger | |||
| func init() { | |||
| createFile() | |||
| } | |||
| // Debug logger | |||
| func Debug(format string, v ...interface{}) { | |||
| debugLogger.Printf(format, v...) | |||
| } | |||
| func Info(format string, v ...interface{}) { | |||
| infoLogger.Printf(format, v...) | |||
| } | |||
| func Warning(format string, v ...interface{}) { | |||
| warningLogger.Printf(format, v...) | |||
| } | |||
| func Error(format string, v ...interface{}) { | |||
| errorLogger.Printf(format, v...) | |||
| } | |||
| // set output file | |||
| func setOutputPath(path string) { | |||
| var err error | |||
| logFile, err = os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) | |||
| if err != nil { | |||
| log.Fatalf("create log file err %+v", err) | |||
| } | |||
| debugLogger = log.New(logFile, preDebug, flag) | |||
| infoLogger = log.New(logFile, preInfo, flag) | |||
| warningLogger = log.New(logFile, preWarning, flag) | |||
| errorLogger = log.New(logFile, preError, flag) | |||
| } | |||
| func SetFlags(flag int) { | |||
| debugLogger.SetFlags(flag) | |||
| infoLogger.SetFlags(flag) | |||
| warningLogger.SetFlags(flag) | |||
| errorLogger.SetFlags(flag) | |||
| } | |||
| func RenameLogFile() { | |||
| if _, err := os.Stat(defaultLogFile); os.IsNotExist(err) { | |||
| Info("文件不存在") | |||
| return | |||
| } | |||
| // 重命名文件 | |||
| err := os.Rename(defaultLogFile, "./logs/"+time.Now().Format("2006-01-02")+"-info.log") | |||
| if err != nil { | |||
| Info("重命名失败:", err) | |||
| } else { | |||
| Info("重命名成功") | |||
| createFile() | |||
| } | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| { | |||
| "fetchTokenUrl": "http://127.0.0.1:8080/auth/deviceLogin", | |||
| "phone": "18910801519", | |||
| "fetchDataUrl": "http://127.0.0.1:8080/system/staff/list", | |||
| "fetchData2Url": "http://10.1.26.139/api/dizhiyuan/member/DZYAction", | |||
| "sendCodeUrl": "http://127.0.0.1:8080/system/sms/api/send-code", | |||
| "checkSendCodeUrl": "http://127.0.0.1:8080/system/staff/api/ableLogin", | |||
| "phoneLoginUrl": "http://127.0.0.1:8080/auth/phoneLogin", | |||
| "mgrLoginUrl": "http://127.0.0.1:8080/auth/mgr-login", | |||
| "validCodeUrl": "http://127.0.0.1:8080/system/staff/api/validCode", | |||
| "staffInfoUrl": "http://127.0.0.1:8080/system/staff/list", | |||
| "enterpriseName": "演示机", | |||
| "port": "18080", | |||
| "pushDataUrl": "http://127.0.0.1:8080/system/staff/api/new-staff", | |||
| "localDetectUrl": "http://192.168.10.32:8080/face/feature", | |||
| "devId": "442926c7610ed10b", | |||
| "timer": 90, | |||
| "mode": "online" | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <module type="WEB_MODULE" version="4"> | |||
| <component name="Go" enabled="true"> | |||
| <buildTags> | |||
| <option name="customFlags"> | |||
| <array> | |||
| <option value="appengine" /> | |||
| </array> | |||
| </option> | |||
| </buildTags> | |||
| </component> | |||
| <component name="NewModuleRootManager" inherit-compiler-output="true"> | |||
| <exclude-output /> | |||
| <content url="file://$MODULE_DIR$" /> | |||
| <orderEntry type="sourceFolder" forTests="false" /> | |||
| </component> | |||
| <component name="SonarLintModuleSettings"> | |||
| <option name="uniqueId" value="03c654ca-3133-4399-af16-d5d0ba94b53f" /> | |||
| </component> | |||
| </module> | |||
| @@ -0,0 +1,38 @@ | |||
| module digimetastaffsync | |||
| go 1.20 | |||
| require ( | |||
| github.com/coocood/freecache v1.2.4 | |||
| github.com/gin-gonic/gin v1.9.1 | |||
| ) | |||
| require ( | |||
| github.com/bytedance/sonic v1.10.2 // indirect | |||
| github.com/cespare/xxhash/v2 v2.1.2 // indirect | |||
| github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect | |||
| github.com/chenzhuoyu/iasm v0.9.1 // 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.17.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.6 // indirect | |||
| github.com/leodido/go-urn v1.3.0 // indirect | |||
| github.com/mattn/go-isatty v0.0.20 // indirect | |||
| github.com/mattn/go-sqlite3 v1.14.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.1.1 // indirect | |||
| github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | |||
| github.com/ugorji/go/codec v1.2.12 // indirect | |||
| golang.org/x/arch v0.7.0 // indirect | |||
| golang.org/x/crypto v0.18.0 // indirect | |||
| golang.org/x/net v0.20.0 // indirect | |||
| golang.org/x/sys v0.16.0 // indirect | |||
| golang.org/x/text v0.14.0 // indirect | |||
| google.golang.org/protobuf v1.32.0 // indirect | |||
| gopkg.in/yaml.v3 v3.0.1 // indirect | |||
| ) | |||
| @@ -0,0 +1,96 @@ | |||
| github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= | |||
| github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= | |||
| github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= | |||
| github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= | |||
| github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= | |||
| github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | |||
| github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= | |||
| github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= | |||
| github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= | |||
| github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= | |||
| github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= | |||
| github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= | |||
| github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= | |||
| github.com/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M= | |||
| github.com/coocood/freecache v1.2.4/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk= | |||
| 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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= | |||
| github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= | |||
| github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= | |||
| 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.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= | |||
| github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= | |||
| 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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | |||
| 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.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= | |||
| github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= | |||
| github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= | |||
| github.com/leodido/go-urn v1.3.0 h1:jX8FDLfW4ThVXctBNZ+3cIWnCSnrACDV73r76dy0aQQ= | |||
| github.com/leodido/go-urn v1.3.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= | |||
| 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/mattn/go-sqlite3 v1.14.20 h1:BAZ50Ns0OFBNxdAqFhbZqdPcht1Xlb16pDCqkq1spr0= | |||
| github.com/mattn/go-sqlite3 v1.14.20/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | |||
| 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.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= | |||
| github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= | |||
| 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/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 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | |||
| github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | |||
| 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.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= | |||
| golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | |||
| golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= | |||
| golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= | |||
| golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= | |||
| golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= | |||
| 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.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= | |||
| golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | |||
| golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= | |||
| golang.org/x/text v0.14.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.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= | |||
| google.golang.org/protobuf v1.32.0/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= | |||
| @@ -0,0 +1,291 @@ | |||
| package main | |||
| import ( | |||
| "digimetastaffsync/business" | |||
| "digimetastaffsync/common" | |||
| "digimetastaffsync/mydatabase" | |||
| "fmt" | |||
| "github.com/gin-gonic/gin" | |||
| "io" | |||
| "log" | |||
| "net/http" | |||
| "os" | |||
| "path/filepath" | |||
| "strconv" | |||
| "strings" | |||
| ) | |||
| func init() { | |||
| setupLogger() | |||
| common.SetFlags(log.Ldate | log.Ltime) | |||
| common.LoadConfig() | |||
| } | |||
| func setupLogger() { | |||
| logsDir := "logs" | |||
| logFileName := "web.log" | |||
| logFilePath := filepath.Join(logsDir, logFileName) | |||
| // 检查日志文件是否存在 | |||
| if _, err := os.Stat(logFilePath); os.IsNotExist(err) { | |||
| // 不存在则创建新文件 | |||
| err := os.MkdirAll(logsDir, 0755) | |||
| if err != nil { | |||
| log.Fatalf("Error creating logs directory: %v", err) | |||
| } | |||
| // 创建日志文件 | |||
| file, err := os.Create(logFilePath) | |||
| if err != nil { | |||
| log.Fatalf("Error creating log file: %v", err) | |||
| } | |||
| defer file.Close() | |||
| // 设置 Gin 默认的写入器 | |||
| gin.DefaultWriter = io.MultiWriter(file) | |||
| } else { | |||
| // 存在则打开已有文件 | |||
| file, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_WRONLY, os.ModeAppend) | |||
| if err != nil { | |||
| log.Fatalf("Error opening log file: %v", err) | |||
| } | |||
| defer file.Close() | |||
| // 设置 Gin 默认的写入器 | |||
| gin.DefaultWriter = io.MultiWriter(file) | |||
| } | |||
| // 设置 Gin 的日志输出 | |||
| gin.SetMode(gin.ReleaseMode) | |||
| } | |||
| type Config struct { | |||
| Port string `json:"port"` | |||
| Phone string `json:"phone"` | |||
| Mode string `json:"mode"` | |||
| DevId string `json:"devId"` | |||
| } | |||
| func main() { | |||
| router := gin.Default() | |||
| router.LoadHTMLGlob("templates/*") | |||
| router.GET("/", HomeHandler) | |||
| router.GET("/reg", RegPageHandler) | |||
| router.GET("/info", InfoPageHandler) | |||
| router.POST("/send_validation_code", SendCodeHandler) | |||
| router.POST("/valid_code", ValidCodeHandler) | |||
| router.POST("/phone_login", PhoneLoginHandler) | |||
| router.POST("/mgr_login", MgrLoginHandler) | |||
| router.GET("/mgr", MgrPageHandler) | |||
| router.GET("/test", TestHandler) | |||
| router.GET("/register", RegisterPageHandler) | |||
| router.POST("/register", RegisterHandler) | |||
| router.POST("/pass", PassHandler) | |||
| router.POST("/update", UpdateConfigHandler) | |||
| common.Info(common.GenLogLine()+"file content:%s", common.ReadFile("di.txt")) | |||
| defer mydatabase.GetDb().Close() | |||
| port := common.ConfigData.Port | |||
| if port == "" { | |||
| port = "18080" | |||
| log.Printf("Defaulting to port %s", port) | |||
| } | |||
| // 启动定时任务,用于同步人脸操作 | |||
| go business.TimerHandle() | |||
| log.Printf("Listening on port %s", port) | |||
| log.Printf("Open http://localhost:%s in the browser", port) | |||
| router.Static("/static", "./static") | |||
| router.Run(fmt.Sprintf(":%s", port)) | |||
| } | |||
| // UpdateConfigHandler 更新配置 | |||
| func UpdateConfigHandler(c *gin.Context) { | |||
| var updateValues map[string]interface{} | |||
| if err := c.BindJSON(&updateValues); err != nil { | |||
| c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | |||
| return | |||
| } | |||
| str := common.UpdateConfig(updateValues) | |||
| if str != "" { | |||
| c.JSON(http.StatusInternalServerError, gin.H{"message": str}) | |||
| return | |||
| } | |||
| //更新配置文件变量configData | |||
| common.LoadConfig() | |||
| c.JSON(http.StatusOK, gin.H{"message": "Config updated successfully"}) | |||
| } | |||
| func HomeHandler(c *gin.Context) { | |||
| t := c.Query("type") | |||
| c.HTML(http.StatusOK, "login.html", gin.H{ | |||
| "type": t, | |||
| }) | |||
| } | |||
| func RegPageHandler(c *gin.Context) { | |||
| c.HTML(http.StatusOK, "reg.html", nil) | |||
| } | |||
| func InfoPageHandler(c *gin.Context) { | |||
| staffId := c.Query("staffId") | |||
| res, err := business.StaffInfo(staffId) | |||
| if err != nil { | |||
| c.HTML(http.StatusOK, "info.html", gin.H{ | |||
| "err": err.Error(), | |||
| }) | |||
| } | |||
| fmt.Printf("res:%v", res) | |||
| c.HTML(http.StatusOK, "info.html", gin.H{ | |||
| "info": res, | |||
| }) | |||
| } | |||
| func PhoneLoginHandler(c *gin.Context) { | |||
| phone := c.PostForm("phone") | |||
| res, err := business.PhoneLogin(phone) | |||
| if err != nil { | |||
| c.JSON(http.StatusInternalServerError, err.Error()) | |||
| } | |||
| c.JSON(http.StatusOK, res) | |||
| } | |||
| func MgrLoginHandler(c *gin.Context) { | |||
| phone := c.PostForm("userName") | |||
| pwd := c.PostForm("password") | |||
| str := business.MgrLogin(phone, pwd) | |||
| c.JSON(http.StatusOK, str) | |||
| } | |||
| func SendCodeHandler(c *gin.Context) { | |||
| phone := c.PostForm("phone") | |||
| str := business.SendCode(phone) | |||
| c.JSON(http.StatusOK, str) | |||
| } | |||
| func ValidCodeHandler(c *gin.Context) { | |||
| phone := c.PostForm("phone") | |||
| code := c.PostForm("code") | |||
| str, err := business.ValidCode(phone, code) | |||
| if err != nil { | |||
| c.JSON(http.StatusInternalServerError, err.Error()) | |||
| } | |||
| c.JSON(http.StatusOK, str) | |||
| } | |||
| // RegisterPageHandler 处理注册页面请求 | |||
| func RegisterPageHandler(c *gin.Context) { | |||
| c.HTML(http.StatusOK, "register.html", map[string]interface{}{"Message": ""}) | |||
| } | |||
| // MgrPageHandler 处理管理页面请求 | |||
| func MgrPageHandler(c *gin.Context) { | |||
| key := c.Query("key") | |||
| users, err := mydatabase.Select(key) | |||
| if err != nil { | |||
| log.Fatal(err) | |||
| } | |||
| if err != nil { | |||
| common.Error("Error marshaling JSON:%s", err) | |||
| return | |||
| } | |||
| c.HTML(http.StatusOK, "mgr.html", gin.H{ | |||
| "users": users, | |||
| "key": key, | |||
| }) | |||
| } | |||
| func TestHandler(c *gin.Context) { | |||
| fmt.Printf("timer: ====%d\n", common.ConfigData.Timer) | |||
| c.JSON(http.StatusOK, "request successfully") | |||
| } | |||
| // RegisterHandler 处理注册页面请求 | |||
| func RegisterHandler(c *gin.Context) { | |||
| // 从表单中获取用户输入 | |||
| username := c.PostForm("staffName") | |||
| phone := c.PostForm("staffPhone") | |||
| avatar := c.PostForm("avatar") | |||
| emp, _ := mydatabase.SelectOneByPhone(phone) | |||
| fmt.Println("====phone:" + phone) | |||
| fmt.Println(emp) | |||
| if emp.ID > 0 { | |||
| c.JSON(http.StatusInternalServerError, map[string]interface{}{"Message": "已存在该手机号对应的员工,请查验后重试!"}) | |||
| return | |||
| } | |||
| // 在实际应用中,你可能会将用户信息保存到数据库中 | |||
| avatar = strings.ReplaceAll(avatar, "data:image/jpeg;base64,", "") | |||
| features, imgName, err := business.LocalFetchStaffFaceFeature(avatar) | |||
| if err != nil { | |||
| c.JSON(http.StatusInternalServerError, map[string]interface{}{"Message": err.Error()}) | |||
| return | |||
| } | |||
| mydatabase.Insert(username, 0, phone, imgName, features, 1) | |||
| // 将注册成功的消息渲染到页面 | |||
| message := fmt.Sprintf("Registration successful! %s %s %s %s!", username, phone, imgName, features) | |||
| fmt.Println(message) | |||
| c.JSON(http.StatusOK, map[string]interface{}{"Message": "注册成功,请等待审核!"}) | |||
| } | |||
| // PassHandler 审核通过或拒绝的处理函数 | |||
| func PassHandler(c *gin.Context) { | |||
| // 从表单中获取用户输入 | |||
| id := c.PostForm("id") | |||
| handleType := c.PostForm("handle") | |||
| handleTypeInt, _ := strconv.Atoi(handleType) | |||
| if handleTypeInt != 1 && handleTypeInt != 2 && handleTypeInt != 4 { | |||
| c.JSON(http.StatusInternalServerError, map[string]interface{}{"Message": "handleType参数错误!"}) | |||
| return | |||
| } | |||
| idInt, _ := strconv.Atoi(id) | |||
| emp, err := mydatabase.SelectOne(idInt) | |||
| if err != nil || !(emp.ID > 0) { | |||
| c.JSON(http.StatusInternalServerError, map[string]interface{}{"Message": "指定id未查询到记录"}) | |||
| return | |||
| } | |||
| if handleTypeInt == 1 { | |||
| emp.Avatar = strings.ReplaceAll(emp.Avatar, "data:image/jpeg;base64,", "") | |||
| //将员工信息提交到后台 | |||
| resp, err := business.RegStaff(business.EmpToReq(emp)) | |||
| if err != nil { | |||
| c.JSON(http.StatusOK, map[string]interface{}{"Message": err.Error()}) | |||
| return | |||
| } | |||
| if resp != nil && resp.Code != 0 { | |||
| c.JSON(http.StatusOK, map[string]interface{}{"Message": resp.Msg}) | |||
| return | |||
| } | |||
| //更新sqllite里面数据状态 | |||
| mydatabase.Update(emp.ID, emp.Name, emp.Phone, emp.Avatar, 1, 1) | |||
| c.JSON(http.StatusOK, map[string]interface{}{"Message": "操作成功,注册流程结束!"}) | |||
| } else if handleTypeInt == 2 { | |||
| //更新sqllite里面数据状态 | |||
| mydatabase.SoftDelete(emp.ID) | |||
| c.JSON(http.StatusOK, map[string]interface{}{"Message": "操作成功"}) | |||
| } else if handleTypeInt == 4 { | |||
| //删除数据 | |||
| mydatabase.Delete(emp.ID) | |||
| //删除指定路径图片 | |||
| if emp.Avatar != "" { | |||
| os.Remove(emp.Avatar) | |||
| } | |||
| c.JSON(http.StatusOK, map[string]interface{}{"Message": "操作成功"}) | |||
| } | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| package mydatabase | |||
| import ( | |||
| "database/sql" | |||
| "fmt" | |||
| _ "github.com/mattn/go-sqlite3" | |||
| "sync" | |||
| ) | |||
| var ( | |||
| db2 *Database | |||
| mu2 sync.Mutex | |||
| ) | |||
| // Database 结构体封装了数据库连接和相关的操作方法 | |||
| type Database struct { | |||
| db *sql.DB | |||
| } | |||
| // NewDatabase 创建一个新的 Database 实例 | |||
| func NewDatabase(dbPath string) *Database { | |||
| db, err := sql.Open("sqlite3", dbPath) | |||
| if err != nil { | |||
| fmt.Printf("Failed to connect to database: %v", err) | |||
| return nil | |||
| } | |||
| return &Database{db} | |||
| } | |||
| // Close 关闭数据库连接 | |||
| func (d *Database) Close() error { | |||
| return d.db.Close() | |||
| } | |||
| // GetDb 返回单例缓存对象 | |||
| func GetDb() *Database { | |||
| if db2 == nil { | |||
| mu2.Lock() | |||
| defer mu2.Unlock() | |||
| if db2 == nil { | |||
| cache := NewDatabase("./staffs.db") | |||
| cache.Create() | |||
| db2 = cache | |||
| } | |||
| } | |||
| return db2 | |||
| } | |||
| @@ -0,0 +1,147 @@ | |||
| package mydatabase | |||
| import ( | |||
| "fmt" | |||
| "log" | |||
| "time" | |||
| ) | |||
| type Emp struct { | |||
| ID int | |||
| Eno int // 员工的ID | |||
| Name string | |||
| Phone string | |||
| Avatar string | |||
| Features string | |||
| Level int | |||
| Status int | |||
| UpdateTime string | |||
| CreateTime string | |||
| } | |||
| func (d *Database) Create() error { | |||
| createTableStmt := ` | |||
| CREATE TABLE IF NOT EXISTS staff ( | |||
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |||
| eno INTEGER, | |||
| name TEXT NOT NULL, | |||
| phone TEXT, | |||
| avatar TEXT, | |||
| level INTEGER, | |||
| status INTEGER DEFAULT 0, | |||
| features TEXT, | |||
| create_time DATETIME, | |||
| update_time DATETIME DEFAULT CURRENT_TIMESTAMP | |||
| ); | |||
| ` | |||
| _, err := d.db.Exec(createTableStmt) | |||
| if err != nil { | |||
| log.Fatal(err) | |||
| } | |||
| return err | |||
| } | |||
| func Insert(name string, eno int, phone string, avatar string, features string, level int) error { | |||
| insertStmt := "INSERT INTO staff (name,eno, phone, avatar,features, level, create_time) VALUES (?, ?, ?, ?,?, ?, ?)" | |||
| eventTime := time.Now() | |||
| // 将时间格式化为 SQLite 支持的 DATETIME 格式("2006-01-02 15:04:05") | |||
| formattedTime := eventTime.Format("2006-01-02 15:04:05") | |||
| _, err := GetDb().db.Exec(insertStmt, name, eno, phone, avatar, features, level, formattedTime) | |||
| if err != nil { | |||
| log.Fatal(err) | |||
| } | |||
| return err | |||
| } | |||
| func Update(id int, name string, phone string, avatar string, level int, status int) error { | |||
| updateStmt := "UPDATE staff SET phone = ?, name = ?, avatar = ?, level=?, status=? WHERE id = ?" | |||
| _, err := GetDb().db.Exec(updateStmt, phone, name, avatar, level, status, id) | |||
| if err != nil { | |||
| log.Fatal(err) | |||
| } | |||
| return err | |||
| } | |||
| func Delete(id int) { | |||
| deleteStmt := "DELETE FROM staff WHERE id = ?" | |||
| _, err := GetDb().db.Exec(deleteStmt, id) | |||
| if err != nil { | |||
| log.Fatal(err) | |||
| } | |||
| } | |||
| func SoftDelete(id int) error { | |||
| updateStmt := "UPDATE staff SET status=2 WHERE id = ?" | |||
| _, err := GetDb().db.Exec(updateStmt, id) | |||
| if err != nil { | |||
| log.Fatal(err) | |||
| } | |||
| return err | |||
| } | |||
| func Select(key string) ([]Emp, error) { | |||
| str := "SELECT id, eno, name, phone, avatar,features, level, status, create_time, update_time FROM staff" | |||
| if key != "" { | |||
| str += " WHERE name LIKE '%" + key + "%' or phone LIKE '%" + key + "%'" | |||
| } | |||
| rows, err := GetDb().db.Query(str + " order by status, update_time desc") | |||
| if err != nil { | |||
| log.Fatal(err) | |||
| } | |||
| defer rows.Close() | |||
| var users []Emp | |||
| for rows.Next() { | |||
| var user Emp | |||
| err := rows.Scan(&user.ID, &user.Eno, &user.Name, &user.Phone, &user.Avatar, &user.Features, &user.Level, &user.Status, &user.CreateTime, &user.UpdateTime) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| users = append(users, user) | |||
| } | |||
| return users, nil | |||
| } | |||
| func SelectOne(id int) (Emp, error) { | |||
| rows, err := GetDb().db.Query("SELECT id, eno, name, phone, avatar,features, level, status, create_time, update_time FROM staff where id = ? LIMIT 1", id) | |||
| if err != nil { | |||
| log.Fatal(err) | |||
| } | |||
| defer rows.Close() | |||
| var user Emp | |||
| if rows.Next() { | |||
| err := rows.Scan(&user.ID, &user.Eno, &user.Name, &user.Phone, &user.Avatar, &user.Features, &user.Level, &user.Status, &user.CreateTime, &user.UpdateTime) | |||
| if err != nil { | |||
| return user, err | |||
| } | |||
| } | |||
| if err := rows.Err(); err != nil { | |||
| return user, err | |||
| } | |||
| return user, nil | |||
| } | |||
| func SelectOneByPhone(phone string) (Emp, error) { | |||
| fmt.Printf("SelectOneByPhone phone:%s\n", phone) | |||
| rows, err := GetDb().db.Query("SELECT id, eno, name, phone, avatar,features, level, status, create_time, update_time FROM staff where phone = ? and status<>2 LIMIT 1", phone) | |||
| if err != nil { | |||
| log.Fatal(err) | |||
| } | |||
| defer rows.Close() | |||
| var user Emp | |||
| if rows.Next() { | |||
| err := rows.Scan(&user.ID, &user.Eno, &user.Name, &user.Phone, &user.Avatar, &user.Features, &user.Level, &user.Status, &user.CreateTime, &user.UpdateTime) | |||
| if err != nil { | |||
| return user, err | |||
| } | |||
| } | |||
| if err := rows.Err(); err != nil { | |||
| return user, err | |||
| } | |||
| return user, nil | |||
| } | |||
| @@ -0,0 +1,159 @@ | |||
| //生成Html | |||
| var GenerateHtml = function(type, title, msg) { | |||
| var _html = ""; | |||
| _html += '<div id="mb_box"></div><div id="mb_con"><span id="mb_tit">' + title + '</span>'; | |||
| _html += '<a id="mb_ico">x</a><div id="mb_msg" style="text-align:center;">' + msg + '</div><div id="mb_btnbox">'; | |||
| if (type == "alert") { | |||
| _html += '<input id="mb_btn_ok" type="button" value="确定" />'; | |||
| } | |||
| if (type == "confirm") { | |||
| _html += '<input id="mb_btn_ok" type="button" value="确定" />'; | |||
| _html += '<input id="mb_btn_no" type="button" value="取消" />'; | |||
| } | |||
| _html += '</div></div>'; | |||
| //必须先将_html添加到body,再设置Css样式 | |||
| $("body").append(_html); | |||
| //生成Css | |||
| GenerateCss(); | |||
| } | |||
| //生成Css | |||
| var GenerateCss = function() { | |||
| var _widht = document.documentElement.clientWidth; //屏幕宽 | |||
| var _height = document.documentElement.clientHeight; //屏幕高 | |||
| var isPhonePage = 0; | |||
| if (_widht<_height) { | |||
| isPhonePage = 1; | |||
| } | |||
| $("#mb_box").css({ | |||
| width: '100%', | |||
| height: '100%', | |||
| zIndex: '99999', | |||
| position: 'fixed', | |||
| filter: 'Alpha(opacity=60)', | |||
| backgroundColor: 'black', | |||
| top: '0', | |||
| left: '0', | |||
| opacity: '0.6' | |||
| }); | |||
| $("#mb_con").css({ | |||
| zIndex: '999999', | |||
| width: isPhonePage?'80%':'400px', | |||
| position: 'fixed', | |||
| backgroundColor: 'White', | |||
| borderRadius: '15px' | |||
| }); | |||
| $("#mb_tit").css({ | |||
| display: 'block', | |||
| fontSize: '14px', | |||
| color: '#444', | |||
| padding: '10px 15px', | |||
| backgroundColor: '#DDD', | |||
| borderRadius: '15px 15px 0 0', | |||
| borderBottom: '3px solid #009BFE', | |||
| fontWeight: 'bold' | |||
| }); | |||
| $("#mb_msg").css({ | |||
| padding: '20px', | |||
| lineHeight: '20px', | |||
| borderBottom: '1px dashed #DDD', | |||
| fontSize: '13px' | |||
| }); | |||
| $("#mb_ico").css({ | |||
| display: 'block', | |||
| position: 'absolute', | |||
| right: '10px', | |||
| top: '9px', | |||
| border: '1px solid Gray', | |||
| width: '18px', | |||
| height: '18px', | |||
| textAlign: 'center', | |||
| lineHeight: '16px', | |||
| cursor: 'pointer', | |||
| borderRadius: '12px', | |||
| fontFamily: '微软雅黑' | |||
| }); | |||
| $("#mb_btnbox").css({ | |||
| margin: '15px 0 10px 0', | |||
| textAlign: 'center' | |||
| }); | |||
| $("#mb_btn_ok,#mb_btn_no").css({ | |||
| width: '85px', | |||
| height: '30px', | |||
| color: 'white', | |||
| border: 'none' | |||
| }); | |||
| $("#mb_btn_ok").css({ | |||
| backgroundColor: '#168bbb' | |||
| }); | |||
| $("#mb_btn_no").css({ | |||
| backgroundColor: 'gray', | |||
| marginLeft: '20px' | |||
| }); | |||
| //右上角关闭按钮hover样式 | |||
| $("#mb_ico").hover(function() { | |||
| $(this).css({ | |||
| backgroundColor: 'Red', | |||
| color: 'White' | |||
| }); | |||
| }, function() { | |||
| $(this).css({ | |||
| backgroundColor: '#DDD', | |||
| color: 'black' | |||
| }); | |||
| }); | |||
| console.log($(document.body).width()) | |||
| console.log(_widht) | |||
| var boxWidth = $("#mb_con").width(); | |||
| var boxHeight = $("#mb_con").height(); | |||
| //让提示框居中 | |||
| $("#mb_con").css({ | |||
| top: (_height - boxHeight) / 2 + "px", | |||
| left: (_widht - boxWidth) / 2 + "px" | |||
| }); | |||
| } | |||
| //确定按钮事件 | |||
| var btnOk = function(callback) { | |||
| $("#mb_btn_ok").click(function() { | |||
| $("#mb_box,#mb_con").remove(); | |||
| if (typeof(callback) == 'function') { | |||
| callback(); | |||
| } | |||
| }); | |||
| } | |||
| //取消按钮事件 | |||
| var btnNo = function() { | |||
| $("#mb_btn_no,#mb_ico").click(function() { | |||
| $("#mb_box,#mb_con").remove(); | |||
| }); | |||
| } | |||
| var btnDel = function(callback) { | |||
| $("#mb_ico").click(function() { | |||
| $("#mb_box,#mb_con").remove(); | |||
| if (typeof(callback) == 'function') { | |||
| callback(); | |||
| } | |||
| }); | |||
| } | |||
| $.MsgBox = { | |||
| Alert: function(title, msg) { | |||
| GenerateHtml("alert", title, msg); | |||
| btnOk(); //alert只是弹出消息,因此没必要用到回调函数callback | |||
| btnNo(); | |||
| }, | |||
| AlertWithCallback: function(title, msg, callback) { | |||
| GenerateHtml("alert", title, msg); | |||
| btnOk(callback); //alert只是弹出消息,因此没必要用到回调函数callback | |||
| btnNo(); | |||
| btnDel(callback) | |||
| }, | |||
| Confirm: function(title, msg, callback) { | |||
| GenerateHtml("confirm", title, msg); | |||
| btnOk(callback); | |||
| btnNo(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| body { | |||
| background-color: #f0f0f0; | |||
| font-family: Arial, sans-serif; | |||
| } | |||
| h1 { | |||
| color: #333; | |||
| } | |||
| @@ -0,0 +1,380 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
| <title>信息页面</title> | |||
| <style> | |||
| body{ | |||
| margin: 0; | |||
| } | |||
| li { | |||
| list-style-type: none; | |||
| } | |||
| @media screen and (orientation: portrait) { | |||
| .login { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/phoneBgc.jpg"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| } | |||
| .register { | |||
| background-image: url("/static/img/registerBgc.jpg") !important; | |||
| padding-top: 15vh; | |||
| } | |||
| .register_loginTitle { | |||
| width: 100vw; | |||
| font-size: 8vw; | |||
| text-align: center; | |||
| } | |||
| .register_inputBox { | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-top: 5vh; | |||
| position: relative; | |||
| } | |||
| .register_phoneInput { | |||
| width: 50vw; | |||
| height: 5vh; | |||
| border-radius: 2vw; | |||
| background-color: transparent; | |||
| font-size: 3vw; | |||
| padding-left: 2vw; | |||
| border: 1px solid #000; | |||
| margin-bottom: 2vh; | |||
| } | |||
| .register_nameInput { | |||
| width: 50vw; | |||
| height: 5vh; | |||
| border-radius: 2vw; | |||
| background-color: transparent; | |||
| font-size: 3vw; | |||
| padding-left: 2vw; | |||
| border: 1px solid #000; | |||
| margin-bottom: 2vh; | |||
| } | |||
| .reg_button { | |||
| width: 40vw; | |||
| height: 5vh; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color:#fff; | |||
| font-size: 4vw; | |||
| margin: 2vh 0 0 30vw; | |||
| } | |||
| .register_loginButton { | |||
| width: 40vw; | |||
| height: 5vh; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color:#fff; | |||
| font-size: 4vw; | |||
| margin: 2vh 0 0 30vw; | |||
| } | |||
| .uploadImg{ | |||
| width: 46vw !important; | |||
| transform: translate(0%, 0%) !important; | |||
| left: 27vw !important; | |||
| position: relative; | |||
| } | |||
| } | |||
| @media screen and (orientation: landscape) { | |||
| input:focus { | |||
| border: 0.1vw solid #000; | |||
| outline: none; /* 可选,用于去除默认的外边框样式 */ | |||
| } | |||
| .login { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/phoneBgc_pc.jpg"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| position: relative; | |||
| } | |||
| .register { | |||
| background-image: url("/static/img/registerBgc_pc.jpg") !important; | |||
| } | |||
| .register_loginTitle { | |||
| width: 100vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| font-size: 4vh; | |||
| text-align: center; | |||
| position: absolute; | |||
| z-index: 10; | |||
| top: 10vh; | |||
| } | |||
| .uploadImg { | |||
| width: 15vw; | |||
| } | |||
| .register_inputBox { | |||
| width: 100vw; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| position: relative; | |||
| padding-top: 42vh; | |||
| } | |||
| .register_phoneInput { | |||
| width: 37vh; | |||
| height: 6vh; | |||
| border-radius: 2vh; | |||
| background-color: transparent; | |||
| font-size: 3vh; | |||
| border: 0.1vw solid #000; | |||
| margin-bottom: 5vh; | |||
| padding-left:1vw ; | |||
| } | |||
| .register_nameInput { | |||
| width: 37vh; | |||
| height: 6vh; | |||
| border-radius: 2vh; | |||
| background-color: transparent; | |||
| font-size: 3vh; | |||
| border: 0.1vw solid #000; | |||
| padding-left: 1vw ; | |||
| margin-bottom: 2vh; | |||
| } | |||
| .reg_button { | |||
| width: 16vw; | |||
| height: 3vw; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vh; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color: #fff; | |||
| font-size: 2vw; | |||
| /* margin: 2vh 0 0 30vh; */ | |||
| position: absolute; | |||
| left: 55vw; | |||
| top: 65vh; | |||
| } | |||
| .register_loginButton { | |||
| width: 8vw; | |||
| height: 2vw; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 1vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color: #fff; | |||
| font-size: 3vh; | |||
| position: absolute; | |||
| left: 46vw; | |||
| top: 65vh; | |||
| } | |||
| .hand:hover{ | |||
| cursor: pointer; | |||
| } | |||
| .uploadImg{ | |||
| width: 200px !important; | |||
| transform: translate(0%, 0%) !important; | |||
| left: 2vw !important; | |||
| position: relative; | |||
| } | |||
| } | |||
| .upload-img-box { | |||
| position: relative; | |||
| } | |||
| .upload-img-box .input-box { | |||
| display: inline-block; | |||
| width: 144px; | |||
| height: 144px; | |||
| /*border: solid black 1px;*/ | |||
| } | |||
| .upload-img-box .befor-img { | |||
| display: flex; | |||
| /*width: 300px;*/ | |||
| /*height: 90px;*/ | |||
| /*border: solid black 1px;*/ | |||
| position: absolute; | |||
| top: 0px; | |||
| left: 0px; | |||
| /*background: #ffffff;*/ | |||
| z-index: 30; | |||
| align-items: center; | |||
| justify-content: center; | |||
| } | |||
| .upload-img-box .befor-img.hasImg { | |||
| display: none; | |||
| } | |||
| .upload-img-box .after-img { | |||
| display: inline-block; | |||
| width: 144px; | |||
| height: 144px; | |||
| border: none; | |||
| /*position: absolute;*/ | |||
| top: 0px; | |||
| left: 0px; | |||
| /*background: #ffffff;*/ | |||
| z-index: 20; | |||
| } | |||
| .after-img img, .uploadImg { | |||
| /*position: absolute;*/ | |||
| width: 100%; | |||
| height: 100%; | |||
| object-fit: cover; /*根据父容器的尺寸裁剪和缩放图片,以填充整个容器,并保持长宽比例不变。*/ | |||
| object-position: center; /*指定对象的位置,如果不设置,则默认是center*/ | |||
| background-size: cover; /*背景图片的大小,如果不设置,则默认是auto*/ | |||
| background-position: center; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, 0%);/*将元素移动到中心*/ | |||
| border-radius: 50%; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <!-- 注册页面 --> | |||
| <div class="register login"> | |||
| <div class="register_loginTitle "> | |||
| <div class="upload-img-box"> | |||
| <div class="input-box"> | |||
| <input type="file" id="img-file" hidden="hidden" name="选择图片"> | |||
| </div> | |||
| <div class="befor-img"> | |||
| <img class="uploadImg hand" src="/static/img/upload.png" alt=""> | |||
| </div> | |||
| <div class="after-img"> | |||
| <img id="img-box" src="" width="30%" height="auto"> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="register_inputBox"> | |||
| {{ if .info }} | |||
| <input class="register_nameInput" name="staffName" type="text" placeholder="员工姓名" value="{{.info.userName}}"> | |||
| <input class="register_phoneInput" name="staffPhone" type="text" placeholder="员工手机号" value="{{.info.phone}}"> | |||
| {{else}} | |||
| <input class="register_nameInput" name="staffName" type="text" placeholder="员工姓名"> | |||
| <input class="register_phoneInput" name="staffPhone" type="text" placeholder="员工手机号"> | |||
| {{ end }} | |||
| </div> | |||
| <div class="register_loginButton hand">修改信息</div> | |||
| </div> | |||
| <script src="/static/js/jquery.min.js"></script> | |||
| <script src="/static/js/msg-box.js"></script> | |||
| <script> | |||
| $(function(){ | |||
| if ("{{.err}}"){ | |||
| $.MsgBox.Alert("错误", "{{.err}}"); | |||
| return; | |||
| } | |||
| $.MsgBox = { | |||
| Alert: function(title, msg) { | |||
| GenerateHtml("alert", title, msg); | |||
| btnOk(); //alert只是弹出消息,因此没必要用到回调函数callback | |||
| btnNo(); | |||
| }, | |||
| Confirm: function(title, msg, callback) { | |||
| GenerateHtml("confirm", title, msg); | |||
| btnOk(callback); | |||
| btnNo(); | |||
| } | |||
| } | |||
| $(".register_loginButton").on("click",function() { | |||
| var staffPhone = $(".register_phoneInput").val(); | |||
| var staffName = $(".register_nameInput").val(); | |||
| const regex = /^1[3-9]\d{9}$/; | |||
| if (staffPhone == "" || !regex.test(staffPhone)){ | |||
| $.MsgBox.Alert("提示", "手机号为空或不合法,请检查"); | |||
| return; | |||
| } | |||
| if (staffName == ""){ | |||
| $.MsgBox.Alert("提示", "员工姓名为空,请检查"); | |||
| return; | |||
| } | |||
| var img = $("#img-box").attr("src"); | |||
| console.log(img); | |||
| $.ajax({ | |||
| url: "/register", | |||
| type: "post", | |||
| data: { | |||
| staffPhone: staffPhone, | |||
| staffName: staffName, | |||
| avatar: img | |||
| }, | |||
| success: function(data) { | |||
| $.MsgBox.Alert("提示", data.Message); | |||
| }, | |||
| error: function(error) { | |||
| console.log(error); | |||
| $.MsgBox.Alert("提示", error.responseJSON.Message); | |||
| } | |||
| }) | |||
| }) | |||
| $(document).on("click", ".upload-img-box .befor-img,.upload-img-box .after-img", function (e) { | |||
| $(".input-box input").trigger("click"); | |||
| }) | |||
| // 使用input 的change事件,监听当文件上传的值进行改变的时候,才触发事件。 | |||
| $(document).on("change", ".upload-img-box #img-file", function () { | |||
| var file = $("#img-file")[0].files[0]; | |||
| console.log(file); | |||
| // file 内容包括: lastModified , lastModifiedDate , name , size , type | |||
| var formdata = new FormData(); | |||
| formdata.append('file', file); | |||
| if (window.FileReader) { | |||
| var fr = new FileReader(); | |||
| fr.onloadend = function (e) { | |||
| // console.log(this.result); | |||
| // document.getElementById("portrait").src = e.target.result; | |||
| $("#img-box").attr("src",e.target.result) | |||
| $(".upload-img-box .befor-img").addClass("hasImg") | |||
| }; | |||
| //给FileReader对象一个读取完成的方法, | |||
| //使用readAsDataURL会返回一个url | |||
| //这个值就保存在事件对象的result里 | |||
| //img通过加载这个地址,完成图片的加载 | |||
| fr.readAsDataURL(file); | |||
| } | |||
| //判断图片大小 | |||
| if (file.size > 3 * 1024 * 1024) { | |||
| alert("上传图片不能大于3M"); | |||
| } | |||
| //判断图片数据类型 | |||
| if (!/.(gif|jpg|jpeg|png|GIF|JPG|bmp)$/.test(file.name)) { | |||
| alert("图片类型必须是.gif,jpeg,jpg,png,bmp中的一种"); | |||
| } | |||
| }) | |||
| }) | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,871 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
| <title>Document</title> | |||
| <style> | |||
| body{ | |||
| margin: 0; | |||
| } | |||
| li { | |||
| list-style-type: none; | |||
| } | |||
| @media screen and (orientation: portrait) { | |||
| .login { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/phoneBgc.jpg"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| } | |||
| .register { | |||
| background-image: url("/static/img/registerBgc.jpg") !important; | |||
| padding-top: 15vh; | |||
| } | |||
| .logo { | |||
| width: 15vw; | |||
| margin: 4vw 0 0 4vw; | |||
| } | |||
| .login_title { | |||
| width: 100vw; | |||
| font-size: 8vw; | |||
| text-align: center; | |||
| margin-top: 15vh; | |||
| } | |||
| .register_loginTitle { | |||
| width: 100vw; | |||
| font-size: 8vw; | |||
| text-align: center; | |||
| } | |||
| .input_box { | |||
| width: 100vw; | |||
| height: 25vw; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-top: 5vh; | |||
| position: relative; | |||
| } | |||
| .register_inputBox { | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-top: 5vh; | |||
| position: relative; | |||
| } | |||
| .phone_input{ | |||
| width: 50vw; | |||
| height: 11vw; | |||
| border-radius: 2vw; | |||
| background-color: transparent; | |||
| font-size: 3vw; | |||
| padding-left: 2vw; | |||
| border: 1px solid #000; | |||
| position: absolute; | |||
| top: 0vw; | |||
| } | |||
| .register_phoneInput { | |||
| width: 50vw; | |||
| height: 5vh; | |||
| border-radius: 2vw; | |||
| background-color: transparent; | |||
| font-size: 3vw; | |||
| padding-left: 2vw; | |||
| border: 1px solid #000; | |||
| margin-bottom: 2vh; | |||
| } | |||
| .code_input { | |||
| width: 50vw; | |||
| height: 11vw; | |||
| margin-top: 2vh; | |||
| border-radius: 2vw; | |||
| padding-left: 2vw; | |||
| font-size: 3vw; | |||
| background-color: transparent; | |||
| border: 1px solid #000; | |||
| position: absolute; | |||
| top: 10vw; | |||
| } | |||
| .register_codeInput { | |||
| width: 50vw; | |||
| height: 5vh; | |||
| border-radius: 2vw; | |||
| background-color: transparent; | |||
| font-size: 3vw; | |||
| padding-left: 2vw; | |||
| border: 1px solid #000; | |||
| } | |||
| .login_button { | |||
| width: 40vw; | |||
| height: 5vh; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color:#fff; | |||
| font-size: 4vw; | |||
| margin: 2vh 0 0 30vw; | |||
| } | |||
| .register_loginButton { | |||
| width: 40vw; | |||
| height: 5vh; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color:#fff; | |||
| font-size: 4vw; | |||
| margin: 2vh 0 0 30vw; | |||
| } | |||
| .send_code { | |||
| position: absolute; | |||
| font-size: 2.5vw; | |||
| top: 18.5vw; | |||
| right: 30vw; | |||
| border-left: 2px solid; | |||
| padding-left: 2vw; | |||
| } | |||
| .employee { | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| .administrator { | |||
| display: flex; | |||
| align-items: center; | |||
| margin-left: 5vw; | |||
| } | |||
| .choose_employee { | |||
| width: 2vw; | |||
| height: 2vw; | |||
| border-radius: 50%; | |||
| border: 2px solid #000; | |||
| margin-right: 1vw; | |||
| } | |||
| .choose_administrator { | |||
| width: 2vw; | |||
| height: 2vw; | |||
| border-radius: 50%; | |||
| border: 2px solid #000; | |||
| margin-right: 1vw; | |||
| } | |||
| .login_style { | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| font-size: 2.5vw; | |||
| margin-top: 2vh; | |||
| } | |||
| .hidden { | |||
| display: none; | |||
| } | |||
| .active { | |||
| display: flex; | |||
| } | |||
| .background { | |||
| background-color: #000; | |||
| } | |||
| .password-container { | |||
| position: relative; | |||
| } | |||
| .password_toggle { | |||
| position: absolute; | |||
| width: 5vw; | |||
| top: 18vw; | |||
| right: 26vw; | |||
| } | |||
| .approval_page { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/registerBgc.jpg"); | |||
| background-size: 100% 100%; | |||
| overflow: auto; | |||
| } | |||
| .approval_box { | |||
| max-width: 100vw; | |||
| min-height: 100vh; | |||
| background-color:rgba(237,237,237,0.8); | |||
| } | |||
| .search_people { | |||
| width: 80vw; | |||
| height: 5vh; | |||
| border-radius: 1vw; | |||
| margin: 5vh 0 0 10vw; | |||
| border: 0 solid #fff; | |||
| font-size: 3vw; | |||
| text-align: center; | |||
| } | |||
| .every_info { | |||
| height: 7vh; | |||
| margin-top: 3vh; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-around; | |||
| font-size: 3vw; | |||
| } | |||
| #scroll-to-top-btn { | |||
| display: none; | |||
| position: fixed; | |||
| right: 20px; | |||
| bottom: 20px; | |||
| width: 7vw; | |||
| height: 10vh; | |||
| color: #fff; | |||
| border: none; | |||
| border-radius: 5px; | |||
| font-size: 3vw; | |||
| cursor: pointer; | |||
| } | |||
| .people_img { | |||
| width: 6vh; | |||
| height: 6vh; | |||
| } | |||
| .pass_nopass { | |||
| display: flex; | |||
| } | |||
| .pass { | |||
| background-color: rgb(92, 186, 45); | |||
| width: 10vw; | |||
| height: 5vw; | |||
| border-radius: 1vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| } | |||
| .no_pass { | |||
| background-color: rgb(187, 44, 45); | |||
| width: 10vw; | |||
| height: 5vw; | |||
| border-radius: 1vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-left: 2vw; | |||
| } | |||
| .goTop_img { | |||
| width: 6vw; | |||
| height: 6vw; | |||
| } | |||
| } | |||
| @media screen and (orientation: landscape) { | |||
| input:focus { | |||
| border: 0.1vw solid #000; | |||
| outline: none; /* 可选,用于去除默认的外边框样式 */ | |||
| } | |||
| .login { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/phoneBgc_pc.jpg"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| position: relative; | |||
| } | |||
| .PC_img { | |||
| width: 52.6vw; | |||
| height: 64.44vh; | |||
| background-image: url("/static/img/loginBox.png"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| position: absolute; | |||
| top: 18.88vh; | |||
| left: 23.7vw; | |||
| } | |||
| .register { | |||
| background-image: url("/static/img/registerBgc_pc.jpg") !important; | |||
| } | |||
| .logo { | |||
| width: 15vh; | |||
| margin: 4vh 0 0 4vh; | |||
| } | |||
| .login_title { | |||
| /* width: 100vw; */ | |||
| font-size: 4vh; | |||
| text-align: center; | |||
| position: absolute; | |||
| z-index: 10; | |||
| top: 28vh; | |||
| margin-left: 58vw; | |||
| /* margin-top: 15vh;*/ | |||
| } | |||
| .register_loginTitle { | |||
| width: 100vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| font-size: 4vh; | |||
| text-align: center; | |||
| position: absolute; | |||
| z-index: 10; | |||
| top: 10vh; | |||
| } | |||
| .uploadImg { | |||
| width: 15vw; | |||
| } | |||
| .input_box { | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-top: 15vh; | |||
| position: relative; | |||
| } | |||
| .register_inputBox { | |||
| width: 100vw; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| position: relative; | |||
| padding-top: 42vh; | |||
| } | |||
| .phone_input{ | |||
| width: 15vw; | |||
| height: 6.66vh; | |||
| border-radius: 2vh; | |||
| background-color: transparent; | |||
| font-size: 2vw; | |||
| padding-left: 2vh; | |||
| padding-right: 5vw; | |||
| border: 0.1vw solid #000; | |||
| margin-left: 25vw; | |||
| } | |||
| .register_phoneInput { | |||
| width: 37vh; | |||
| height: 6vh; | |||
| border-radius: 2vh; | |||
| background-color: transparent; | |||
| font-size: 3vh; | |||
| border: 0.1vw solid #000; | |||
| margin-bottom: 5vh; | |||
| padding-left:1vw ; | |||
| } | |||
| .register_codeInput { | |||
| width: 37vh; | |||
| height: 6vh; | |||
| border-radius: 2vh; | |||
| background-color: transparent; | |||
| font-size: 3vh; | |||
| border: 0.1vw solid #000; | |||
| padding-left: 1vw ; | |||
| } | |||
| .code_input { | |||
| width: 15vw; | |||
| height: 6.66vh; | |||
| margin-top: 2vw; | |||
| border-radius: 2vh; | |||
| padding-left: 2vh; | |||
| font-size: 1.5vw; | |||
| padding-right: 5vw; | |||
| background-color: transparent; | |||
| border: 0.1vw solid #000; | |||
| margin-left: 25vw; | |||
| } | |||
| .login_button { | |||
| width: 16vw; | |||
| height: 3vw; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vh; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color: #fff; | |||
| font-size: 2vw; | |||
| /* margin: 2vh 0 0 30vh; */ | |||
| position: absolute; | |||
| left: 55vw; | |||
| top: 65vh; | |||
| } | |||
| .register_loginButton { | |||
| width: 30vh; | |||
| height: 3vw; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 1vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color: #fff; | |||
| font-size: 3vh; | |||
| position: absolute; | |||
| left: 42.5vw; | |||
| top: 65vh; | |||
| } | |||
| .send_code { | |||
| position: absolute; | |||
| font-size: 1.5vh; | |||
| top: 11.5vh; | |||
| right: 27.3vw; | |||
| border-left: 0.1vw solid; | |||
| padding-left: 1vh; | |||
| display: flex; | |||
| align-items: center; | |||
| height: 3vw; | |||
| } | |||
| .employee { | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| .administrator { | |||
| display: flex; | |||
| align-items: center; | |||
| margin-left: 3vw; | |||
| } | |||
| .hand:hover{ | |||
| cursor: pointer; | |||
| } | |||
| .choose_employee { | |||
| width: 2vh; | |||
| height: 2vh; | |||
| border-radius: 50%; | |||
| border: 0.1vw solid #000; | |||
| margin-right: 1vh; | |||
| } | |||
| .choose_administrator { | |||
| width: 2vh; | |||
| height: 2vh; | |||
| border-radius: 50%; | |||
| border: 0.1vw solid #000; | |||
| margin-right: 1vh; | |||
| } | |||
| .login_style { | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| font-size: 1vw; | |||
| margin-top: 2vw; | |||
| position: absolute; | |||
| left: 54.8vw; | |||
| } | |||
| .hidden { | |||
| display: none; | |||
| } | |||
| .active { | |||
| display: flex; | |||
| } | |||
| .background { | |||
| background-color: #000; | |||
| } | |||
| .password-container { | |||
| position: relative; | |||
| } | |||
| .password_toggle { | |||
| width: 3vw; | |||
| position: absolute; | |||
| font-size: 1.5vh; | |||
| top: 11.5vh; | |||
| right: 28.5vw; | |||
| padding-left: 0.5vh; | |||
| } | |||
| .approval_page { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/registerBgc.jpg"); | |||
| background-size: 100% 100%; | |||
| overflow: auto; | |||
| } | |||
| .approval_box { | |||
| max-width: 100vw; | |||
| min-height: 100vh; | |||
| background-color:rgba(237,237,237,0.8); | |||
| } | |||
| .search_people { | |||
| width: 60vw; | |||
| height: 6vh; | |||
| border-radius: 1vh; | |||
| margin: 5vh 0 0 20vw; | |||
| border: 0 solid #fff; | |||
| font-size: 3vh; | |||
| text-align: center; | |||
| } | |||
| .every_info { | |||
| height: 7vw; | |||
| margin-top: 3vw; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-around; | |||
| font-size: 3vh; | |||
| } | |||
| #scroll-to-top-btn { | |||
| display: none; | |||
| position: fixed; | |||
| right: 20px; | |||
| bottom: 20px; | |||
| width: 7vh; | |||
| height: 10vw; | |||
| color: #fff; | |||
| border: none; | |||
| border-radius: 5px; | |||
| font-size: 3vh; | |||
| cursor: pointer; | |||
| } | |||
| .people_img { | |||
| width: 6vw; | |||
| height: 6vw; | |||
| } | |||
| .pass_nopass { | |||
| display: flex; | |||
| } | |||
| .pass { | |||
| background-color: rgb(92, 186, 45); | |||
| width: 10vh; | |||
| height: 5vh; | |||
| border-radius: 1vh; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| } | |||
| .no_pass { | |||
| background-color: rgb(187, 44, 45); | |||
| width: 10vh; | |||
| height: 5vh; | |||
| border-radius: 1vh; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-left: 2vh; | |||
| } | |||
| .goTop_img { | |||
| width: 6vh; | |||
| height: 6vh; | |||
| } | |||
| } | |||
| .list_box{ | |||
| padding-inline-start: 0px; | |||
| } | |||
| .uploadImg{ | |||
| width: 48% !important; | |||
| transform: translate(0%, 0%) !important; | |||
| } | |||
| .upload-img-box { | |||
| position: relative; | |||
| } | |||
| .upload-img-box .input-box { | |||
| display: inline-block; | |||
| width: 144px; | |||
| height: 144px; | |||
| /*border: solid black 1px;*/ | |||
| } | |||
| .upload-img-box .befor-img { | |||
| display: flex; | |||
| /*width: 300px;*/ | |||
| /*height: 90px;*/ | |||
| /*border: solid black 1px;*/ | |||
| position: absolute; | |||
| top: 0px; | |||
| left: 0px; | |||
| /*background: #ffffff;*/ | |||
| z-index: 30; | |||
| align-items: center; | |||
| justify-content: center; | |||
| } | |||
| .upload-img-box .befor-img.hasImg { | |||
| display: none; | |||
| } | |||
| .upload-img-box .after-img { | |||
| display: inline-block; | |||
| width: 144px; | |||
| height: 144px; | |||
| border: none; | |||
| /*position: absolute;*/ | |||
| top: 0px; | |||
| left: 0px; | |||
| /*background: #ffffff;*/ | |||
| z-index: 20; | |||
| } | |||
| .after-img img, .uploadImg { | |||
| /*position: absolute;*/ | |||
| width: 100%; | |||
| height: 100%; | |||
| object-fit: cover; /*根据父容器的尺寸裁剪和缩放图片,以填充整个容器,并保持长宽比例不变。*/ | |||
| object-position: center; /*指定对象的位置,如果不设置,则默认是center*/ | |||
| background-size: cover; /*背景图片的大小,如果不设置,则默认是auto*/ | |||
| background-position: center; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, 0%);/*将元素移动到中心*/ | |||
| border-radius: 50%; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <!-- 登录页面 --> | |||
| <div class="login"> | |||
| <img class="logo" src="/static/img/logo.png" alt=""> | |||
| <div class="PC_img"></div> | |||
| <div class="login_title">请 登 录</div> | |||
| <div id="employeeForm" class="input_box hidden active"> | |||
| <input class="phone_input" type="text" placeholder="请输入手机号"> | |||
| <input class="code_input" type="text" placeholder="请输入验证码"> | |||
| <div class="send_code hand">发送验证码</div> | |||
| </div> | |||
| <div id="adminForm" class="input_box hidden password-container"> | |||
| <input class="phone_input" type="text" placeholder="请输入账号"> | |||
| <input id="password-input" class="code_input" type="password" placeholder="请输入密码"> | |||
| <img id="password_toggle" class="password_toggle hand" src="/static/img/close.png" alt=""> | |||
| </div> | |||
| <div class="login_style"> | |||
| <div id="employeeBtn" class="employee hand"> | |||
| <div id="choose_employee" class="choose_employee background"></div> | |||
| <div>员工登录</div> | |||
| </div> | |||
| <div id="adminBtn" class="administrator hand"> | |||
| <div id="choose_administrator" class="choose_administrator"></div> | |||
| <div>管理员登录</div> | |||
| </div> | |||
| </div> | |||
| <div class="login_button hand">登 录</div> | |||
| </div> | |||
| <!-- 注册页面 --> | |||
| <div style="display: none;" class="register login"> | |||
| <div class="register_loginTitle "> | |||
| <div class="upload-img-box"> | |||
| <div class="input-box"> | |||
| <input type="file" id="img-file" hidden="hidden" name="选择图片"> | |||
| </div> | |||
| <div class="befor-img"> | |||
| <img class="uploadImg hand" src="/static/img/upload.png" alt=""> | |||
| </div> | |||
| <div class="after-img"> | |||
| <img id="img-box" src="" width="30%" height="auto"> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="register_inputBox"> | |||
| <input class="register_phoneInput" type="text" placeholder="员工编号"> | |||
| <input class="register_codeInput" type="text" placeholder="员工姓名"> | |||
| </div> | |||
| <div class="register_loginButton hand">注册人脸</div> | |||
| </div> | |||
| <!-- 管理员页面 --> | |||
| <div style="display: none;" class="approval_page"> | |||
| <div id="scroll_box" class="approval_box"> | |||
| <input id="search" class="search_people" type="text" placeholder="🔍 搜索"> | |||
| <ul class="list_box"> | |||
| <li class="every_info"> | |||
| <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div> | |||
| </li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| <li class="every_info"> <img class="people_img" src="/static/img/logo.png" alt=""> | |||
| <div class="name">张三</div> | |||
| <div class="people_code">001</div> | |||
| <div class="pass_nopass"> | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| </div></li> | |||
| </ul> | |||
| </div> | |||
| <a id="scroll-to-top-btn" href=""> | |||
| <img class="goTop_img" src="/static/img/goTop.png" alt=""> | |||
| </a> | |||
| </div> | |||
| <script src="/static/js/jquery.min.js"></script> | |||
| <script> | |||
| $(function(){ | |||
| $(".login_button").on("click",function(){ | |||
| console.log("click"); | |||
| $(".login").css("display","none"); | |||
| // $(".approval_page").css("display","block"); | |||
| $(".register").css("display","block"); | |||
| }) | |||
| $(document).on("click", ".upload-img-box .befor-img,.upload-img-box .after-img", function (e) { | |||
| $(".input-box input").trigger("click"); | |||
| }) | |||
| // 使用input 的change事件,监听当文件上传的值进行改变的时候,才触发事件。 | |||
| $(document).on("change", ".upload-img-box #img-file", function () { | |||
| var file = $("#img-file")[0].files[0]; | |||
| console.log(file); | |||
| // file 内容包括: lastModified , lastModifiedDate , name , size , type | |||
| var formdata = new FormData(); | |||
| formdata.append('file', file); | |||
| if (window.FileReader) { | |||
| var fr = new FileReader(); | |||
| fr.onloadend = function (e) { | |||
| // console.log(this.result); | |||
| // document.getElementById("portrait").src = e.target.result; | |||
| $("#img-box").attr("src",e.target.result) | |||
| $(".upload-img-box .befor-img").addClass("hasImg") | |||
| }; | |||
| //给FileReader对象一个读取完成的方法, | |||
| //使用readAsDataURL会返回一个url | |||
| //这个值就保存在事件对象的result里 | |||
| //img通过加载这个地址,完成图片的加载 | |||
| fr.readAsDataURL(file); | |||
| } | |||
| //判断图片大小 | |||
| if (file.size > 3 * 1024 * 1024) { | |||
| alert("上传图片不能大于3M"); | |||
| } | |||
| //判断图片数据类型 | |||
| if (!/.(gif|jpg|jpeg|png|GIF|JPG|bmp)$/.test(file.name)) { | |||
| alert("图片类型必须是.gif,jpeg,jpg,png,bmp中的一种"); | |||
| } | |||
| }) | |||
| }) | |||
| // 切换登陆页面 | |||
| document.getElementById("employeeBtn").addEventListener("click", function() { | |||
| document.getElementById("employeeForm").classList.add("active"); | |||
| document.getElementById("adminForm").classList.remove("active"); | |||
| document.getElementById("choose_administrator").classList.remove("background"); | |||
| document.getElementById("choose_employee").classList.add("background"); | |||
| }); | |||
| document.getElementById("adminBtn").addEventListener("click", function() { | |||
| document.getElementById("employeeForm").classList.remove("active"); | |||
| document.getElementById("adminForm").classList.add("active"); | |||
| document.getElementById("choose_employee").classList.remove("background"); | |||
| document.getElementById("choose_administrator").classList.add("background"); | |||
| }); | |||
| document.getElementById("password_toggle").addEventListener("click", function() { | |||
| let passwordInput = document.getElementById("password-input") | |||
| if (passwordInput.type === 'password') { | |||
| passwordInput.type ='text'; | |||
| document.getElementById("password_toggle").src = "/static/img/open.png" | |||
| } else { | |||
| passwordInput.type = "password"; | |||
| document.getElementById("password_toggle").src = "/static/img/close.png" | |||
| } | |||
| }); | |||
| // 点击回到顶部 | |||
| var element = document.getElementById('search'); | |||
| var observer = new IntersectionObserver(function(entries) { | |||
| entries.forEach(function(entry) { | |||
| var scrollToTopBtn = document.getElementById('scroll-to-top-btn'); | |||
| if (entry.isIntersecting) { | |||
| // 元素进入视口,执行代码 | |||
| scrollToTopBtn.style.display = 'none'; | |||
| } else { | |||
| scrollToTopBtn.style.display = 'block'; | |||
| } | |||
| }); | |||
| }); | |||
| observer.observe(element); | |||
| document.getElementById('scroll-to-top-btn').addEventListener('click', function() { | |||
| document.getElementById("scroll_box").scrollTop = 0 | |||
| }); | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,578 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
| <title>页面登录</title> | |||
| <style> | |||
| body{ | |||
| margin: 0; | |||
| } | |||
| li { | |||
| list-style-type: none; | |||
| } | |||
| @media screen and (orientation: portrait) { | |||
| .login { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/phoneBgc.jpg"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| } | |||
| .register { | |||
| background-image: url("/static/img/registerBgc.jpg") !important; | |||
| padding-top: 15vh; | |||
| } | |||
| .logo { | |||
| width: 15vw; | |||
| margin: 4vw 0 0 4vw; | |||
| } | |||
| .login_title { | |||
| width: 100vw; | |||
| font-size: 8vw; | |||
| text-align: center; | |||
| margin-top: 15vh; | |||
| } | |||
| .input_box { | |||
| width: 100vw; | |||
| height: 25vw; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-top: 5vh; | |||
| position: relative; | |||
| } | |||
| .phone_input, .account_input{ | |||
| width: 50vw; | |||
| height: 11vw; | |||
| border-radius: 2vw; | |||
| background-color: transparent; | |||
| font-size: 3vw; | |||
| padding-left: 2vw; | |||
| border: 1px solid #000; | |||
| position: absolute; | |||
| top: 0vw; | |||
| } | |||
| .code_input, .password_input{ | |||
| width: 50vw; | |||
| height: 11vw; | |||
| margin-top: 2vh; | |||
| border-radius: 2vw; | |||
| padding-left: 2vw; | |||
| font-size: 3vw; | |||
| background-color: transparent; | |||
| border: 1px solid #000; | |||
| position: absolute; | |||
| top: 10vw; | |||
| } | |||
| .login_button { | |||
| width: 40vw; | |||
| height: 5vh; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color:#fff; | |||
| font-size: 4vw; | |||
| margin: 2vh 0 0 30vw; | |||
| } | |||
| #sendCodeBtn { | |||
| position: absolute; | |||
| font-size: 2.5vw; | |||
| top: 18.5vw; | |||
| right: 30vw; | |||
| border-left: 2px solid; | |||
| padding-left: 2vw; | |||
| border: none; | |||
| background: transparent; | |||
| } | |||
| .employee { | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| .administrator { | |||
| display: flex; | |||
| align-items: center; | |||
| margin-left: 5vw; | |||
| } | |||
| .choose_employee { | |||
| width: 2vw; | |||
| height: 2vw; | |||
| border-radius: 50%; | |||
| border: 2px solid #000; | |||
| margin-right: 1vw; | |||
| } | |||
| .choose_administrator { | |||
| width: 2vw; | |||
| height: 2vw; | |||
| border-radius: 50%; | |||
| border: 2px solid #000; | |||
| margin-right: 1vw; | |||
| } | |||
| .login_style { | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| font-size: 2.5vw; | |||
| margin-top: 2vh; | |||
| } | |||
| .hidden { | |||
| display: none; | |||
| } | |||
| .active { | |||
| display: flex; | |||
| } | |||
| .background { | |||
| background-color: #000; | |||
| } | |||
| .password-container { | |||
| position: relative; | |||
| } | |||
| .password_toggle { | |||
| position: absolute; | |||
| width: 5vw; | |||
| top: 18vw; | |||
| right: 26vw; | |||
| } | |||
| } | |||
| @media screen and (orientation: landscape) { | |||
| input:focus { | |||
| border: 0.1vw solid #000; | |||
| outline: none; /* 可选,用于去除默认的外边框样式 */ | |||
| } | |||
| .login { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/phoneBgc_pc.jpg"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| position: relative; | |||
| } | |||
| .PC_img { | |||
| width: 52.6vw; | |||
| height: 64.44vh; | |||
| background-image: url("/static/img/loginBox.png"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| position: absolute; | |||
| top: 18.88vh; | |||
| left: 23.7vw; | |||
| } | |||
| .register { | |||
| background-image: url("/static/img/registerBgc_pc.jpg") !important; | |||
| } | |||
| .logo { | |||
| width: 15vh; | |||
| margin: 4vh 0 0 4vh; | |||
| } | |||
| .login_title { | |||
| /* width: 100vw; */ | |||
| font-size: 4vh; | |||
| text-align: center; | |||
| position: absolute; | |||
| z-index: 10; | |||
| top: 28vh; | |||
| margin-left: 58vw; | |||
| /* margin-top: 15vh;*/ | |||
| } | |||
| .input_box { | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-top: 15vh; | |||
| position: relative; | |||
| } | |||
| .phone_input, .account_input { | |||
| width: 15vw; | |||
| height: 6.66vh; | |||
| border-radius: 2vh; | |||
| background-color: transparent; | |||
| font-size: 1.5vw; | |||
| padding-left: 2vh; | |||
| padding-right: 5vw; | |||
| border: 0.1vw solid #000; | |||
| margin-left: 25vw; | |||
| } | |||
| .code_input, .password_input { | |||
| width: 15vw; | |||
| height: 6.66vh; | |||
| margin-top: 2vw; | |||
| border-radius: 2vh; | |||
| padding-left: 2vh; | |||
| font-size: 1.5vw; | |||
| padding-right: 5vw; | |||
| background-color: transparent; | |||
| border: 0.1vw solid #000; | |||
| margin-left: 25vw; | |||
| } | |||
| .login_button { | |||
| width: 16vw; | |||
| height: 3vw; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vh; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color: #fff; | |||
| font-size: 2vw; | |||
| /* margin: 2vh 0 0 30vh; */ | |||
| position: absolute; | |||
| left: 55vw; | |||
| top: 65vh; | |||
| } | |||
| #sendCodeBtn { | |||
| position: absolute; | |||
| font-size: 1.5vh; | |||
| top: 11.5vh; | |||
| right: 27.3vw; | |||
| border-left: 0.1vw solid; | |||
| padding-left: 1vh; | |||
| display: flex; | |||
| align-items: center; | |||
| height: 3vw; | |||
| border: none; | |||
| background: transparent; | |||
| } | |||
| .employee { | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| .administrator { | |||
| display: flex; | |||
| align-items: center; | |||
| margin-left: 3vw; | |||
| } | |||
| .hand:hover { | |||
| cursor: pointer; | |||
| } | |||
| .choose_employee { | |||
| width: 2vh; | |||
| height: 2vh; | |||
| border-radius: 50%; | |||
| border: 0.1vw solid #000; | |||
| margin-right: 1vh; | |||
| } | |||
| .choose_administrator { | |||
| width: 2vh; | |||
| height: 2vh; | |||
| border-radius: 50%; | |||
| border: 0.1vw solid #000; | |||
| margin-right: 1vh; | |||
| } | |||
| .login_style { | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| font-size: 1vw; | |||
| margin-top: 2vw; | |||
| position: absolute; | |||
| left: 54.8vw; | |||
| } | |||
| .hidden { | |||
| display: none; | |||
| } | |||
| .active { | |||
| display: flex; | |||
| } | |||
| .background { | |||
| background-color: #000; | |||
| } | |||
| .password-container { | |||
| position: relative; | |||
| } | |||
| .password_toggle { | |||
| width: 3vw; | |||
| position: absolute; | |||
| font-size: 1.5vh; | |||
| top: 11.5vh; | |||
| right: 28.5vw; | |||
| padding-left: 0.5vh; | |||
| } | |||
| #scroll-to-top-btn { | |||
| display: none; | |||
| position: fixed; | |||
| right: 20px; | |||
| bottom: 20px; | |||
| width: 7vh; | |||
| height: 10vw; | |||
| color: #fff; | |||
| border: none; | |||
| border-radius: 5px; | |||
| font-size: 3vh; | |||
| cursor: pointer; | |||
| } | |||
| } | |||
| button:disabled { | |||
| color: black !important; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <!-- 登录页面 --> | |||
| <div class="login"> | |||
| <img class="logo" src="/static/img/logo.png" alt=""> | |||
| <div class="PC_img"></div> | |||
| <div class="login_title">请 登 录</div> | |||
| <div id="employeeForm" class="input_box hidden active"> | |||
| <input class="phone_input" type="text" placeholder="请输入手机号"> | |||
| <input class="code_input" type="text" placeholder="请输入验证码"> | |||
| <button id="sendCodeBtn" class="btn">发送验证码</button> | |||
| </div> | |||
| <div id="adminForm" class="input_box hidden password-container"> | |||
| <input class="account_input" type="text" placeholder="请输入账号"> | |||
| <input id="password_input" class="password_input" type="password" placeholder="请输入密码"> | |||
| <img id="password_toggle" class="password_toggle hand" src="/static/img/close.png" alt=""> | |||
| </div> | |||
| <div class="login_style"> | |||
| <div id="employeeBtn" class="employee hand"> | |||
| <div id="choose_employee" class="choose_employee background"></div> | |||
| <div>员工登录</div> | |||
| </div> | |||
| <div id="adminBtn" class="administrator hand"> | |||
| <div id="choose_administrator" class="choose_administrator"></div> | |||
| <div>管理员登录</div> | |||
| </div> | |||
| </div> | |||
| <div class="login_button hand">登 录</div> | |||
| </div> | |||
| <script src="/static/js/jquery.min.js"></script> | |||
| <script src="/static/js/msg-box.js"></script> | |||
| <script> | |||
| $(function(){ | |||
| console.log("{{.type}}"); | |||
| if ("{{.type}}" == "mgr") { | |||
| $("#adminBtn").click(); | |||
| } | |||
| $.MsgBox = { | |||
| Alert: function(title, msg) { | |||
| GenerateHtml("alert", title, msg); | |||
| btnOk(); //alert只是弹出消息,因此没必要用到回调函数callback | |||
| btnNo(); | |||
| }, | |||
| Confirm: function(title, msg, callback) { | |||
| GenerateHtml("confirm", title, msg); | |||
| btnOk(callback); | |||
| btnNo(); | |||
| } | |||
| } | |||
| const sendCodeBtn = $('#sendCodeBtn'); | |||
| let countdown = 30; | |||
| let timer; | |||
| function startCountdown() { | |||
| sendCodeBtn.attr("disabled", "disabled"); | |||
| sendCodeBtn.html(`${countdown}秒`); | |||
| timer = setInterval(() => { | |||
| console.log(countdown); | |||
| countdown--; | |||
| sessionStorage.setItem('countdown',countdown); | |||
| sendCodeBtn.html(`${countdown}秒`); | |||
| if (countdown <= 0) { | |||
| clearInterval(timer); | |||
| sendCodeBtn.html('发送验证码'); | |||
| sendCodeBtn.removeAttr("disabled"); | |||
| countdown = 30; | |||
| } | |||
| }, 1000); | |||
| } | |||
| $("#sendCodeBtn").on("click",function(){ | |||
| if (!phoneValid()){ | |||
| $.MsgBox.Alert("提示", "手机号为空或不合法,请检查"); | |||
| return; | |||
| } | |||
| countdown = 30; | |||
| startCountdown(); | |||
| sessionStorage.setItem('countdown',countdown); | |||
| // ajax发送验证码 | |||
| $.ajax({ | |||
| url: '/send_validation_code', | |||
| type: 'POST', | |||
| dataType: 'json', | |||
| data: { | |||
| phone: $(".phone_input").val(), | |||
| }, | |||
| success: function (response) { | |||
| console.log(response); | |||
| response = JSON.parse(response) | |||
| if (response.code == 200) { | |||
| $.MsgBox.Alert("提示", "验证码发送成功"); | |||
| } else { | |||
| $.MsgBox.Alert("提示", response.msg ? response.msg : "验证码发送失败"); | |||
| } | |||
| }, | |||
| error: function () { | |||
| } | |||
| }); | |||
| }) | |||
| function initCountdown() { | |||
| const storedCountdown = sessionStorage.getItem('countdown'); | |||
| if (storedCountdown) { | |||
| countdown = parseInt(storedCountdown, 10); | |||
| if (countdown > 0) { | |||
| startCountdown(); | |||
| } | |||
| } | |||
| } | |||
| initCountdown(); | |||
| function phoneValid() { | |||
| const regex = /^1[3-9]\d{9}$/; | |||
| var phone = $(".phone_input").val(); | |||
| if (phone == "" || !regex.test(phone)){ | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| $(".login_button").on("click",function(){ | |||
| if ($("#employeeForm").hasClass("active")) { | |||
| if (!phoneValid()){ | |||
| $.MsgBox.Alert("提示", "手机号为空或不合法,请检查"); | |||
| return; | |||
| } | |||
| const codeRegex = /^\d{4}$/; | |||
| var code = $(".code_input").val(); | |||
| if (code == "" || !codeRegex.test(code)){ | |||
| $.MsgBox.Alert("提示", "验证码为4位数字,请检查"); | |||
| return; | |||
| } | |||
| $.ajax({ | |||
| url: '/valid_code', | |||
| type: 'POST', | |||
| dataType: 'json', | |||
| data: { | |||
| phone: $(".phone_input").val(), | |||
| code: code, | |||
| }, | |||
| success: function (response) { | |||
| console.log(response); | |||
| response = JSON.parse(response) | |||
| if (response.status == 0) { | |||
| $.ajax({ | |||
| url: '/phone_login', | |||
| type: 'POST', | |||
| dataType: 'json', | |||
| data: { | |||
| phone: $(".phone_input").val(), | |||
| }, | |||
| success: function (response) { | |||
| console.log(response); | |||
| console.log(response.data); | |||
| console.log(response.data.staffId); | |||
| // response = JSON.parse(response) | |||
| if (response.code == 200) { | |||
| window.location.href = '/info?staffId='+response.data.staffId; | |||
| } else { | |||
| $.MsgBox.Alert("提示", response.msg); | |||
| } | |||
| }, | |||
| error: function () { | |||
| } | |||
| }); | |||
| } else { | |||
| $.MsgBox.Alert("提示", response.err); | |||
| } | |||
| }, | |||
| error: function () { | |||
| } | |||
| }); | |||
| } else { | |||
| if ($(".account_input").val() == "") { | |||
| $.MsgBox.Alert("提示", "请输入账号!"); | |||
| return; | |||
| } | |||
| if ($(".password_input").val() == "") { | |||
| $.MsgBox.Alert("提示", "请输入密码!"); | |||
| return; | |||
| } | |||
| $.ajax({ | |||
| url: '/mgr_login', | |||
| type: 'POST', | |||
| dataType: 'json', | |||
| data: { | |||
| userName: $(".account_input").val(), | |||
| password: $(".password_input").val(), | |||
| }, | |||
| success: function (response) { | |||
| console.log(response); | |||
| response = JSON.parse(response) | |||
| if (response.code == 200) { | |||
| console.log(response.data.access_token); | |||
| console.log(response.type); | |||
| sessionStorage.setItem('token',response.type); | |||
| window.location.href = '/mgr'; | |||
| } else { | |||
| $.MsgBox.Alert("提示", response.msg); | |||
| } | |||
| }, | |||
| error: function () { | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }) | |||
| // 切换登陆页面 | |||
| document.getElementById("employeeBtn").addEventListener("click", function() { | |||
| document.getElementById("employeeForm").classList.add("active"); | |||
| document.getElementById("adminForm").classList.remove("active"); | |||
| document.getElementById("choose_administrator").classList.remove("background"); | |||
| document.getElementById("choose_employee").classList.add("background"); | |||
| }); | |||
| document.getElementById("adminBtn").addEventListener("click", function() { | |||
| document.getElementById("employeeForm").classList.remove("active"); | |||
| document.getElementById("adminForm").classList.add("active"); | |||
| document.getElementById("choose_employee").classList.remove("background"); | |||
| document.getElementById("choose_administrator").classList.add("background"); | |||
| }); | |||
| document.getElementById("password_toggle").addEventListener("click", function() { | |||
| let passwordInput = document.getElementById("password_input") | |||
| if (passwordInput.type === 'password') { | |||
| passwordInput.type ='text'; | |||
| document.getElementById("password_toggle").src = "/static/img/open.png" | |||
| } else { | |||
| passwordInput.type = "password"; | |||
| document.getElementById("password_toggle").src = "/static/img/close.png" | |||
| } | |||
| }); | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,402 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
| <title>管理页面</title> | |||
| <style> | |||
| body{ | |||
| margin: 0; | |||
| } | |||
| li { | |||
| list-style-type: none; | |||
| } | |||
| @media screen and (orientation: portrait) { | |||
| .approval_page { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/registerBgc.jpg"); | |||
| background-size: 100% 100%; | |||
| overflow: auto; | |||
| } | |||
| .approval_box { | |||
| max-width: 100vw; | |||
| min-height: 100vh; | |||
| background-color:rgba(237,237,237,0.8); | |||
| } | |||
| .search_people { | |||
| width: 80vw; | |||
| height: 5vh; | |||
| border-radius: 1vw; | |||
| margin: 5vh 0 0 10vw; | |||
| border: 0 solid #fff; | |||
| font-size: 3vw; | |||
| text-align: center; | |||
| } | |||
| .every_info { | |||
| height: 7vh; | |||
| margin-top: 3vh; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-around; | |||
| font-size: 3vw; | |||
| } | |||
| #scroll-to-top-btn { | |||
| display: none; | |||
| position: fixed; | |||
| right: 20px; | |||
| bottom: 20px; | |||
| width: 7vw; | |||
| height: 10vh; | |||
| color: #fff; | |||
| border: none; | |||
| border-radius: 5px; | |||
| font-size: 3vw; | |||
| cursor: pointer; | |||
| } | |||
| .people_img { | |||
| width: 6vh; | |||
| height: 6vh; | |||
| border-radius: 50%; | |||
| } | |||
| .pass_nopass { | |||
| display: flex; | |||
| } | |||
| .pass { | |||
| background-color: rgb(92, 186, 45); | |||
| width: 12vw; | |||
| height: 6vw; | |||
| border-radius: 1vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color: white; | |||
| } | |||
| .no_pass, .del_pass { | |||
| background-color: rgb(187, 44, 45); | |||
| width: 12vw; | |||
| height: 6vw; | |||
| border-radius: 1vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-left: 3vw; | |||
| color: white; | |||
| } | |||
| .goTop_img { | |||
| width: 6vw; | |||
| height: 6vw; | |||
| } | |||
| .pass_info{ | |||
| width: 25vw; | |||
| } | |||
| .no_pass_info{ | |||
| line-height: 3vh; | |||
| } | |||
| } | |||
| @media screen and (orientation: landscape) { | |||
| input:focus { | |||
| border: 0.1vw solid #000; | |||
| outline: none; /* 可选,用于去除默认的外边框样式 */ | |||
| } | |||
| .approval_page { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/registerBgc.jpg"); | |||
| background-size: 100% 100%; | |||
| overflow: auto; | |||
| } | |||
| .approval_box { | |||
| max-width: 100vw; | |||
| min-height: 100vh; | |||
| background-color:rgba(237,237,237,0.8); | |||
| } | |||
| .search_people { | |||
| width: 60vw; | |||
| height: 6vh; | |||
| border-radius: 1vh; | |||
| margin: 5vh 0 0 20vw; | |||
| border: 0 solid #fff; | |||
| font-size: 3vh; | |||
| text-align: center; | |||
| } | |||
| .every_info { | |||
| height: 7vw; | |||
| margin-top: 3vw; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: space-around; | |||
| font-size: 3vh; | |||
| } | |||
| #scroll-to-top-btn { | |||
| display: none; | |||
| position: fixed; | |||
| right: 20px; | |||
| bottom: 20px; | |||
| width: 7vh; | |||
| height: 10vw; | |||
| color: #fff; | |||
| border: none; | |||
| border-radius: 5px; | |||
| font-size: 3vh; | |||
| cursor: pointer; | |||
| } | |||
| .people_img { | |||
| width: 6vw; | |||
| height: 6vw; | |||
| border-radius: 50%; | |||
| } | |||
| .pass_nopass { | |||
| display: flex; | |||
| } | |||
| .pass { | |||
| background-color: rgb(92, 186, 45); | |||
| width: 10vh; | |||
| height: 5vh; | |||
| border-radius: 1vh; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color: white; | |||
| cursor: pointer; | |||
| } | |||
| .no_pass, .del_pass { | |||
| background-color: rgb(187, 44, 45); | |||
| width: 10vh; | |||
| height: 5vh; | |||
| border-radius: 1vh; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-left: 2vh; | |||
| color: white; | |||
| cursor: pointer; | |||
| } | |||
| .goTop_img { | |||
| width: 6vh; | |||
| height: 6vh; | |||
| } | |||
| } | |||
| .list_box{ | |||
| padding-inline-start: 0px; | |||
| } | |||
| .uploadImg{ | |||
| width: 48% !important; | |||
| transform: translate(0%, 0%) !important; | |||
| } | |||
| .upload-img-box { | |||
| position: relative; | |||
| } | |||
| .upload-img-box .input-box { | |||
| display: inline-block; | |||
| width: 144px; | |||
| height: 144px; | |||
| /*border: solid black 1px;*/ | |||
| } | |||
| .upload-img-box .befor-img { | |||
| display: flex; | |||
| /*width: 300px;*/ | |||
| /*height: 90px;*/ | |||
| /*border: solid black 1px;*/ | |||
| position: absolute; | |||
| top: 0px; | |||
| left: 0px; | |||
| /*background: #ffffff;*/ | |||
| z-index: 30; | |||
| align-items: center; | |||
| justify-content: center; | |||
| } | |||
| .upload-img-box .befor-img.hasImg { | |||
| display: none; | |||
| } | |||
| .upload-img-box .after-img { | |||
| display: inline-block; | |||
| width: 144px; | |||
| height: 144px; | |||
| border: none; | |||
| /*position: absolute;*/ | |||
| top: 0px; | |||
| left: 0px; | |||
| /*background: #ffffff;*/ | |||
| z-index: 20; | |||
| } | |||
| .after-img img, .uploadImg { | |||
| /*position: absolute;*/ | |||
| width: 100%; | |||
| height: 100%; | |||
| object-fit: cover; /*根据父容器的尺寸裁剪和缩放图片,以填充整个容器,并保持长宽比例不变。*/ | |||
| object-position: center; /*指定对象的位置,如果不设置,则默认是center*/ | |||
| background-size: cover; /*背景图片的大小,如果不设置,则默认是auto*/ | |||
| background-position: center; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, 0%);/*将元素移动到中心*/ | |||
| border-radius: 50%; | |||
| } | |||
| .pass_info{ | |||
| color: green; | |||
| } | |||
| .no_pass_info{ | |||
| color: red; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <!-- 管理员页面 --> | |||
| <div class="approval_page" style="display: none;"> | |||
| <div id="scroll_box" class="approval_box"> | |||
| <input id="search" class="search_people" value="{{.key}}" type="text" placeholder="🔍 搜索"> | |||
| <ul class="list_box"> | |||
| {{ range $i, $v := .users }} | |||
| <li class="every_info" itemid="{{$v.ID}}"> | |||
| <img class="people_img" src="{{$v.Avatar}}" alt=""> | |||
| <div class="name">{{$v.Name}}</div> | |||
| <div class="people_code">{{$v.Phone}}</div> | |||
| <div class="pass_nopass"> | |||
| {{ if eq $v.Status 1 }} | |||
| <div class="pass_info">通过</div> | |||
| {{ else if eq $v.Status 2 }} | |||
| <div class="no_pass_info">不通过</div> | |||
| <div class="del_pass">删除X</div> | |||
| {{ else }} | |||
| <div class="pass">通过</div> | |||
| <div class="no_pass">不通过</div> | |||
| {{ end }} | |||
| </div> | |||
| </li> | |||
| {{ end }} | |||
| </ul> | |||
| </div> | |||
| <a id="scroll-to-top-btn" href=""> | |||
| <img class="goTop_img" src="/static/img/goTop.png" alt=""> | |||
| </a> | |||
| </div> | |||
| <script src="/static/js/jquery.min.js"></script> | |||
| <script src="/static/js/msg-box.js"></script> | |||
| <script> | |||
| console.log("9999999"); | |||
| var token= sessionStorage.getItem('token'); | |||
| console.log(token) | |||
| if (!token){ | |||
| $.MsgBox.AlertWithCallback("提示", "此页面需要先进行登录", function (){ | |||
| window.location.href = "/?type=mgr"; | |||
| }); | |||
| } else { | |||
| $(".approval_page").css("display","block"); | |||
| } | |||
| $(function(){ | |||
| var t=$("#search").val(); | |||
| $("#search").val("").focus().val(t); | |||
| $('.pass').on("click",function(){ | |||
| var id = $(this).parent().parent().attr('itemid'); | |||
| $.ajax({ | |||
| url: "/pass", | |||
| type: "post", | |||
| data: { | |||
| handle: 1, | |||
| id: id | |||
| }, | |||
| success: function (data) { | |||
| $.MsgBox.AlertWithCallback("提示", data.Message, function (){ | |||
| location.reload(); | |||
| }); | |||
| }, | |||
| error: function (error) { | |||
| console.log(error); | |||
| $.MsgBox.Alert("提示", error.responseJSON.Message); | |||
| } | |||
| }) | |||
| }) | |||
| $('.no_pass').on("click",function(){ | |||
| var id = $(this).parent().parent().attr('itemid'); | |||
| $.MsgBox.Confirm("提示", "确定要否决这条注册吗?", function () { | |||
| $.ajax({ | |||
| url: "/pass", | |||
| type: "post", | |||
| data: { | |||
| handle: 2, | |||
| id: id | |||
| }, | |||
| success: function (data) { | |||
| location.reload(); | |||
| }, | |||
| error: function (error) { | |||
| console.log(error); | |||
| $.MsgBox.Alert("提示", error.responseJSON.Message); | |||
| } | |||
| }) | |||
| }) | |||
| }) | |||
| $('.del_pass').on("click",function(){ | |||
| var id = $(this).parent().parent().attr('itemid'); | |||
| $.MsgBox.Confirm("提示", "确定要删除这条注册吗?", function (){ | |||
| $.ajax({ | |||
| url: "/pass", | |||
| type: "post", | |||
| data: { | |||
| handle: 4, | |||
| id: id | |||
| }, | |||
| success: function (data) { | |||
| location.reload(); | |||
| }, | |||
| error: function (error) { | |||
| console.log(error); | |||
| $.MsgBox.Alert("提示", error.responseJSON.Message); | |||
| } | |||
| }) | |||
| }) | |||
| }) | |||
| $("#search").on('change',function(e){ | |||
| window.location.href = "/mgr?key="+$(this).val() | |||
| }); | |||
| }) | |||
| // 点击回到顶部 | |||
| var element = document.getElementById('search'); | |||
| var observer = new IntersectionObserver(function(entries) { | |||
| entries.forEach(function(entry) { | |||
| var scrollToTopBtn = document.getElementById('scroll-to-top-btn'); | |||
| if (entry.isIntersecting) { | |||
| // 元素进入视口,执行代码 | |||
| scrollToTopBtn.style.display = 'none'; | |||
| } else { | |||
| scrollToTopBtn.style.display = 'block'; | |||
| } | |||
| }); | |||
| }); | |||
| observer.observe(element); | |||
| document.getElementById('scroll-to-top-btn').addEventListener('click', function() { | |||
| document.getElementById("scroll_box").scrollTop = 0 | |||
| }); | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,396 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
| <title>注册页面</title> | |||
| <style> | |||
| body{ | |||
| margin: 0; | |||
| } | |||
| li { | |||
| list-style-type: none; | |||
| } | |||
| @media screen and (orientation: portrait) { | |||
| .login { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/phoneBgc.jpg"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| } | |||
| .register { | |||
| background-image: url("/static/img/registerBgc.jpg") !important; | |||
| padding-top: 15vh; | |||
| } | |||
| .register_loginTitle { | |||
| width: 100vw; | |||
| font-size: 8vw; | |||
| text-align: center; | |||
| } | |||
| .register_inputBox { | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| margin-top: 5vh; | |||
| position: relative; | |||
| } | |||
| .register_phoneInput { | |||
| width: 50vw; | |||
| height: 5vh; | |||
| border-radius: 2vw; | |||
| background-color: transparent; | |||
| font-size: 3vw; | |||
| padding-left: 2vw; | |||
| border: 1px solid #000; | |||
| margin-bottom: 2vh; | |||
| } | |||
| .register_nameInput { | |||
| width: 50vw; | |||
| height: 5vh; | |||
| border-radius: 2vw; | |||
| background-color: transparent; | |||
| font-size: 3vw; | |||
| padding-left: 2vw; | |||
| border: 1px solid #000; | |||
| margin-bottom: 2vh; | |||
| } | |||
| .reg_button { | |||
| width: 40vw; | |||
| height: 5vh; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color:#fff; | |||
| font-size: 4vw; | |||
| margin: 2vh 0 0 30vw; | |||
| } | |||
| .register_loginButton { | |||
| width: 40vw; | |||
| height: 5vh; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color:#fff; | |||
| font-size: 4vw; | |||
| margin: 2vh 0 0 30vw; | |||
| } | |||
| #scroll-to-top-btn { | |||
| display: none; | |||
| position: fixed; | |||
| right: 20px; | |||
| bottom: 20px; | |||
| width: 7vw; | |||
| height: 10vh; | |||
| color: #fff; | |||
| border: none; | |||
| border-radius: 5px; | |||
| font-size: 3vw; | |||
| cursor: pointer; | |||
| } | |||
| .uploadImg{ | |||
| width: 46vw !important; | |||
| transform: translate(0%, 0%) !important; | |||
| left: 27vw !important; | |||
| position: relative; | |||
| } | |||
| } | |||
| @media screen and (orientation: landscape) { | |||
| input:focus { | |||
| border: 0.1vw solid #000; | |||
| outline: none; /* 可选,用于去除默认的外边框样式 */ | |||
| } | |||
| .login { | |||
| width: 100vw; | |||
| height: 100vh; | |||
| background-image: url("/static/img/phoneBgc_pc.jpg"); | |||
| background-size: 100% 100%; | |||
| background-repeat: no-repeat; | |||
| position: relative; | |||
| } | |||
| .register { | |||
| background-image: url("/static/img/registerBgc_pc.jpg") !important; | |||
| } | |||
| .register_loginTitle { | |||
| width: 100vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| font-size: 4vh; | |||
| text-align: center; | |||
| position: absolute; | |||
| z-index: 10; | |||
| top: 10vh; | |||
| } | |||
| .uploadImg { | |||
| width: 15vw; | |||
| } | |||
| .register_inputBox { | |||
| width: 100vw; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| position: relative; | |||
| padding-top: 42vh; | |||
| } | |||
| .register_phoneInput { | |||
| width: 37vh; | |||
| height: 6vh; | |||
| border-radius: 2vh; | |||
| background-color: transparent; | |||
| font-size: 3vh; | |||
| border: 0.1vw solid #000; | |||
| margin-bottom: 5vh; | |||
| padding-left:1vw ; | |||
| } | |||
| .register_nameInput { | |||
| width: 37vh; | |||
| height: 6vh; | |||
| border-radius: 2vh; | |||
| background-color: transparent; | |||
| font-size: 3vh; | |||
| border: 0.1vw solid #000; | |||
| padding-left: 1vw ; | |||
| margin-bottom: 2vh; | |||
| } | |||
| .reg_button { | |||
| width: 16vw; | |||
| height: 3vw; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 10vh; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color: #fff; | |||
| font-size: 2vw; | |||
| /* margin: 2vh 0 0 30vh; */ | |||
| position: absolute; | |||
| left: 55vw; | |||
| top: 65vh; | |||
| } | |||
| .register_loginButton { | |||
| width: 8vw; | |||
| height: 2vw; | |||
| background-color: rgb(101, 161, 255); | |||
| border-radius: 1vw; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| color: #fff; | |||
| font-size: 3vh; | |||
| position: absolute; | |||
| left: 46vw; | |||
| top: 65vh; | |||
| } | |||
| .hand:hover{ | |||
| cursor: pointer; | |||
| } | |||
| #scroll-to-top-btn { | |||
| display: none; | |||
| position: fixed; | |||
| right: 20px; | |||
| bottom: 20px; | |||
| width: 7vh; | |||
| height: 10vw; | |||
| color: #fff; | |||
| border: none; | |||
| border-radius: 5px; | |||
| font-size: 3vh; | |||
| cursor: pointer; | |||
| } | |||
| .uploadImg{ | |||
| width: 200px !important; | |||
| transform: translate(0%, 0%) !important; | |||
| left: 2vw !important; | |||
| position: relative; | |||
| } | |||
| } | |||
| .upload-img-box { | |||
| position: relative; | |||
| } | |||
| .upload-img-box .input-box { | |||
| display: inline-block; | |||
| width: 144px; | |||
| height: 144px; | |||
| /*border: solid black 1px;*/ | |||
| } | |||
| .upload-img-box .befor-img { | |||
| display: flex; | |||
| /*width: 300px;*/ | |||
| /*height: 90px;*/ | |||
| /*border: solid black 1px;*/ | |||
| position: absolute; | |||
| top: 0px; | |||
| left: 0px; | |||
| /*background: #ffffff;*/ | |||
| z-index: 30; | |||
| align-items: center; | |||
| justify-content: center; | |||
| } | |||
| .upload-img-box .befor-img.hasImg { | |||
| display: none; | |||
| } | |||
| .upload-img-box .after-img { | |||
| display: inline-block; | |||
| width: 144px; | |||
| height: 144px; | |||
| border: none; | |||
| /*position: absolute;*/ | |||
| top: 0px; | |||
| left: 0px; | |||
| /*background: #ffffff;*/ | |||
| z-index: 20; | |||
| } | |||
| .after-img img, .uploadImg { | |||
| /*position: absolute;*/ | |||
| width: 100%; | |||
| height: 100%; | |||
| object-fit: cover; /*根据父容器的尺寸裁剪和缩放图片,以填充整个容器,并保持长宽比例不变。*/ | |||
| object-position: center; /*指定对象的位置,如果不设置,则默认是center*/ | |||
| background-size: cover; /*背景图片的大小,如果不设置,则默认是auto*/ | |||
| background-position: center; | |||
| top: 50%; | |||
| left: 50%; | |||
| transform: translate(-50%, 0%);/*将元素移动到中心*/ | |||
| border-radius: 50%; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <!-- 注册页面 --> | |||
| <div class="register login"> | |||
| <div class="register_loginTitle "> | |||
| <div class="upload-img-box"> | |||
| <div class="input-box"> | |||
| <input type="file" id="img-file" hidden="hidden" name="选择图片"> | |||
| </div> | |||
| <div class="befor-img"> | |||
| <img class="uploadImg hand" src="/static/img/upload.png" alt=""> | |||
| </div> | |||
| <div class="after-img"> | |||
| <img id="img-box" src="" width="30%" height="auto"> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="register_inputBox"> | |||
| <input class="register_nameInput" name="staffName" type="text" placeholder="员工姓名"> | |||
| <input class="register_phoneInput" name="staffPhone" type="text" placeholder="员工手机号"> | |||
| </div> | |||
| <div class="register_loginButton hand">注册人脸</div> | |||
| </div> | |||
| <script src="/static/js/jquery.min.js"></script> | |||
| <script src="/static/js/msg-box.js"></script> | |||
| <script> | |||
| $(function(){ | |||
| $.MsgBox = { | |||
| Alert: function(title, msg) { | |||
| GenerateHtml("alert", title, msg); | |||
| btnOk(); //alert只是弹出消息,因此没必要用到回调函数callback | |||
| btnNo(); | |||
| }, | |||
| Confirm: function(title, msg, callback) { | |||
| GenerateHtml("confirm", title, msg); | |||
| btnOk(callback); | |||
| btnNo(); | |||
| } | |||
| } | |||
| $(".register_loginButton").on("click",function() { | |||
| var staffPhone = $(".register_phoneInput").val(); | |||
| var staffName = $(".register_nameInput").val(); | |||
| const regex = /^1[3-9]\d{9}$/; | |||
| if (staffPhone == "" || !regex.test(staffPhone)){ | |||
| $.MsgBox.Alert("提示", "手机号为空或不合法,请检查"); | |||
| return; | |||
| } | |||
| if (staffName == ""){ | |||
| $.MsgBox.Alert("提示", "员工姓名为空,请检查"); | |||
| return; | |||
| } | |||
| var img = $("#img-box").attr("src"); | |||
| console.log(img); | |||
| $.ajax({ | |||
| url: "/register", | |||
| type: "post", | |||
| data: { | |||
| staffPhone: staffPhone, | |||
| staffName: staffName, | |||
| avatar: img | |||
| }, | |||
| success: function(data) { | |||
| $.MsgBox.Alert("提示", data.Message); | |||
| }, | |||
| error: function(error) { | |||
| console.log(error); | |||
| $.MsgBox.Alert("提示", error.responseJSON.Message); | |||
| } | |||
| }) | |||
| }) | |||
| $(document).on("click", ".upload-img-box .befor-img,.upload-img-box .after-img", function (e) { | |||
| $(".input-box input").trigger("click"); | |||
| }) | |||
| // 使用input 的change事件,监听当文件上传的值进行改变的时候,才触发事件。 | |||
| $(document).on("change", ".upload-img-box #img-file", function () { | |||
| var file = $("#img-file")[0].files[0]; | |||
| console.log(file); | |||
| // file 内容包括: lastModified , lastModifiedDate , name , size , type | |||
| var formdata = new FormData(); | |||
| formdata.append('file', file); | |||
| if (window.FileReader) { | |||
| var fr = new FileReader(); | |||
| fr.onloadend = function (e) { | |||
| // console.log(this.result); | |||
| // document.getElementById("portrait").src = e.target.result; | |||
| $("#img-box").attr("src",e.target.result) | |||
| $(".upload-img-box .befor-img").addClass("hasImg") | |||
| }; | |||
| //给FileReader对象一个读取完成的方法, | |||
| //使用readAsDataURL会返回一个url | |||
| //这个值就保存在事件对象的result里 | |||
| //img通过加载这个地址,完成图片的加载 | |||
| fr.readAsDataURL(file); | |||
| } | |||
| //判断图片大小 | |||
| if (file.size > 3 * 1024 * 1024) { | |||
| alert("上传图片不能大于3M"); | |||
| } | |||
| //判断图片数据类型 | |||
| if (!/.(gif|jpg|jpeg|png|GIF|JPG|bmp)$/.test(file.name)) { | |||
| alert("图片类型必须是.gif,jpeg,jpg,png,bmp中的一种"); | |||
| } | |||
| }) | |||
| }) | |||
| </script> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,40 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
| <title>Register</title> | |||
| <link rel="stylesheet" href="/static/styles.css"> | |||
| </head> | |||
| <body> | |||
| <h1>Register</h1> | |||
| {{if .Message}} | |||
| <p>{{.Message}}</p> | |||
| {{end}} | |||
| <form action="/register" method="post"> | |||
| <label for="username">姓名:</label> | |||
| <input type="text" id="username" name="username" required><br> | |||
| {{ range $i, $v := .users }} | |||
| <label >{{ $v.Name }}</label> | |||
| {{ end }} | |||
| <label for="phone">电话:</label> | |||
| <input type="phone" id="phone" name="phone" required><br> | |||
| <label for="level">员工类型:</label> | |||
| <select> | |||
| <option value="10">员工</option> | |||
| <option value="71">领导</option> | |||
| </select><br> | |||
| <label for="level">性别:</label> | |||
| <select> | |||
| <option value="1">男</option> | |||
| <option value="2">女</option> | |||
| </select><br> | |||
| <input type="submit" value="Register"> | |||
| </form> | |||
| <a href="/">Home</a> | |||
| </body> | |||
| </html> | |||