From 63bdf7c564d6262e3e9813f96e023f725d4daaae Mon Sep 17 00:00:00 2001 From: nopdan <xci@live.com> Date: Sat, 9 Mar 2024 18:09:26 +0800 Subject: [PATCH] fix: upload file --- build.ps1 | 1 + frontend/components.d.ts | 5 - frontend/src/components/Text.vue | 2 +- internal/serve/assets/tmpl.html | 310 ------------------------------- internal/serve/tmpl.go | 131 ------------- pkg/server/dist/placeholder | 0 pkg/server/race.go | 4 +- pkg/server/serve.go | 201 -------------------- 8 files changed, 4 insertions(+), 650 deletions(-) delete mode 100644 internal/serve/assets/tmpl.html delete mode 100644 internal/serve/tmpl.go delete mode 100644 pkg/server/dist/placeholder delete mode 100644 pkg/server/serve.go diff --git a/build.ps1 b/build.ps1 index 1e34a07..c7e1046 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,6 +3,7 @@ cd frontend bun install bun run build cd .. +rm pkg/server/dist -r xcopy frontend\dist\ pkg\server\dist\ /E /Y go mod tidy diff --git a/frontend/components.d.ts b/frontend/components.d.ts index b082a1d..d3b31b6 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -16,9 +16,7 @@ declare module "@vue/runtime-core" { CollisionDistBar: (typeof import("./src/components/Result/CollisionDistBar.vue"))["default"]; CombsDescription: (typeof import("./src/components/Result/CombsDescription.vue"))["default"]; CombsDistBar: (typeof import("./src/components/Result/CombsDistBar.vue"))["default"]; - Compare: (typeof import("./src/components/Compare.vue"))["default"]; ComparedBars: (typeof import("./src/components/Result/comparedBars.vue"))["default"]; - Data: (typeof import("./src/components/Data.vue"))["default"]; FingerPie: (typeof import("./src/components/Result/FingerPie.vue"))["default"]; FingersDescription: (typeof import("./src/components/Result/FingersDescription.vue"))["default"]; HandComp: (typeof import("./src/components/Result/HandComp.vue"))["default"]; @@ -28,8 +26,6 @@ declare module "@vue/runtime-core" { MultiResult: (typeof import("./src/components/MultiResult.vue"))["default"]; NButton: (typeof import("naive-ui"))["NButton"]; NCard: (typeof import("naive-ui"))["NCard"]; - NDescriptions: (typeof import("naive-ui"))["NDescriptions"]; - NDescriptionsItem: (typeof import("naive-ui"))["NDescriptionsItem"]; NDivider: (typeof import("naive-ui"))["NDivider"]; NDrawer: (typeof import("naive-ui"))["NDrawer"]; NDrawerContent: (typeof import("naive-ui"))["NDrawerContent"]; @@ -41,7 +37,6 @@ declare module "@vue/runtime-core" { NRadio: (typeof import("naive-ui"))["NRadio"]; NRadioGroup: (typeof import("naive-ui"))["NRadioGroup"]; NSelect: (typeof import("naive-ui"))["NSelect"]; - NSpace: (typeof import("naive-ui"))["NSpace"]; NSwitch: (typeof import("naive-ui"))["NSwitch"]; NTag: (typeof import("naive-ui"))["NTag"]; NText: (typeof import("naive-ui"))["NText"]; diff --git a/frontend/src/components/Text.vue b/frontend/src/components/Text.vue index 177699d..06e8a58 100644 --- a/frontend/src/components/Text.vue +++ b/frontend/src/components/Text.vue @@ -48,7 +48,7 @@ const _Type = computed(() => { function tidyPath(path: string) { const index = path.lastIndexOf(props._type); let name = path; - if (index > 0) { + if (index != -1) { name = path.substring(index + 5); } name = name.replace(".txt", ""); diff --git a/internal/serve/assets/tmpl.html b/internal/serve/assets/tmpl.html deleted file mode 100644 index 3a809d1..0000000 --- a/internal/serve/assets/tmpl.html +++ /dev/null @@ -1,310 +0,0 @@ -<!DOCTYPE html> -<html lang=""> - <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>{{.TextName}} 赛码结果</title> - </head> - <style> - body { - background: rgb(240, 244, 245); - } - - #header { - display: flex; - flex-direction: column; - text-align: center; - padding-top: 1rem; - } - - #textFileName { - font-size: x-large; - font-weight: bold; - padding-bottom: 0.5rem; - } - - #content { - display: flex; - justify-content: center; - min-width: fit-content; - text-align: left; - margin: 0.5rem; - } - - .reportCard { - display: flex; - flex-direction: column; - transition: 0.2s; - width: 31rem; - margin: 0.8rem; - padding: 0.8rem; - background-color: #fefefe; - border: none; - border-radius: 10px; - } - - .reportCard:hover { - box-shadow: 0 0 6px 0 #bbb; - } - - .cardBlock.schemaInfo { - display: flex; - align-items: end; - justify-content: start; - font-size: larger; - font-weight: bold; - padding-left: 0.2rem; - } - - .isSingle { - color: red; - padding: 0 1rem; - } - - .dictLen { - padding-left: 1rem; - font-size: 0.8em; - color: lightslategray; - white-space: nowrap; - } - - .dictLenValue { - font-size: 0.7em; - font-weight: normal; - } - - .cardBlock { - padding: 0.5rem 0; - } - - table { - table-layout: fixed; - } - - th { - color: lightslategray; - } - - .tableBasic { - width: 80%; - } - - .keyRateTitle { - text-align: center; - font-weight: bold; - padding-bottom: 0.5rem; - } - - .heatMap { - width: 100%; - border-spacing: 6px 6px; - } - - .key { - padding: 0.1rem; - border-radius: 5px; - box-shadow: 0 0 2px 0 #bbb; - } - - td.key { - text-align: center; - } - - .heatMapRate { - text-align: center; - font-size: 0.5em; - color: rgb(0, 0, 4, 0.4); - } - - .fin { - font-size: smaller; - } - - .finHeatMap td { - white-space: nowrap; - } - - .tableFeel { - width: 100%; - } - - .cardBlock.stat { - display: flex; - align-items: flex-start; - } - - .statHead th { - padding-right: 0.5rem; - white-space: nowrap; - } - - .statTable { - display: flex; - overflow-x: auto; - } - - .statTable > table td { - padding-right: 0.2rem; - } - - #footer { - text-align: center; - } - </style> - - <body> - <div id="header"> - <div id="textFileName">{{.TextName}}</div> - <div id="textLen">文本字数 {{.TextLen}}; 非汉字数 {{.NotHanCount}}</div> - </div> - - <div id="content"> - {{range .Results}} - <div class="reportCard"> - <div class="cardBlock schemaInfo"> - <div class="dictName">{{.DictName}}</div> - {{if .Single}} - <div class="isSingle"> *单 </div> - {{end}} - <div class="dictLen">词条数:</div> - <div class="dictLenValue">{{.DictLen}}</div> - </div> - - <div class="cardBlock"> - <table class="tableBasic"> - <tr> - <th>码长</th> - <th>总键数</th> - <th>上屏数</th> - <th>缺字数</th> - </tr> - <tr> - <td>{{.CodeLen.PerChar | printf "%.3f"}}</td> - <td>{{.CodeLen.Total }}</td> - <td>{{.Basic.Commits }}</td> - <td>{{.Basic.Lacks }}</td> - </tr> - - <tr> - <th>打词数</th> - <th>打词字数</th> - <th>选重数</th> - <th>选重字数</th> - </tr> - <tr> - <td>{{.Words.Commits.Count }}</td> - <td>{{.Words.Chars.Count }}</td> - <td>{{.Collision.Commits.Count }}</td> - <td>{{.Collision.Chars.Count }}</td> - </tr> - <tr> - <td>{{.Words.Commits.Rate | toPer }}</td> - <td>{{.Words.Chars.Rate | toPer }}</td> - <td>{{.Collision.Commits.Rate | toPer }}</td> - <td>{{.Collision.Chars.Rate | toPer }}</td> - </tr> - </table> - </div> - - <div class="cardBlock"> - <table class="tableFeel"> - <tr> - <th>左手</th> - <th>左左</th> - <th>左右</th> - <th>右左</th> - <th>右右</th> - </tr> - <tr> - <td>{{.Hands.Left.Rate | toPer}}</td> - <td>{{.Hands.LL.Rate | toPer}}</td> - <td>{{.Hands.LR.Rate | toPer}}</td> - <td>{{.Hands.RL.Rate | toPer}}</td> - <td>{{.Hands.RR.Rate | toPer}}</td> - </tr> - <tr> - <th>右手</th> - <th>同指</th> - <th>同键</th> - <th>小跨排</th> - <th>大跨排</th> - </tr> - <tr> - <td>{{.Hands.Right.Rate | toPer}}</td> - <td>{{.Fingers.Same.Rate | toPer}}</td> - <td>{{.Combs.DoubleHit.Rate | toPer}}</td> - <td>{{.Combs.SingleSpan.Rate | toPer}}</td> - <td>{{.Combs.MultiSpan.Rate | toPer}}</td> - </tr> - <tr> - <th>当量</th> - <th>异手</th> - <th>异指</th> - <th>错手</th> - <th>小指干扰</th> - </tr> - <tr> - <td>{{.Combs.Equivalent | printf "%.3f"}}</td> - <td>{{.Hands.Diff.Rate | toPer}}</td> - <td>{{.Fingers.Diff.Rate | toPer}}</td> - <td>{{.Combs.LongFingersDisturb.Rate | toPer}}</td> - <td>{{.Combs.LittleFingersDisturb.Rate | toPer}}</td> - </tr> - </table> - </div> - - <div class="cardBlock stat"> - <table class="statHead"> - <tr> - <th>码长</th> - </tr> - <tr> - <th>词长</th> - </tr> - <tr> - <th>选重</th> - </tr> - </table> - <div class="statTable"> - <table> - <tr> - {{range $k,$v := .CodeLen.Dist}} {{ if ne $v 0 }} - <td>{{$k}}:{{$v}}</td> - {{end}} {{end}} - </tr> - <tr> - {{range $k,$v := .Words.Dist}}{{ if ne $v 0 }} - <td>{{$k}}:{{$v}}</td> - {{end}} {{end}} - </tr> - <tr> - {{range $k,$v := .Collision.Dist}}{{ if ne $v 0 }} - <td>{{$k}}:{{$v}}</td> - {{end}} {{end}} - </tr> - </table> - </div> - </div> - - <div class="cardBlock"> - <div class="keyRateTitle">按键频率 %</div> - <table class="heatMap"> - {{range .KeyHeatMap}} - <tr> - {{range .}} {{.}}{{end}} - </tr> - {{end}} - <tr class="finHeatMap"> - {{range .FinHeatMap}} {{.}} {{end}} - </tr> - </table> - </div> - </div> - {{end}} - </div> - <div id="footer"> - <a href="https://github.com/nopdan/gosmq" target="_blank">访问仓库</a> - </div> - </body> -</html> diff --git a/internal/serve/tmpl.go b/internal/serve/tmpl.go deleted file mode 100644 index 8d7799e..0000000 --- a/internal/serve/tmpl.go +++ /dev/null @@ -1,131 +0,0 @@ -package serve - -import ( - "fmt" - "html/template" - "io" - "os" - "strings" - - _ "embed" - - "github.com/nopdan/gosmq/pkg/smq" -) - -//go:embed assets/tmpl.html -var tmpl string - -// 赛码结果 -type Result struct { - smq.Result - KeyHeatMap [][]template.HTML - FinHeatMap [10]template.HTML -} - -// 供模版使用的数据 -type TmplData struct { - TextName string - TextLen int - NotHanCount int - Results []*Result -} - -// 初始化 -func NewHTML() *TmplData { - ret := new(TmplData) - return ret -} - -// 添加一个结果 -func (d *TmplData) AddResult(res *smq.Result) { - - if strings.ContainsRune(res.TextName, '《') { - d.TextName = res.TextName - } else { - d.TextName = "《" + res.TextName + "》" - } - d.TextLen = res.TextLen - d.NotHanCount = res.Basic.NotHanCount - - tmp := new(Result) - tmp.Result = *res - - tmp.genKeyHeatMap() - tmp.genFinHeatMap() - d.Results = append(d.Results, tmp) -} - -// 输出 html 文件 -func (d *TmplData) OutputHTMLFile(fileName string) { - file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) - if err != nil { - panic(err) - } - defer file.Close() - - d.OutputHTML(file) -} - -func (d *TmplData) OutputHTML(w io.Writer) { - funcMap := template.FuncMap{"toPer": toPercentage} - t := template.New("tmpl.html").Funcs(funcMap) - _, err := t.Parse(tmpl) - if err != nil { - panic(err) - } - t.Execute(w, d) -} - -// float64转换成百分数 -func toPercentage(src float64) string { - return fmt.Sprintf("%.2f%%", src*100.0) -} - -// 生成按键热力图 -func (res *Result) genKeyHeatMap() { - keys := res.Keys - max := 0.07 - res.KeyHeatMap = make([][]template.HTML, 4) - line1 := "1234567890 " - line2 := "qwertyuiop " - line3 := "asdfghjkl;'" - line4 := "zxcvbnm,./ " - for j := 0; j < 11; j++ { - res.KeyHeatMap[0] = append(res.KeyHeatMap[0], genKeyHeatCode(keys[string(line1[j])], max, line1[j])) - res.KeyHeatMap[1] = append(res.KeyHeatMap[1], genKeyHeatCode(keys[string(line2[j])], max, line2[j])) - res.KeyHeatMap[2] = append(res.KeyHeatMap[2], genKeyHeatCode(keys[string(line3[j])], max, line3[j])) - res.KeyHeatMap[3] = append(res.KeyHeatMap[3], genKeyHeatCode(keys[string(line4[j])], max, line4[j])) - } -} - -// 按键颜色代码片段 -func genKeyHeatCode(sk *smq.CountRate, max float64, key byte) template.HTML { - if key == ' ' { - return template.HTML("") - } - var freq float64 - if sk != nil { - freq = sk.Rate - } - return template.HTML(fmt.Sprintf( - `<td class="key" style="background-color: rgba(255,0,0,%.4f);">%s <div class="heatMapRate">%.2f</div></td>`, - freq/max*0.6, string(key), freq*100)) -} - -// 生成手指热力图 -func (res *Result) genFinHeatMap() { - src := res.Fingers.Dist - max := 0.25 - fins := []string{"左小", "左无", "左中", "左食", "左拇指", "右拇指", "右食", "右中", "右无", "右小"} - for i := 0; i < 9; i++ { - res.FinHeatMap[i] = genFinHeatCode(src[i+1].Rate, max, fins[i]) - } - res.FinHeatMap[9] = genFinHeatCode(src[0].Rate, max, fins[9]) -} - -// 手指颜色代码片段 -func genFinHeatCode(freq, max float64, fin string) template.HTML { - return template.HTML(fmt.Sprintf( - `<td class="key fin" style="background-color: rgba(0,0,255,%.4f);">%s <div class="heatMapRate">%.2f</div></td>`, - freq/max*0.6, fin, freq*100)) -} diff --git a/pkg/server/dist/placeholder b/pkg/server/dist/placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/server/race.go b/pkg/server/race.go index 4f723ad..c1bc54d 100644 --- a/pkg/server/race.go +++ b/pkg/server/race.go @@ -48,7 +48,7 @@ func (d *Data) Race() []byte { case "local": t.Path = v.Path case "upload": - t.Bytes = fileList[v.Index] + t.Bytes = files[v.Index] case "clipboard": t.String = v.Text default: @@ -64,7 +64,7 @@ func (d *Data) Race() []byte { case "local": t.Path = v.Path case "upload": - t.Bytes = fileList[v.Index] + t.Bytes = files[v.Index] case "clipboard": t.String = v.Text default: diff --git a/pkg/server/serve.go b/pkg/server/serve.go deleted file mode 100644 index daf5f3d..0000000 --- a/pkg/server/serve.go +++ /dev/null @@ -1,201 +0,0 @@ -package server - -import ( - "encoding/json" - "fmt" - "io" - "io/fs" - "log" - "net/http" - "os" - "strconv" - "sync" - "time" -) - -type Options struct { - Text optText `json:"text"` - Dicts []optDict `json:"dicts"` - Web string `json:"web"` -} - -type optText struct { - Name string `json:"name"` - Plain string `json:"plain"` - Path string `json:"path"` - Flag bool `json:"flag"` // 手动输入赛文 -} - -type optDict struct { - Path string `json:"path"` - Single bool `json:"single"` - Space string `json:"space"` - Stable bool `json:"stable"` -} - -func parseOptions(src []byte) Options { - var opt Options - if e := json.Unmarshal(src, &opt); e != nil { - fmt.Println("...Error in Unmarshal: ", e.Error()) - } - return opt -} - -// func toSmqDict(opt optDict) *smq.Dict { -// dict := &smq.Dict{ -// Single: opt.Single, -// Stable: opt.Stable, -// PressSpaceBy: opt.Space, -// } -// dict.Load("dict/" + opt.Path) -// fmt.Println("载入码表:", dict.Name) -// return dict -// } - -// var smqRes []*smq.Result - -// func GetResultJson(src []byte) []byte { -// var opts = parseOptions(src) -// s := &smq.Text{} -// if opts.Text.Flag { -// if opts.Text.Name == "" { -// opts.Text.Name = "赛文" -// } -// s.LoadString(opts.Text.Name, opts.Text.Plain) -// fmt.Println("载入文本:", opts.Text.Name) -// } else { -// s.Load("text/" + opts.Text.Path) -// fmt.Println("载入文本:", s.Name) -// } -// dicts := make([]*smq.Dict, 0) -// for _, v := range opts.Dicts { -// // opt := toSmqDict(v) -// dicts = append(dicts, toSmqDict(v)) -// // s.Add(opt) -// } -// smqRes = s.Race(dicts, false) -// result, _ := json.Marshal(smqRes) -// return result -// } - -func PostHandler(w http.ResponseWriter, r *http.Request) { - setHeader(&w) - defer r.Body.Close() - if r.Method == "POST" { - start := time.Now() - // body, _ := io.ReadAll(r.Body) - // fmt.Println(" post body: ", string(body)) - // rjson := GetResultJson(body) - // w.Write(rjson) - // fmt.Println(" returned json: ", string(rjson)) - fmt.Printf("比赛结束,耗时:%v\n\n", time.Since(start)) - } -} - -// 递归遍历文件夹 -func getFiles(dirname string, pre string) []string { - ret := make([]string, 0) - - fileInfos, err := os.ReadDir(dirname) - if err != nil { - panic(err) - } - for _, fi := range fileInfos { - if fi.IsDir() { - //继续遍历fi这个目录 - tmp := getFiles(dirname+"/"+fi.Name(), fi.Name()+"/") - ret = append(ret, tmp...) - } else { - ret = append(ret, pre+fi.Name()) - } - } - return ret -} - -func Serve2(port string, silent bool) { - dist, _ := fs.Sub(dist, "dist") - http.Handle("/", http.FileServer(http.FS(dist))) - http.HandleFunc("/race", RaceHandler) - http.HandleFunc("/upload", UploadHandler) - http.HandleFunc("/file_index", IndexHandler) - http.HandleFunc("/api", PostHandler) - http.HandleFunc("/texts", func(w http.ResponseWriter, r *http.Request) { - setHeader(&w) - b, _ := json.Marshal(getFiles(`text/`, "")) - w.Write(b) - }) - http.HandleFunc("/dicts", func(w http.ResponseWriter, r *http.Request) { - setHeader(&w) - b, _ := json.Marshal(getFiles(`dict/`, "")) - w.Write(b) - }) - - http.HandleFunc("/result", func(w http.ResponseWriter, r *http.Request) { - setHeader(&w) - // if len(smqRes) == 0 { - // return - // } - // h := NewHTML() - // for _, v := range smqRes { - // h.AddResult(v) - // } - // h.OutputHTML(w) - }) - - url := "http://localhost:" + port - var wg sync.WaitGroup - wg.Add(1) - go func() { - fmt.Println("Listen and serve: ", url) - err := http.ListenAndServe(":"+port, nil) - if err != nil { - fmt.Println(err) - panic("...Server failed.") - } - wg.Done() - }() - if !silent { - // openBrowser(url) - } - wg.Wait() -} - -func RaceHandler(w http.ResponseWriter, r *http.Request) { - setHeader(&w) - text := r.FormValue("text") - dict := r.FormValue("dict") - fmt.Printf(" text: %v\n dict: %v\n", text, dict) - w.Write([]byte("{\"hello\": \"world\"}")) -} - -var fileList = make([][]byte, 0, 1) - -func UploadHandler(w http.ResponseWriter, r *http.Request) { - setHeader(&w) - r.ParseMultipartForm(1024 * 1024 * 1024) // 最大 1GB - fmt.Println(r.Form) - - file, handler, err := r.FormFile("file") - if err != nil { - log.Printf("err: %v\n", err) - return - } - defer file.Close() - log.Printf("POST:%v", handler.Header) - - mbData, err := io.ReadAll(file) - if err != nil { - log.Printf("err: %v\n", err) - return - } - fileList = append(fileList, mbData) - // fmt.Println(string(mbData)) - index := len(fileList) - 1 - w.Write([]byte("{'index': " + strconv.Itoa(index) + "}")) -} - -func IndexHandler(w http.ResponseWriter, r *http.Request) { - setHeader(&w) - index := len(fileList) - w.Write([]byte("{\"index\": " + strconv.Itoa(index) + "}")) -}