Go语言(Gin框架)实现传统文化读书软件

一、用GO语言实现

 

 

 

二、源代码公开,欢迎修改转载使用。

  1. https://pan.baidu.com/s/1NW_Am0WsjwrTtcfZKVTkOg 提取码: ptaw 

  2. 下载地址:https://files.cnblogs.com/files/javatiandi/DuRenZhou.rar

  3.  演示地址: http://106.12.161.76:8089/boat/index

 

  部分源代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main
 
import (
    "DuRenZhou/src/conf"
    "DuRenZhou/src/controllers"
    "github.com/gin-gonic/gin"
    "net/http"
    "strconv"
)
 
func main() {
 
    router := gin.Default()
    router.StaticFS("/static", http.Dir("src/static"))
    router.LoadHTMLGlob("src/views/book/*")
    router.NoRoute(controllers.Handle404)
    router.StaticFile("/favicon.ico", "src/static/favicon/favicon.ico")
 
    //渡人舟
    boat := router.Group("/boat")
    {
        boat.GET("/index", controllers.BoatBookMainIndex)
        boat.POST("/clientInit", controllers.BoatClientInit)
        boat.POST("/client", controllers.BoatClient)
        boat.POST("/mark", controllers.BoatBookMark)
    }
 
    sererPort := conf.AppConf.ServerConfig.Port
    port := strconv.Itoa(sererPort)
    router.Run(":" + port)
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
package controllers
 
import (
    "DuRenZhou/src/conf"
    "fmt"
    "github.com/axgle/mahonia"
    "github.com/gin-gonic/gin"
    "io"
    "io/ioutil"
    "log"
    "math"
    "net/http"
    "os"
    "strconv"
    "strings"
    "sync"
    "time"
    "unicode/utf8"
)
 
type PageContent struct {
    BookId       string
    BookName     string
    BookRealPath string
    PWordsCount  int
    PContent     string
    ClientWidth  int
    ClientHeight int
    FontSize     int
    OriginalX    int
    OriginalY    int
    PageHeight   int
    LineSize     int
    MaxLetters   int
    PageNum      int
    TotalPage    int
    JumpMark     string
    ReadType     int
    JumpPageNum  int
}
 
type BookDetail struct {
    conf.Book
    JumpMark    string
    JumpPageNum int
    ReadType    int
}
 
var once sync.Once
var contentMap = make(map[string]string, 100)
var bookList = make([]conf.Book, 0)
 
func init() {
    once.Do(func() {
        initResources()
    })
}
 
func GetResourcesPath() (string, error) {
    getwd, err := os.Getwd()
    if err != nil {
        panic("初始化资源路径出错!")
        os.Exit(2)
    }
    resources := getwd + "\\src\\resources"
    //fmt.Println(resources)
    return resources, err
}
 
func listResources() []string {
    list := make([]string, 0)
    resourcesPath, err := GetResourcesPath()
    if err != nil {
        panic("初始化资源路径出错!")
        os.Exit(2)
    }
    dir, err := ioutil.ReadDir(resourcesPath)
    if err != nil {
        panic("初始化资源路径出错!")
        os.Exit(2)
    }
    for _, file := range dir {
        if file.IsDir() {
            continue
        } else {
            if strings.Contains(file.Name(), "jumpMark") {
                continue
            } else {
                list = append(list, file.Name())
            }
        }
    }
    return list
}
func initResources() {
    resourcesPath, err := GetResourcesPath()
    if err != nil {
        panic("初始化资源路径出错!")
        os.Exit(2)
    }
    resources := listResources()
    list := conf.AppConf.BookConfig.BookList
    for _, book := range list {
        for _, name := range resources {
            fmt.Println("书名:", name)
            if name == (book.BookName + ".txt") {
                book.RealPath = resourcesPath + "\\" + name
                fmt.Printf("书籍名称:%+v \n", book)
                bookList = append(bookList, book)
            }
        }
    }
}
 
func calcFontSize(deviceWidth int) int {
    /**
    321px <= device-width <= 375px,font-size:11px        --->  .item的width:34px
    376px <= device-width <= 414px,font-size:12px        --->  .item的width:37.4px
    415px <= device-width <= 639px,font-size:15px        --->  .item的width:40.8px
    640px <= device-width <= 719px,font-size:20px        --->  .item的width:51px
    720px <= device-width <= 749px,font-size:22.5px      --->  .item的width:76.5px
    750px <= device-width <= 799px,font-size:23.5px      --->  .item的width:79.8999999px
    800px <= device-width         ,font-size:25px        --->  .item的width:85px
    */
 
    fontSize := 25
    switch {
    case deviceWidth <= 375:
        fontSize = 11
        break
    case deviceWidth <= 414:
        fontSize = 12
        break
    case deviceWidth <= 639:
        fontSize = 15
        break
    case deviceWidth <= 719:
        fontSize = 20
        break
    case deviceWidth <= 749:
        fontSize = 22
        break
    case deviceWidth <= 799:
        fontSize = 23
        break
    case deviceWidth > 799:
        fontSize = 25
        break
    default:
        fontSize = 25
    }
    fmt.Println(fontSize)
    return fontSize
}
 
func calcLineSize(pageHeight int, fontSize int) int {
    lineSize := 0
    tmp1 := float64(fontSize) * 1.5
    tmp2 := float64(pageHeight) / tmp1
    lineSize = int(math.Floor(tmp2))
    return lineSize
}
 
func BoatBookMainIndex(c *gin.Context) {
    list := bookList
    bookDetailList := make([]BookDetail, 0)
    for _, d := range list {
        //fmt.Println(d)
        bookDetail := BookDetail{}
        bookDetail.BookId = d.BookId
        bookDetail.BookName = d.BookName
        bookDetail.RealPath = d.RealPath
        //加载书签文件
        jumpMarkPath := strings.ReplaceAll(bookDetail.RealPath, ".txt", "_jumpMark.txt")
        b := exists(jumpMarkPath)
        if b {
            jumpMarkGBK := ReadFile(jumpMarkPath)
            dec := mahonia.NewDecoder("gbk")
            jumpMarkUTF8 := dec.ConvertString(jumpMarkGBK)
            //  fmt.Println("jumpMarkUTF8:", jumpMarkUTF8)
            bookDetail.JumpMark = jumpMarkUTF8
        }
        bookDetailList = append(bookDetailList, bookDetail)
    }
    data := make(map[string][]BookDetail, 1)
    data["bookDetailList"] = bookDetailList
    c.HTML(200, "mainIndex.tmpl", data)
}
 
// 判断所给路径文件/文件夹是否存在
func exists(path string) bool {
    _, err := os.Stat(path) //os.Stat获取文件信息
    if err != nil {
        if os.IsExist(err) {
            return true
        }
        return false
    }
    return true
}
 
func BoatClientInit(c *gin.Context) {
    bookId := c.PostForm("bookId")
    readTypeStr := c.PostForm("readType")
    bookName := c.PostForm("bookName")
    jumpMark := c.PostForm("jumpMark")
    jumpPageNumStr := c.PostForm("jumpPageNum")
    fmt.Println("书名:", bookName, "BookId:", bookId, "readType:", readTypeStr)
    //fmt.Println("书签:", jumpMark)
    //fmt.Println("跳转到:", jumpPageNumStr)
    book := conf.Book{}
    list := bookList
    for _, d := range list {
        if string(d.BookName) == bookName {
            book.BookId = d.BookId
            book.BookName = d.BookName
            book.RealPath = d.RealPath
            break
        }
    }
    bookDetail := BookDetail{}
    bookDetail.BookId = book.BookId
    bookDetail.BookName = book.BookName
    bookDetail.RealPath = book.RealPath
    bookDetail.JumpMark = jumpMark
    jumpPageNum, err := strconv.Atoi(jumpPageNumStr)
    if err != nil {
        jumpPageNum = 1
    }
    bookDetail.JumpPageNum = jumpPageNum
    bookDetail.JumpMark = jumpMark
    readType, err := strconv.Atoi(readTypeStr)
    if err != nil {
        readType = 0
    }
    bookDetail.ReadType = readType
    c.HTML(200, "client.tmpl", bookDetail)
}
 
func BoatClient(c *gin.Context) {
    pageContent := initPageContent(c)
    //fmt.Printf("pageContent %+v\n", *pageContent)
    var bookContentUTF8 string = ""
    if _, ok := contentMap[pageContent.BookName]; ok {
        bookContentUTF8 = contentMap[pageContent.BookName]
    } else {
        bookContentGBK := ReadFile(pageContent.BookRealPath)
        dec := mahonia.NewDecoder("gbk")
        bookContentUTF8 = dec.ConvertString(bookContentGBK)
        //去除任意的空白符
        //reg := regexp.MustCompile(`[\s]+`)
        //bookContentUTF8 = reg.ReplaceAllString(bookContentUTF8, "")
        contentMap[pageContent.BookName] = bookContentUTF8
    }
 
    if pageContent.ReadType == 1 {
        //jumpMark 处开始阅读
        index := findZhongWenSubStrIndex(bookContentUTF8, pageContent.JumpMark)
        if index == -1 {
            pageContent.PageNum = 1
        } else {
            //根据index 设置PageNum
            if index%pageContent.MaxLetters == 0 {
                pageNum := index / pageContent.MaxLetters
                if pageNum == 0 {
                    pageContent.PageNum = 1
                } else {
                    pageContent.PageNum = pageNum
                }
            } else {
                pageContent.PageNum = index/pageContent.MaxLetters + 1
            }
        }
    } else if pageContent.ReadType == 2 {
        //jumpPageNumStr 跳转到第 jumpPageNum页开始阅读
        pageContent.PageNum = pageContent.JumpPageNum
    } else if pageContent.ReadType == 3 {
        //readType 3   直接从头开始阅读
        pageContent.PageNum = 1
    }
 
    totalWords := utf8.RuneCountInString(bookContentUTF8)
    if totalWords%pageContent.MaxLetters == 0 {
        pageContent.TotalPage = totalWords / pageContent.MaxLetters
    } else {
        pageContent.TotalPage = totalWords/pageContent.MaxLetters + 1
    }
    if pageContent.PageNum > pageContent.TotalPage {
        c.HTML(http.StatusOK, "pagefinish.tmpl", nil)
        return
    }
    firstIndex := (pageContent.PageNum - 1) * pageContent.MaxLetters
    lastIndex := pageContent.PageNum * pageContent.MaxLetters
    if lastIndex+1 >= totalWords {
        lastIndex = totalWords - 1
    }
    runes := []rune(bookContentUTF8)
    content := string(runes[firstIndex:lastIndex])
    pageContent.PContent = content
    //fmt.Println("本页长度:", utf8.RuneCountInString(pageContent.PContent), "完整切割:本页内容")
    //fmt.Println(pageContent.PContent)
    c.HTML(http.StatusOK, "page.tmpl", pageContent)
}
 
func BoatBookMark(c *gin.Context) {
    jumpMark := c.PostForm("jumpMark")
    bookId := c.PostForm("bookId")
    book := conf.Book{}
    list := bookList
    for _, d := range list {
        if string(d.BookId) == bookId {
            book.BookId = d.BookId
            book.BookName = d.BookName
            book.RealPath = d.RealPath
            break
        }
    }
    //加载书签文件
    jumpMarkPath := strings.ReplaceAll(book.RealPath, ".txt", "_jumpMark.txt")
    WriteFileGBK(jumpMarkPath, jumpMark)
    c.JSON(200, gin.H{"message": "保留书签成功!"})
}
 
func WriteFileGBK(filePath string, content string) {
    /*
       O_RDONLY int = syscall.O_RDONLY // 只读打开文件和os.Open()同义
       O_WRONLY int = syscall.O_WRONLY // 只写打开文件
       O_RDWR   int = syscall.O_RDWR   // 读写方式打开文件
       O_APPEND int = syscall.O_APPEND // 当写的时候使用追加模式到文件末尾
       O_CREATE int = syscall.O_CREAT  // 如果文件不存在,此案创建
       O_EXCL   int = syscall.O_EXCL   // 和O_CREATE一起使用, 只有当文件不存在时才创建
       O_SYNC   int = syscall.O_SYNC   // 以同步I/O方式打开文件,直接写入硬盘.
       O_TRUNC  int = syscall.O_TRUNC  // 如果可以的话,当打开文件时先清空文件
    */
 
    _, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    enc := mahonia.NewEncoder("gbk")
    convertString := enc.ConvertString(content)
    bytes := []byte(convertString)
    ioutil.WriteFile(filePath, bytes, 0644)
}
 
func findZhongWenSubStrIndex(str string, substr string) int {
    if strings.Contains(str, substr) {
        runes := []rune(str)
        firstIndex := 0
        totalWords := utf8.RuneCountInString(str)
        for {
            lastIndex := firstIndex + utf8.RuneCountInString(substr)
            content := string(runes[firstIndex:lastIndex])
            if content == substr {
                break
            }
            if lastIndex > totalWords-1 {
                break
            }
            firstIndex++
        }
        return firstIndex
    } else {
        return -1
    }
}
 
func ReadFile(filePath string) string {
    strBuilder := strings.Builder{}
    f, err := os.Open(filePath)
    if err != nil {
        fmt.Println("can't opened this file")
    }
    defer f.Close()
    b := make([]byte, 1024)
    var counter int = 0
    for {
        read, err := f.Read(b)
        if err != nil && err != io.EOF {
            log.Println("读文章出错!", err)
            strBuilder.WriteString("")
            break
        }
        if err == io.EOF {
            strBuilder.WriteString("")
            break
        }
        s := string(b[0:read])
        strBuilder.WriteString(s)
        counter++
    }
    //解决文件读取比较慢,获取内容跟不上下面的分页程序的问题
    var str string
    var spinningCounter int = 0
    for {
        time.Sleep(200 * time.Millisecond)
        if counter == 0 {
            str = ""
            break
        }
        str = strBuilder.String()
        if len(str) > 1 {
            break
        }
        if spinningCounter > 200 {
            break
        }
        spinningCounter++
    }
    return str
}
 
func initPageContent(c *gin.Context) *PageContent {
    cWidth := c.PostForm("cWidth")
    clientWidth, _ := strconv.Atoi(cWidth)
 
    cHeight := c.PostForm("cHeight")
    clientHeight, _ := strconv.Atoi(cHeight)
 
    fontSize := calcFontSize(clientWidth)
    pageHeight := clientHeight - fontSize*5
    lineSize := calcLineSize(pageHeight, fontSize)
    originalX := clientWidth - fontSize*4
    originalY := fontSize * 3
 
    bookId := c.PostForm("bookId")
    pageNumStr := c.PostForm("pageNum")
    pageNum, err := strconv.Atoi(pageNumStr)
    if err != nil {
        pageNum = 1
    }
    maxLettersStr := c.PostForm("maxLetters")
    maxLetters, err := strconv.Atoi(maxLettersStr)
    if err != nil {
        maxLetters = ((originalX - fontSize*2) / (fontSize * 2)) * lineSize
        maxLetters = (lineSize - maxLetters%lineSize) + maxLetters
    }
    book := findByBookId(bookId)
    pageContent := &PageContent{
        BookId:       bookId,
        BookName:     book.BookName,
        BookRealPath: book.RealPath,
        PWordsCount:  0,
        PContent:     "",
        ClientWidth:  clientWidth,
        ClientHeight: clientHeight,
        FontSize:     fontSize,
        OriginalX:    originalX,
        OriginalY:    originalY,
        PageHeight:   pageHeight,
        LineSize:     lineSize,
        MaxLetters:   maxLetters,
        PageNum:      pageNum,
        TotalPage:    0,
    }
 
    //处理阅读书签
    readTypeStr := c.PostForm("readType")
    jumpMark := c.PostForm("jumpMark")
    jumpPageNumStr := c.PostForm("jumpPageNum")
    readType, err := strconv.Atoi(readTypeStr)
    jumpPageNum, err := strconv.Atoi(jumpPageNumStr)
    pageContent.JumpMark = jumpMark
    if err != nil {
        readType = 0
    }
    pageContent.ReadType = readType
    if err != nil {
        jumpPageNum = 1
    }
    pageContent.JumpPageNum = jumpPageNum
    return pageContent
}
 
func findByBookId(bookId string) conf.Book {
    book := conf.Book{}
    list := bookList
    for _, d := range list {
        if string(d.BookId) == bookId {
            book.BookId = bookId
            book.BookName = d.BookName
            book.RealPath = d.RealPath
            break
        }
    }
    return book
}
 
/***
GBK 1个字占 2个字节 2byte
1MB = 1024 kb
1kb = 1024 b
 
所以 1MB 的书可以装多少字
1kb = 512 个字
1MB 可以装  1024 * 512 = 524,288  52 万字
100MB 可以装   52 * 100 = 5200 万字
 
 
*/

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
<!DOCTYPE >
<html>
    <head>
        <title>传统文化书单</title>
    </head>
    <style>
        html,
        body {
            width: 100%;
            height: 100%;
            margin: 0px;
            background: url(/static/book/img/page.png) repeat-x;
             
        }
        .main{
            padding-top: 100px;
            width: 100%;
            height: 100%;
            overflow: hidden;
            height: 1800px;
        }
        .row{
            padding-top: 20px;
            padding-left: 10%;
            width: 100%;
            text-align: center;
              
        }
        .book{
            background: url(/static/book/img/book.png) no-repeat center;
            margin: 10px;
            display: inline-block;
            width: 300px ;
            height: 180px;
            padding-top: 10px;
            font-size: 40px;
            float: left;
            cursor:pointer;
            cursor: hand;
        }
        .clearFloat{
            clear: both;
        }
        .zi  {       
 
            margin-top: 30px;
            margin-left: 108px;
            line-height: 40px;
            writing-mode: vertical-rl;
            -webkit-writing-mode: vertical-rl;
            -ms-writing-mode: vertical-rl;
            font-size: 14px;
            font-family: '楷体';
            float: left;
        }
        .menu{
            font-size:12px ;
            background: #DDDDDD ;
            opacity: 0.9;
            width: 200px;
            text-align: left;
            visibility: hidden;
        }
        .menu ul{
            padding:2px;
            line-height: 30px;
            list-style: none;
            cursor: hand;
            cursor: pointer;
        }
        ul li:hover {
            background: #32CD32;
        }
        .jump{
            width: 20px;
        }
         
    </style>
    <body>
        <div class="main">
            <div class="row">
              {{range .bookDetailList}}
                    <div class="book">
                        <div class="zi"  myBookId="{{.BookId}}"  myJumpMark="{{.JumpMark}}" >{{.BookName}}</div>
                    </div>
              {{end}}
            </div>
            <div class="clearFloat"></div>
            <div id="menu" class="menu"  >
                <ul>
                    <li id="startReadLi" ><a>开始阅读</a></li>
                    <li id="jumpMarkLi"  >跳转到标签:<a id="jumpMarkText">孝者天之经地之义也...</a></li>
                    <li>跳转到第<input type="text" id="jumpInput"  onclick="null"  onbeforepaste="jumpPaste()"   onchange="jumpChange(this)"    class="jump" value="1">页<a id="jumpPageNumLi" >【GO】</a></li>
                </ul>
            </div>
            <div class="form">
                <form id="form1" method="post" action="/boat/clientInit">
                    <input type="hidden" id="bookName" name="bookName" value="" />
                    <input type="hidden" id="bookId"  name="bookId" value="" />
                    <input type="hidden" id="jumpMark" name="jumpMark" value=""  />
                    <input type="hidden" id="jumpPageNum"  name="jumpPageNum" value="1" />
                    <input type="hidden" id="readType" name="readType"  value="3" />
                </form>
            </div>
        </div>
 
    </body>
    <script>
        var mousePosition = {};
        function mouseMove(ev) {
            Ev = ev || window.event;
            var mousePos = mouseCoords(ev);
            mousePosition['x'] = mousePos.x;
            mousePosition['y'] = mousePos.y;
        }
        function mouseCoords(ev) {
            if (ev.pageX || ev.pageY) {
                return {
                    x: ev.pageX,
                    y: ev.pageY
                };
            }
            return {
                x: ev.clientX + document.body.scrollLeft - document.body.clientLeft,
                y: ev.clientY + document.body.scrollTop - document.body.clientTop
            };
        }
        document.onmousemove = mouseMove;
    </script>
 
 
    <script>
        var timeout ;
        var menu={
            bookId:"",
            bookName:"",
            jumpMark:"",
            jumpPageNum:0,
            readType:0
        }; 
        function showMenu(objBook) {
            var menuDiv =document.getElementById("menu");
            menuDiv.style.visibility = "visible";
            menuDiv.style.left = mousePosition.x + 'px'
            menuDiv.style.top = mousePosition.y + 'px'
            menuDiv.style.position='absolute';
            var jumpMarkText = document.getElementById("jumpMarkText");
 
            var mark = objBook.jumpMark;
            mark = mark.replace(/\s*/g,"")
            jumpMarkText.innerText = mark;
 
            clearTimeout(timeout);
            hiddenTimeOut(menuDiv,20000);
            menu.bookId = objBook.bookId;
            menu.bookName = objBook.bookName;
            menu.jumpMark = objBook.jumpMark;
 
        }
         
        function hiddenTimeOut(obj,milliseconds){
           timeout = window.setTimeout(function(){
                if(obj.style.visibility = "visible"){
                    obj.style.visibility = "hidden";
                }
            },milliseconds);
        }
         
        function submitRead(thisMenu){
            document.getElementById("bookId").value = thisMenu.bookId;
            document.getElementById("bookName").value = thisMenu.bookName;
            document.getElementById("jumpMark").value = thisMenu.jumpMark;
            try{
              var thisJumpPageNum = parseInt(thisMenu.jumpPageNum);
              document.getElementById("jumpPageNum").value = thisJumpPageNum;
              if((thisJumpPageNum<=0) && (thisMenu.readType==2)){
                 alert("请输入正确的页码!");
                 return
              }
            }catch(e){
              alert("请输入正确的页码!");
            }
            document.getElementById("readType").value = thisMenu.readType;
            document.getElementById("form1").submit();
        }
 
        function jumpPaste(){
            window.clipboardData.setData('text',clipboardData.getData('text').replace(/^[0-9]*$/g,''));
        }
 
 
        function jumpChange(obj){
            if(!/^[0-9]*$/.test(obj.value)){
               obj.value='';
               alert('请输入正确的页码!');
           }
        }
 
         
        window.onload = function() {
            document.getElementById("startReadLi").onclick = function(event){
                menu.readType = 3;
                submitRead(menu);
                event.stopPropagation();
            }
            document.getElementById("jumpMarkLi").onclick = function(event){
                menu.readType = 1;
                submitRead(menu);
                event.stopPropagation();
            }
            document.getElementById("jumpPageNumLi").onclick = function(event){
                menu.readType = 2;
                menu.jumpPageNum = document.getElementById("jumpInput").value;
                submitRead(menu);
                event.stopPropagation();
            }
            var books = document.getElementsByClassName("book");
            for (var i = 0; i < books.length; i++) {
                var book = books[i];
                book.onclick = function() {
                     var myBookId = this.children[0].getAttribute("myBookId");
                     var myBookName = this.children[0].innerText;
                     var myJumpMark = this.children[0].getAttribute("myJumpMark");
                     var bookQuery = {
                         bookId : myBookId,
                         bookName : myBookName,
                         jumpMark : myJumpMark
                     };
 
                     showMenu(bookQuery);
                }
            }
 
        }
    </script>
 
</html>

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
<!DOCTYPE html>
<html>
    <head lang="en">
        <style>
            html,
            body {
                width: 100%;
                height: 100%;
                margin: 0px;
                /* overflow: hidden; */
                /* background-image: linear-gradient(#BEBEBE,#778899) ; */
                background: url(/static/book/img/page.png) repeat-x;
                cursor:pointer;
                cursor: hand;
            }
            .main{
                text-align: center;
                padding: 10px;
            }
            .page{
                 
            }
            .pageFoot{
                width: 100%;
                padding-top: 10px;
                padding-bottom: 10px ;
                /* height: 40px; */
                /* border: 1px solid red;*/
                text-align: center;
            }
            .prev{
                border: 1px solid #008080;
                background:#E6E6FA;
                padding-top: 5px;
                padding-bottom: 5px;
                padding-left: 60px;
                padding-right: 60px;
                border-radius: 2px;
                text-decoration: none;
                /* font-size: 20px; */
            }
            .next{
                border: 1px solid #008080;
                background: #F0F8FF;
                padding-top: 5px;
                padding-bottom: 5px;
                padding-left: 60px;
                padding-right: 60px;
                border-radius: 2px; 
                text-decoration: none;
                /* font-size: 20px; */
            
            .current {
                border: 1px solid #008080;
                background: #54FF9F;
                padding-top: 5px;
                padding-bottom: 5px;
                padding-left: 60px;
                padding-right: 60px;
                border-radius: 2px;
                text-decoration: none;
            }
            .helpWin{
                border: 1px solid #008080;
                background: #7FFF00;
                padding-top: 5px;
                padding-bottom: 5px;
                padding-left: 60px;
                padding-right: 60px;
                border-radius: 2px;
                text-decoration: none;
            }
            .mark{
                border: 1px solid #008080;
                background: #7FFF00;
                padding-top: 5px;
                padding-bottom: 5px;
                padding-left: 60px;
                padding-right: 60px;
                border-radius: 2px;
                text-decoration: none;
            }
            .backMain{
                border: 1px solid #008080;
                background: #7FFF00;
                padding-top: 5px;
                padding-bottom: 5px;
                padding-left: 60px;
                padding-right: 60px;
                border-radius: 2px;
                text-decoration: none;
            }
            a:hover{ 
                background: limegreen;
                text-decoration: none;
            }
            .jumpBox{
                border: 1px solid #008080;
                background: #7FFF00;
                padding-left: 10px;
                padding-right: 10px;
                border-radius: 2px;
                padding-top: 5px;
                padding-bottom: 5px;
            }
            .jump{
                display: inline-block;
                border: 1px solid #008080;
                border-bottom: 1px solid #000000 ;
                height: 25px;
                margin-bottom: 20x;
                border-radius: 2px;
                width: 40px;
                text-align: center;
            }
 
 
            .searchTxt{
                margin: 0;
                padding: 0;
                height: 30px;
            }
            .searchTxt input{
                display: inline-block;
                height: 25px;
                width: 92%;
                float: left;
            }
             .searchTxt .booklogo{
                    display: inline-block;
                    height: 30px;
                    border-radius: 2px ;
                    float: left;
 
             }
             .searchTxt .searchlogo{
                 display: inline-block;
                 height: 30px;
                 background: #7FFF00;
                 border: 1px solid #DDDDDD;
                 border-radius: 2px ;
                 float: left;
             }
        </style>
        <meta charset="UTF-8">
        <title></title>
 
    </head>
    <body>
        <div class="main"  >
            <canvas id='canvas' class="page" ></canvas>
        </div>
        <div class="searchTxt">
            <img class="booklogo" src="/static/book/img/booklittle.png" />
            <input type="text" id="searchTxt"   name="searchTxt"   value="" />
            <img class="searchlogo"  onclick="doSearch()"   src="/static/book/img/search.png" />
        </div>
        <div class="pageFoot">
            <a class="prev" onclick="prev()" ><</a>
            <a class="current" onclick="current()" >刷新</a>
            <a class="next"  onclick="next()" >></a>
            <a class="helpWin"  onclick="doHelp()" href="javascript:void(0);">帮助</a>
            <a class="mark"  onclick="mark()" href="javascript:void(0);">保存书签</a>
            <a class="backMain"  onclick="backMain()" href="javascript:void(0);">返回主页</a>
            <a class="jumpBox" onclick="jump()" >共{{.TotalPage}}页,当前第{{.PageNum}}页,转到<input type="text" id="jumpPageNum"  onclick="null"  onbeforepaste="jumpPaste()"   onchange="jumpChange(this)"   class="jump" />页</a>
        </div>
 
        <div>
            <form id="form1" method="post" action="/boat/client" >
                <input type="hidden" id="bookId" name="bookId" value="{{.BookId}}" />
                <input type="hidden" id="pageNum" name="pageNum" value="{{.PageNum}}" />
                <input type="hidden" id="maxLetters" name="maxLetters" value="{{.MaxLetters}}" />
                <input type="hidden" id="cWidth" name="cWidth" value="{{.ClientWidth}}" />
                <input type="hidden" id="cHeight" name="cHeight" value="{{.ClientHeight}}" />
            </form>
        </div>
 
        <script>
      var mousePosition = {};
            function mouseMove(ev) {
                Ev = ev || window.event;
                var mousePos = mouseCoords(ev);
                mousePosition['x'] = mousePos.x;
                mousePosition['y'] = mousePos.y;
                inputSearchText(mousePos.x, mousePos.y);
            }
 
            function mouseCoords(ev) {
                if (ev.pageX || ev.pageY) {
                    return {
                        x: ev.pageX,
                        y: ev.pageY
                    };
                }
                return {
                    x: ev.clientX + document.body.scrollLeft - document.body.clientLeft,
                    y: ev.clientY + document.body.scrollTop - document.body.clientTop
                };
            }
            document.onmousemove = mouseMove;
        </script>
 
        <script>
            var bookId = {{.BookId}}
            var fontSize = {{.FontSize}};
            var orginalX = {{.OriginalX}};
            var orginalY = {{.OriginalY}};
            var pageHeight = {{.PageHeight}};
            var lineSize =  {{.LineSize}};
            var position = {
                x: orginalX,
                y: orginalY
            };
            var article = ' '+'{{.PContent}}';
            var canvas;
            var context;
            var maxLetters = {{.MaxLetters}} ;
 
            function init() {
                canvas = document.getElementById('canvas');
                context = canvas.getContext('2d');
                canvas.width =  {{.ClientWidth}};
                canvas.height = {{.ClientHeight}};
                drawWenZhang();
                window.onresize = function(ev) {
                    var oEvent = event || ev;
                    canvas.width =  {{.ClientWidth}};
                    canvas.height = {{.ClientHeight}};
                      
                }
            }
 
        var contentAry = [];
            var positionAry = [];
            var index = 0;
 
            function inputSearchText(mouseX,mouseY) {
                if(inMaxMinArea(mouseX,mouseY)){
                    for (var i = 0; i < positionAry.length; i++) {
                        var p = positionAry[i];
                        var AryX = p.split("##");
                        if (mouseX > parseFloat(AryX[0]) && mouseX < parseFloat(AryX[1])) {
                            index = i+1;
                            break;
                        }
                    }
                 if(index<contentAry.length+1){
                      document.getElementById("searchTxt").value = contentAry[index];
                 }
                }
            }
 
            function inMaxMinArea(posX,posY){
                 var maxY = pageHeight;
                 if(posY>=parseFloat(maxY)){
                    return false;
                 }
                if(positionAry.length>1){
                    minX = positionAry[positionAry.length-1].split("##")[0];
                    maxX = positionAry[0].split("##")[1]
                    if(posX>=parseFloat(minX) && posX<=parseFloat(maxX)){
                        return true;
                    }
                }
                return false;
            }
 
 
        function doSearch(){
               var searchTxt = document.getElementById("searchTxt").value;
                 window.open("https://www.baidu.com/s?wd="+ searchTxt);
         }
 
 
 
           function drawWhiteSpace(){
              context.font = fontSize + "px Georgia";
              context.save();
              context.translate(position.x, position.y);
              context.fillText("  ", 0, 0);
              context.restore();
              position.y = position.y + fontSize * 1.5;
           }
            var strTemp = "";
            var lastStrTemp = "";
            function drawWenZhang() {
                //drawWhiteSpace();
                var cnt = 0;
                for (var i = 0; i < article.length; i++) {
                    context.font = fontSize + "px Georgia";
                    context.save();
                    if(i==0){
                        context.translate(position.x,position.y+fontSize * 1.5);
                    }else{
                       context.translate(position.x,position.y);
                    }
                    var ch = undefined ;
                    try{
                      ch = article[i];
                      if(cnt>(article.length -  lineSize)){
                          lastStrTemp = lastStrTemp + article[i];
                      }
                    }catch(e){
                       context.restore();
                       break;
                    }
                    if(ch == undefined){
                       context.fillText(' ', 0, 0);
                       context.restore();
                       break;
                    }else{
                       strTemp = strTemp + ch;
                       context.fillText(ch, 0, 0);
                       context.restore();
                    }
                    if (cnt == article.length-1){
                        break;
                    }
                    if (cnt % lineSize == 0) {
                        contentAry.push(strTemp);
                        strTemp = "";
                        context.save();
                        var flag = 0;
                        if(cnt>0){
                           flag=1;
                        }
                        position.x = position.x - fontSize * 2*flag;
                        position.y = orginalY ;
                        context.moveTo(position.x - ((fontSize*2 - fontSize)/2.0) , orginalY);
                        context.lineTo(position.x - ((fontSize*2 - fontSize)/2.0) , orginalY+pageHeight);
                        var x1 = position.x + fontSize ;
                        var x2 = x1 + fontSize*2 ;
                        positionAry.push(x1 + "##" + x2);
                        context.stroke();
                        context.restore();
                     }
                    cnt++;
                    position.y = position.y + fontSize * 1.5;
                }
                    contentAry.push(lastStrTemp);
            }
 
 
            function jump(){
               var jumpPageNumStr =  document.getElementById("jumpPageNum").value;
               if(jumpPageNumStr!=""){
                   try{
                      var jumpPageNum = parseInt(jumpPageNumStr);
                      document.getElementById("pageNum").value=jumpPageNum;
                      if(jumpPageNum>0){
                         document.getElementById("form1").submit();
                      }else{
                         alert("请输入正确的页码!");
                      }
                   }catch(e){
                      alert("请输入正确的页码!");
                      return
                   }
               }
            }
 
            function jumpPaste(){
                window.clipboardData.setData('text',clipboardData.getData('text').replace(/^[0-9]*$/g,''));
            }
 
 
            function jumpChange(obj){
                if(!/^[0-9]*$/.test(obj.value)){
                   obj.value='';
                   alert('输入数字!');
               }
            }
 
            function urlencode (str) {
                        str = (str + '').toString();
                        return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').
                        replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
                }
            function doHelp() {
                var searchTxt = document.getElementById("searchTxt").value;
                if(searchTxt.length>1){
                    alert("条目太长,限一个生字");
                }else{
                    window.open("https://www.zdic.net/hans/"+urlencode(searchTxt));
                }
 
            }
            function prev(){
                var pageNum = document.getElementById("pageNum").value;
                if( pageNum == "" || pageNum == 1 ){
                    document.getElementById("pageNum").value = 1;
                }else{
                    document.getElementById("pageNum").value= parseInt(pageNum) - 1;
                }
                document.getElementById("form1").submit();
            }
 
            function current(){
                var pageNum = document.getElementById("pageNum").value;
                if( pageNum == "" || pageNum == 1 ){
                    document.getElementById("pageNum").value = 1;
                }
                document.getElementById("form1").submit();
            }
 
            function next(){
                var pageNum = document.getElementById("pageNum").value;
                if( pageNum == "" ){
                    document.getElementById("pageNum").value = 1;
                }else{
                    document.getElementById("pageNum").value= parseInt(pageNum) + 1;
                }
                document.getElementById("form1").submit();
            }
 
 
            function mark(){
                var content  =  article;
                var firstIndex = 1;
                var re = /[^'"“”?‘’《》○\s]+/;
                var mark = "";
                for(var i=0;i<content.length;i++){
                   var myArray = null ;
                   var lastIndex = firstIndex + ((content.length)/5) -1;
                   if (lastIndex<=1){
                      mark = "";
                      break;
                   }
                   if(lastIndex>content.length-1){
                      break;
                   }
                   var str1 = content.substring(firstIndex,lastIndex);
                   myArray = str1.match(re);
                   if((myArray==null) || (myArray=="") || (myArray.length==0)){
                       continue;
                   }
 
                   for(var j=0;j<myArray.length;j++){
                        var  markTemp = myArray[j];
                        if(markTemp.length>mark.length){
                           mark = markTemp;
                        }
                   }
                   firstIndex++;
                }
 
                if(mark.length>1){
                   if( mark.substring(0,1)==","){
                      mark = mark.substring(1);
                   }
                }
                alert(mark);
                send_request(mark,bookId);
            }
 
            function backMain(){
                window.location.href = "/boat/index";
            }
            function send_request( mark,bookId ){
                var xmlhttp = null;
                if (window.XMLHttpRequest){
                    xmlhttp=new XMLHttpRequest();
                }else if (window.ActiveXObject){
                    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
                }
                if (xmlhttp!=null){
                    var requrl = '/boat/mark';
                    xmlhttp.open( "POST",requrl,true);
                    xmlhttp.onreadystatechange=function(){
                        //异步需要指定回调函数
                        if (xmlhttp.readyState==4 && xmlhttp.status==200){
                           //readyState为4,表示ajax请求已经完成,status是目标url返回的http状态码,200表示服务器响应成功
                        var d= xmlhttp.responseText;
                        // 处理返回结果
                           alert(d);
                      }
                    }
                    //post需要设置Content-type,防止乱码
                    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
                    //post需要将参数放在send方法,当然参数放在url也还是可以的,但不好
                    var jumpMarkStr = mark;
                    xmlhttp.send("jumpMark="+jumpMarkStr+"&bookId="+ bookId);
                }else{
                    alert("您的浏览器不支持AJAX!");
                }
            }
 
            window.onload = function(){
                init();
            }  
        </script>
    </body>
</html>

  2.效果如下:

 

 

源代码公开,欢迎修改转载使用。

 https://pan.baidu.com/s/1NW_Am0WsjwrTtcfZKVTkOg 提取码: ptaw 

下载地址:https://files.cnblogs.com/files/javatiandi/DuRenZhou.rar

 

posted @   和平鸽  阅读(814)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
阅读排行:
· 10亿数据,如何做迁移?
· 推荐几款开源且免费的 .NET MAUI 组件库
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· c# 半导体/led行业 晶圆片WaferMap实现 map图实现入门篇
· 易语言 —— 开山篇
点击右上角即可分享
微信分享提示