Compare commits

...

21 Commits

Author SHA1 Message Date
e4e511aed8 feat: 移出书架 2025-03-20 22:03:10 +08:00
ec9674f2aa feat: 移出书架 2025-03-20 21:51:51 +08:00
06074faf9a feat(pc): 移除书架 2025-03-20 17:30:18 +08:00
73654dda2b ci: GitHub Actions 设置上传 token 2025-03-20 14:58:26 +08:00
c0634a335e ci: GitHub Actions 上传ZIP包 2025-03-20 14:48:42 +08:00
5dcc2b0b46 v5.0.1 发布 2025-03-20 13:27:46 +08:00
6cdb68899b ci: GitHub Actions 指定中央仓库下载 maven 依赖 2025-03-20 13:26:10 +08:00
d955b11165 ci: 创建 GitHub Actions 工作流自动发布版本 2025-03-20 13:19:51 +08:00
1081b8e10a chore: 修改日志 2025-03-19 10:12:22 +08:00
4b1507b2d1 perf: 连接池统一创建 2025-03-19 10:04:54 +08:00
82658f3b5f perf: 兼容其它数据源 2025-03-19 08:03:45 +08:00
acf9c76757 perf: 提前创建数据库连接池
Spring Boot 新版本默认会在第一次请求数据库时创建连接池
2025-03-19 07:50:50 +08:00
4b00ea68a9 perf: 提高第一次登录速度 2025-03-19 00:09:10 +08:00
d9f9fd8bd2 build: 优化打包配置 2025-03-18 21:39:01 +08:00
c1583f83bb fix: 网站icon显示 2025-03-18 21:19:18 +08:00
eecbb2dd9c fix: 通用异常处理 2025-03-18 21:18:51 +08:00
0afc7b1bbf fix: 通用异常处理 2025-03-18 21:03:21 +08:00
f5a9a7423f fix: 生产环境web静态资源目录配置 2025-03-18 20:22:54 +08:00
3858cd4e49 build: 修改版本号 2025-03-18 19:10:10 +08:00
c4a6acf2b3 fix: 网站ico图标显示 2025-03-18 14:24:19 +08:00
8781cc54d4 chore: 模版/配置/SQL/文档更新 2025-03-17 18:47:05 +08:00
42 changed files with 911 additions and 143 deletions

79
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,79 @@
name: Create novel-plus Maven Release with ZIPs
on:
push:
# 匹配所有以'v'开头的标签
tags:
- 'v*'
jobs:
build-and-release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '21'
# 可选默认是 temurin也可以选择其他发行版
distribution: 'temurin'
- name: Build project with Maven
run: mvn clean install -DskipTests=true -Pcentral-repo
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# 使用 tag_name 而不是 github.ref
tag_name: ${{ github.ref_name }}
release_name: novel-plus ${{ github.ref_name }}
draft: false
prerelease: false
# 使用 action 来替代直接 curl 进行上传
- name: Upload sql.zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/novel-common/target/build/sql.zip
asset_name: sql.zip
asset_content_type: application/zip
- name: Upload novel-crawl.zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/novel-crawl/target/build/novel-crawl.zip
asset_name: novel-crawl.zip
asset_content_type: application/zip
- name: Upload novel-front.zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/novel-front/target/build/novel-front.zip
asset_name: novel-front.zip
asset_content_type: application/zip
- name: Upload novel-admin.zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ github.workspace }}/novel-admin/target/build/novel-admin.zip
asset_name: novel-admin.zip
asset_content_type: application/zip

View File

@ -42,6 +42,7 @@ novel-plus -- 父工程
| 技术 | 说明
|---------------------| ---------------------------
| Spring Boot | Spring 应用快速开发脚手架
| Spring AI | Spring 官方 AI 框架
| MyBatis | 持久层 ORM 框架
| MyBatis Dynamic SQL | Mybatis 动态 sql
| PageHelper | MyBatis 分页插件

44
doc/sql/20250317.sql Normal file
View File

@ -0,0 +1,44 @@
INSERT INTO crawl_source (source_name, crawl_rule, source_status, create_time, update_time)
VALUES ('香书小说网', '{
"bookListUrl": "http://www.xbiqugu.la/fenlei/{catId}_{page}.html",
"catIdRule": {
"catId1": "1",
"catId2": "2",
"catId3": "3",
"catId4": "4",
"catId5": "6",
"catId6": "5"
},
"bookIdPatten": "<a\\\\s+href=\\"http://www.xbiqugu.la/(\\\\d+/\\\\d+)/\\"\\\\s+target=\\"_blank\\">",
"pagePatten": "<em\\\\s+id=\\"pagestats\\">(\\\\d+)/\\\\d+</em>",
"totalPagePatten": "<em\\\\s+id=\\"pagestats\\">\\\\d+/(\\\\d+)</em>",
"bookDetailUrl": "http://www.xbiqugu.la/{bookId}/",
"bookNamePatten": "<h1>([^/]+)</h1>",
"authorNamePatten": "者:([^/]+)</p>",
"picUrlPatten": "src=\\"(http://www.xbiqugu.la/files/article/image/\\\\d+/\\\\d+/\\\\d+s\\\\.jpg)\\"",
"bookStatusRule": {},
"descStart": "<div id=\\"intro\\">",
"descEnd": "</div>",
"upadateTimePatten": "<p>最后更新:(\\\\d+-\\\\d+-\\\\d+\\\\s\\\\d+:\\\\d+:\\\\d+)</p>",
"upadateTimeFormatPatten": "yyyy-MM-dd HH:mm:ss",
"bookIndexUrl": "http://www.xbiqugu.la/{bookId}/",
"indexIdPatten": "<a\\\\s+href=''/\\\\d+/\\\\d+/(\\\\d+)\\\\.html''\\\\s+>[^/]+</a>",
"indexNamePatten": "<a\\\\s+href=''/\\\\d+/\\\\d+/\\\\d+\\\\.html''\\\\s+>([^/]+)</a>",
"bookContentUrl": "http://www.xbiqugu.la/{bookId}/{indexId}.html",
"contentStart": "<div id=\\"content\\">",
"contentEnd": "<p>",
"filterContent":"<div\\\\s+id=\\"content_tip\\">\\\\s*<b>([^/]+)</b>\\\\s*</div>"
}', 0, '2024-06-01 10:11:39', '2024-06-01 10:11:39');
update crawl_source
set crawl_rule = replace(crawl_rule, 'ibiquzw.org', 'biquxs.info')
where id = 16;
delete
from sys_menu
where menu_id = 104;
delete
from sys_menu
where menu_id = 57;

View File

@ -5,7 +5,7 @@
<groupId>com.java2nb</groupId>
<artifactId>novel-admin</artifactId>
<version>5.0.0</version>
<version>5.0.1</version>
<packaging>jar</packaging>
<name>novel-admin</name>
@ -284,11 +284,6 @@
<include name="*.*"/>
</fileset>
</copy>
<copy todir="${project.build.directory}/build/config" overwrite="true">
<fileset dir="${basedir}/../config">
<include name="*.*"/>
</fileset>
</copy>
<copy file="${project.build.directory}/${project.artifactId}-${project.version}.jar"
tofile="${project.build.directory}/build/${project.artifactId}.jar"/>
@ -337,4 +332,35 @@
</snapshots>
</pluginRepository>
</pluginRepositories>
<profiles>
<profile>
<!-- 定义一个用于切换到中央仓库的profile -->
<id>central-repo</id>
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central-plugin</id>
<url>https://repo.maven.apache.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,47 @@
mode:
# 单机模式
type: Standalone
# 元数据持久化
repository:
# 数据库持久化
type: JDBC
# 数据源配置
dataSources:
ds_1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/novel_plus?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: test123456
# 规则配置
rules:
- !SINGLE
tables:
- "*.*"
- !SHARDING
tables: # 数据分片规则配置
book_content:
# 分库策略缺省表示使用默认分库策略
actualDataNodes: ds_${1}.book_content${0..9}
# 分表策略
tableStrategy:
standard:
# 分片列名称
shardingColumn: index_id
# 分片算法名称
shardingAlgorithmName: bookContentSharding
shardingAlgorithms:
bookContentSharding:
# 行表达式分片算法使用 Groovy 的表达式提供对 SQL 语句中的 = IN 的分片操作支持
type: INLINE
props:
# 分片算法的行表达式
algorithm-expression: book_content${index_id % 10}
props:
# 是否在日志中打印 SQL
sql-show: true

View File

@ -11,27 +11,37 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.net.InetAddress;
import java.sql.Connection;
@EnableTransactionManagement
@ServletComponentScan
@MapperScan("com.java2nb.*.dao")
@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class
})
@EnableCaching
@Slf4j
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
public CommandLineRunner commandLineRunner(ApplicationContext ctx, DataSource dataSource) {
return args -> {
log.info("项目启动啦,访问路径:{}", "http://" + InetAddress.getLocalHost().getHostAddress() + ":" + ctx.getEnvironment().getProperty("server.port"));
log.info("创建连接池...");
try (Connection connection = dataSource.getConnection()) {
log.info("连接池已创建.");
log.info("数据库:{}", connection.getMetaData().getDatabaseProductName());
log.info("数据库版本:{}", connection.getMetaData().getDatabaseProductVersion());
}
log.info("项目启动啦,访问路径:{}",
"http://" + InetAddress.getLocalHost().getHostAddress() + ":" + ctx.getEnvironment()
.getProperty("server.port"));
};
}
}

View File

@ -27,9 +27,6 @@ spring:
max-file-size: 100MB
max-request-size: 100MB
devtools:
restart:
enabled: true
main:
allow-bean-definition-overriding: true
@ -39,9 +36,6 @@ mybatis:
map-underscore-to-camel-case: true
mapper-locations: mybatis/**/*Mapper.xml
typeAliasesPackage: com.java2nb.**.domain
#[弃用]配置缓存和session存储方式默认ehcache,可选redis,[弃用]调整至 spring cache typeshiro.用户权限sessionspring.cache通用
#[弃用]cacheType: ehcache
logging:
config: classpath:logback-boot.xml

View File

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -6,7 +6,7 @@
<title>小说精品屋 - 文件管理器</title>
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="shortcut icon" href="favicon.ico">
<link rel="shortcut icon" href="/favicon.ico">
<link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link href="/css/font-awesome.css?v=4.4.0" rel="stylesheet">
<link href="/css/animate.css" rel="stylesheet">

View File

@ -6,7 +6,7 @@
<title>403 页面</title>
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="shortcut icon" href="favicon.ico">
<link rel="shortcut icon" href="/favicon.ico">
<link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link href="/css/font-awesome.css?v=4.4.0" rel="stylesheet">
<link href="/css/animate.css" rel="stylesheet">

View File

@ -10,7 +10,7 @@
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="shortcut icon" href="favicon.ico"> <link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link rel="shortcut icon" href="/favicon.ico"> <link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link href="/css/font-awesome.css?v=4.4.0" rel="stylesheet">
<link href="/css/animate.css" rel="stylesheet">

View File

@ -11,7 +11,7 @@
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="shortcut icon" href="favicon.ico"> <link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link rel="shortcut icon" href="/favicon.ico"> <link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link href="/css/font-awesome.css?v=4.4.0" rel="stylesheet">
<link href="/css/animate.css" rel="stylesheet">

View File

@ -6,7 +6,7 @@
<title>500错误</title>
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="shortcut icon" href="favicon.ico"> <link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link rel="shortcut icon" href="/favicon.ico"> <link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link href="/css/font-awesome.css?v=4.4.0" rel="stylesheet">
<link href="/css/animate.css" rel="stylesheet">
<link href="/css/style.css?v=4.1.0" rel="stylesheet">

View File

@ -4,7 +4,7 @@
<title></title>
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="shortcut icon" href="favicon.ico">
<link rel="shortcut icon" href="/favicon.ico">
<link href="css/bootstrap.min.css?v=3.3.6"
th:href="@{/css/bootstrap.min.css?v=3.3.6}" rel="stylesheet">
<link href="/css/font-awesome.css?v=4.4.0"

View File

@ -10,7 +10,7 @@
<!--[if lt IE 9]>
<meta http-equiv="refresh" content="0;ie.html"/>
<![endif]-->
<link rel="shortcut icon" href="favicon.ico">
<link rel="shortcut icon" href="/favicon.ico">
<link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link href="/css/font-awesome.min.css?v=4.4.0" rel="stylesheet">
<link href="/css/plugins/toastr/toastr.min.css" rel="stylesheet">

View File

@ -11,7 +11,7 @@
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="shortcut icon" href="favicon.ico">
<link rel="shortcut icon" href="/favicon.ico">
<link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link href="/css/font-awesome.css?v=4.4.0" rel="stylesheet">
<link href="/css/animate.css" rel="stylesheet">

View File

@ -4,7 +4,7 @@
<title></title>
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="shortcut icon" href="favicon.ico">
<link rel="shortcut icon" href="/favicon.ico">
<link rel="stylesheet" href="/css/bootstrap.min.css" />
<link rel="stylesheet" href="/css/animate.css" />
<link rel="stylesheet" href="/css/font-awesome.css" />

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>novel</artifactId>
<groupId>com.java2nb</groupId>
<version>5.0.0</version>
<version>5.0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -130,5 +130,29 @@
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<zip destfile='${project.build.directory}/build/sql.zip'>
<zipfileset filemode="755" dir='${basedir}/../doc/sql'/>
</zip>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -3,11 +3,19 @@ package com.java2nb.novel.core.advice;
import io.github.xxyopen.model.resp.RestResult;
import io.github.xxyopen.model.resp.SysResultCode;
import io.github.xxyopen.web.exception.BusinessException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;
/**
* 统一异常处理器
*
* @author xiongxiaoyang
*/
@Slf4j
@RestControllerAdvice
public class CommonExceptionHandler {
@ -27,9 +35,21 @@ public class CommonExceptionHandler {
return RestResult.fail(e.getResultCode());
}
@ExceptionHandler({Exception.class})
public RestResult<Void> handlerException(Exception e) {
@ExceptionHandler(Exception.class)
public Object handleException(HttpServletRequest request, Exception e) {
log.error(e.getMessage(), e);
return RestResult.error();
if (isJsonRequest(request)) {
// 如果是REST请求返回JSON格式的错误响应
return RestResult.error();
} else {
//跳转页面过程中出现异常时统一跳转到404页面
return new ModelAndView("404");
}
}
private boolean isJsonRequest(HttpServletRequest request) {
String acceptHeader = request.getHeader("Accept");
return acceptHeader != null && acceptHeader.contains(MediaType.APPLICATION_JSON_VALUE);
}
}

View File

@ -1,26 +0,0 @@
package com.java2nb.novel.core.advice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* 页面异常处理器
*
* @author 11797
*/
@Slf4j
@ControllerAdvice(basePackages = "com.java2nb.novel.controller.page")
public class PageExceptionHandler {
/**
* 处理所有异常
*/
@ExceptionHandler(Exception.class)
public String handlerException(Exception e) {
log.error(e.getMessage(), e);
//跳转页面过程中出现异常时统一跳转到404页面
return "404";
}
}

View File

@ -2,9 +2,6 @@ spring:
datasource:
url: jdbc:shardingsphere:absolutepath:${user.dir}/config/shardingsphere-jdbc.yml
driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
cache:
ehcache:
config: classpath:ehcache.xml
thymeleaf:
mode: LEGACYHTML5 #去除thymeleaf的html严格校验thymeleaf.mode=LEGACYHTML5
cache: false # 是否开启模板缓存默认true,建议在开发时关闭缓存,不然没法看到实时

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>novel</artifactId>
<groupId>com.java2nb</groupId>
<version>5.0.0</version>
<version>5.0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -63,11 +63,6 @@
<include name="*.*"/>
</fileset>
</copy>
<copy todir="${project.build.directory}/build/config" overwrite="true">
<fileset dir="${basedir}/../config">
<include name="*.*"/>
</fileset>
</copy>
<copy file="${project.build.directory}/${project.artifactId}-${project.version}.jar"
tofile="${project.build.directory}/build/${project.artifactId}.jar"/>

View File

@ -0,0 +1,47 @@
mode:
# 单机模式
type: Standalone
# 元数据持久化
repository:
# 数据库持久化
type: JDBC
# 数据源配置
dataSources:
ds_1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/novel_plus?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: test123456
# 规则配置
rules:
- !SINGLE
tables:
- "*.*"
- !SHARDING
tables: # 数据分片规则配置
book_content:
# 分库策略缺省表示使用默认分库策略
actualDataNodes: ds_${1}.book_content${0..9}
# 分表策略
tableStrategy:
standard:
# 分片列名称
shardingColumn: index_id
# 分片算法名称
shardingAlgorithmName: bookContentSharding
shardingAlgorithms:
bookContentSharding:
# 行表达式分片算法使用 Groovy 的表达式提供对 SQL 语句中的 = IN 的分片操作支持
type: INLINE
props:
# 分片算法的行表达式
algorithm-expression: book_content${index_id % 10}
props:
# 是否在日志中打印 SQL
sql-show: true

View File

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>novel</artifactId>
<groupId>com.java2nb</groupId>
<version>5.0.0</version>
<version>5.0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -90,11 +90,6 @@
<include name="application-website.yml"/>
</fileset>
</copy>
<copy todir="${project.build.directory}/build/config" overwrite="true">
<fileset dir="${basedir}/../config">
<include name="*.*"/>
</fileset>
</copy>
<copy todir="${project.build.directory}/build/templates" overwrite="true">
<fileset dir="${basedir}/../templates">
<include name="**/*.*"/>

View File

@ -49,3 +49,13 @@ http:
# 代理密码
password: swiftproxy_p
--- #--------------------- Spring AI 配置----------------------
spring:
ai:
openai:
api-key: sk-nnhjmxuljagcuubbovjztbhkiawqaabzziazeurppinxtgva
base-url: https://api.siliconflow.cn
chat:
options:
model: deepseek-ai/DeepSeek-R1-Distill-Llama-8B

View File

@ -0,0 +1,47 @@
mode:
# 单机模式
type: Standalone
# 元数据持久化
repository:
# 数据库持久化
type: JDBC
# 数据源配置
dataSources:
ds_1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/novel_plus?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: test123456
# 规则配置
rules:
- !SINGLE
tables:
- "*.*"
- !SHARDING
tables: # 数据分片规则配置
book_content:
# 分库策略缺省表示使用默认分库策略
actualDataNodes: ds_${1}.book_content${0..9}
# 分表策略
tableStrategy:
standard:
# 分片列名称
shardingColumn: index_id
# 分片算法名称
shardingAlgorithmName: bookContentSharding
shardingAlgorithms:
bookContentSharding:
# 行表达式分片算法使用 Groovy 的表达式提供对 SQL 语句中的 = IN 的分片操作支持
type: INLINE
props:
# 分片算法的行表达式
algorithm-expression: book_content${index_id % 10}
props:
# 是否在日志中打印 SQL
sql-show: true

View File

@ -3,12 +3,13 @@ spring:
import: classpath:application-common-prod.yml
#静态文件路径配置
resources:
static-locations: file:${user.dir}/templates/${templates.name}/static/
#thymeleaf模版路径配置
thymeleaf:
prefix: file:${user.dir}/templates/${templates.name}/html/
suffix: .html
web:
resources:
static-locations: file:${user.dir}/templates/${templates.name}/static/
#模版配置
templates:

View File

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -6,8 +6,8 @@
<meta name="keywords" th:content="${application.website.name}+',小说,小说网,言情小说,都市小说,玄幻小说,穿越小说,青春小说,总裁豪门小说,网络小说,免费小说,全本小说,原创网络文学'"/>
<meta name="description"
th:content="${application.website.name}+'每日更新小说连载,小说排行榜,提供言情小说,都市小说,玄幻小说,穿越小说,青春小说,总裁豪门小说,网络小说,免费小说,全本小说,首发小说,最新章节免费小说阅读,精彩尽在'+${application.website.name}+'。'"/>
<link href="favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link href="/favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="/favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link rel="stylesheet" href="/css/main.css"/>
</head>
<body>

View File

@ -7,8 +7,8 @@
<meta name="keywords" th:content="${application.website.keyword}"/>
<meta name="description"
th:content="${application.website.description}"/>
<link href="favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link href="/favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="/favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link rel="stylesheet" href="/css/main.css"/>
</head>
<body>

View File

@ -20,6 +20,28 @@
<style type="text/css">
body {
-webkit-user-select: none; /* Chrome, Safari, Opera */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently supported by Chrome, Opera, and Firefox */
}
.line-limit-length {
@ -62,11 +84,21 @@
height: 180px;
}
#tipLayer {
display: none;
position: absolute;
background-color: rgba(255, 0, 0, 0.8);
color: white;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
z-index: 1000;
}
</style>
</head>
<body>
<input type="hidden" id="limit" th:value="${limit}"/>
<input type="hidden" id="curr" th:value="${curr}"/>
<input type="hidden" id="total" th:value="${total}"/>
@ -95,6 +127,10 @@
</div>
<div id="tipLayer"
style="display:none; position:absolute; background-color:rgba(255, 0, 0, 0.8); color:white; padding:5px; border-radius:3px; cursor:pointer;">
移出书架
</div>
<div id="books" style="text-align: center;"></div>
@ -105,11 +141,12 @@
<a name="buttom"></a>
</body>
<div th:replace="mobile/common/js :: js"></div>
<script>
var timeout, isLongPress = false;
search(1, 20);
function search(curr, limit) {
@ -136,22 +173,16 @@
book.bookDesc = book.bookDesc.replace(/<[^>]+>/g, "").replace(/\s+/g, "").replace(/&nbsp;/g, "");
}
bookListHtml += ("<div class=\"layui-row\" style=\"margin-bottom:10px;padding:10px;background: #f2f2f2\">\n" +
" <a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>\n" +
bookListHtml += ("<div id='"+book.bookId+"' onclick='read(\""+book.bookId+"\",\""+book.preContentId+"\")' class=\"item layui-row\" style=\"margin-bottom:10px;padding:10px;background: #f2f2f2\">\n" +
" <div class=\"layui-col-xs6 layui-col-sm3 layui-col-md2 layui-col-lg2\" style=\"text-align: center\">\n" +
" <img style='width: 130px;height: 180px' align=\"center\"\n" +
" src=\"" + book.picUrl + "\"/>\n" +
"\n" +
" </div>\n" +
" </a>\n" +
" \n" +
" <div style=\"padding: 10px\" class=\"layui-col-xs6 layui-col-sm8 layui-col-md8 layui-col-lg8\">\n" +
" <a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>\n" +
" <div class=\"line-limit-length\" style=\";color: #000;font-size: 15px\">" + book.bookName + "</div>\n" +
" </a>\n" +
" <div style=\";color: #4c6978;float: right;\"><i style=\"color: red\"></i></div>\n" +
" <a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>\n" +
" <div style=\";color: #a6a6a6;\" class=\"line-limit-length\">作者:" + book.authorName + "</div>\n" +
" </a>\n" +
" <div style=\"margin-top: 5px;color: #a6a6a6;\">类别:" + book.catName + "</div>\n" +
" <div style=\"margin-top: 5px;color: #a6a6a6;\">状态:" + (book.bookStatus == 0 ? '连载' : '完结') + "</div>\n" +
" <div style=\"margin-top: 5px;color: #a6a6a6;\">更新:<i style='color: red'>" + book.lastIndexUpdateTime.substr(0, 11) + "</i>\n" +
@ -165,6 +196,43 @@
}
$("#bookList").html(bookListHtml);
$(".item").on('touchstart', function(e) {
var element = $(this);
// 清除可能存在的定时器
clearTimeout(timeout);
isLongPress = false;
// 获取触摸点位置
var touch = e.originalEvent.touches[0];
// 设置一个定时器在500ms后触发可以根据需要调整时间
timeout = setTimeout(function() {
e.preventDefault();
showTip(touch, element);
}, 1000);
}).on('touchend', function(e) {
if (!isLongPress) {
// 如果没有发生长按,则执行点击事件的逻辑
clearTimeout(timeout);
}
}).on('touchmove', function() {
clearTimeout(timeout);
hideTip();
}).on('contextmenu', function(e) {
e.preventDefault();
});
$('#tipLayer').click(function() {
// 点击tips层时删除对应的.item元素
removeFromBookShelf($(this).data('target').attr("id"));
$(this).data('target').remove();
hideTip();
});
layui.use('laypage', function () {
var laypage = layui.laypage;
@ -203,6 +271,23 @@
}
function showTip(touchEvent, element) {
isLongPress = true;
// 根据触摸点位置设置弹出层的位置
$('#tipLayer')
.css({
top: touchEvent.pageY - 100, // 调整这个值以改变弹出层相对于触摸点的位置
left: touchEvent.pageX - ($('#tipLayer').outerWidth() / 2)
})
.data('target', element) // 存储目标元素以便后续操作
.show();
}
function hideTip() {
isLongPress = false;
$('#tipLayer').hide().removeData('target'); // 隐藏tips并清除数据
}
function searchByAllCondition(curr, limit, newKeyword) {
var toUrl = "/book/search?curr=" + curr + "&limit=" + limit;
@ -248,18 +333,35 @@
searchByAllCondition(1, 20, keywords);
}
function read(bookId,contentId){
if(isLongPress){
return false;
}
location.href = '/book/'+bookId+"/"+contentId+".html"
hideTip();
}
</script>
<script>
function toMyCollect() {
var token = localStorage.getItem("token");
if (token) {
window.location.href = "/book/search?token=" + token;
} else {
window.location.href = "/user/login.html";
}
}
function removeFromBookShelf(bookId) {
$.ajax({
type: "delete",
url: "/user/removeFromBookShelf/" + bookId,
data: {},
dataType: "json",
success: function (data) {
if (data.code == 200) {
$("#shelf" + bookId).remove();
}
}
});
}
</script>

View File

@ -53,7 +53,7 @@
更新时间
</th>
<th class="goread">
书签
操作
</th>
</tr>
</thead>
@ -94,7 +94,7 @@
var bookShelfListHtml = "";
for (var i = 0; i < bookShelfList.length; i++) {
var book = bookShelfList[i];
bookShelfListHtml += (" <tr class=\"book_list\" vals=\"291\">\n" +
bookShelfListHtml += (" <tr id='shelf" + book.bookId + "' class=\"book_list\" vals=\"291\">\n" +
" <td class=\"style bookclass\">\n" +
" <a href=\"/book/bookclass.html?c=" + book.catId + "\" >[" + book.catName + "]</a>\n" +
" </td>\n" +
@ -109,7 +109,8 @@
" " + book.lastIndexUpdateTime + "\n" +
" </td>\n" +
" <td class=\"goread\">\n" +
"<a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>继续阅读</a>" +
"<div style=''><a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>继续阅读</a></div>" +
"<div style='line-height:8px;padding-bottom:13px'><a href='javascript:removeFromBookShelf(\"" + book.bookId + "\")'>移出书架</a></div>" +
" </td>\n" +
" </tr>");
}
@ -160,5 +161,21 @@
})
}
function removeFromBookShelf(bookId) {
$.ajax({
type: "delete",
url: "/user/removeFromBookShelf/" + bookId,
data: {},
dataType: "json",
success: function (data) {
if (data.code == 200) {
$("#shelf" + bookId).remove();
}
}
});
}
</script>
</html>

33
pom.xml
View File

@ -5,7 +5,7 @@
<groupId>com.java2nb</groupId>
<artifactId>novel</artifactId>
<version>5.0.0</version>
<version>5.0.1</version>
<modules>
<module>novel-common</module>
<module>novel-front</module>
@ -113,4 +113,35 @@
</snapshots>
</pluginRepository>
</pluginRepositories>
<profiles>
<profile>
<!-- 定义一个用于切换到中央仓库的profile -->
<id>central-repo</id>
<repositories>
<repository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central-plugin</id>
<url>https://repo.maven.apache.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</project>

View File

@ -8,6 +8,79 @@
<title>作家管理系统-小说精品屋</title>
<link rel="stylesheet" href="/css/base.css?v=1"/>
<link rel="stylesheet" href="/css/user.css"/>
<style>
/* 编辑器容器样式 */
.editor-container {
margin: 10px 0px;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
}
/* 文本域样式 */
#bookContent {
width: 93%;
height: 400px;
padding: 15px;
border: 1px solid #e6e6e6;
border-radius: 4px;
font-size: 14px;
line-height: 1.6;
resize: vertical;
}
/* 工具栏样式 */
.ai-toolbar {
margin-bottom: 15px;
display: flex;
gap: 10px; /* 按钮间距 */
}
/* 自定义链接按钮样式 */
.ai-link {
display: inline-block;
padding: 10px 20px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
color: #fff;
text-decoration: none;
cursor: pointer;
transition: all 0.3s ease;
background: linear-gradient(135deg, #6a11cb, #2575fc);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* 链接按钮悬停效果 */
.ai-link:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
/* 链接按钮点击效果 */
.ai-link:active {
transform: translateY(0);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* 不同按钮的颜色 */
.ai-link.expand {
background: linear-gradient(135deg, #ff9a9e, #fad0c4);
}
.ai-link.condense {
background: linear-gradient(135deg, #a18cd1, #fbc2eb);
}
.ai-link.continue {
background: linear-gradient(135deg, #f6d365, #fda085);
}
.ai-link.polish {
background: linear-gradient(135deg, #ff6f61, #ffcc00);
}
</style>
</head>
</head>
<body class="">
@ -46,18 +119,29 @@
<ul class="log_list">
<li><span id="LabErr"></span></li>
<b>章节名:</b>
<li><input type="text" id="bookIndex" name="bookIndex" class="s_input" ></li>
<b>章节内容:</b><li id="contentLi">
<textarea name="bookContent" rows="30" cols="80" id="bookContent"
class="textarea"></textarea></li><br/>
<li><input type="text" id="bookIndex" name="bookIndex" class="s_input"></li>
<b>章节内容:</b>
<li id="contentLi" style="width: 500px">
<div class="editor-container">
<div class="ai-toolbar">
<a class="ai-link expand" data-type="expand">AI扩写</a>
<a class="ai-link condense" data-type="condense">AI缩写</a>
<a class="ai-link continue" data-type="continue">AI续写</a>
<a class="ai-link polish" data-type="polish">AI润色</a>
</div>
<textarea id="bookContent" name="bookContent"
placeholder="请输入文本内容..."></textarea>
</div>
<b>是否收费:</b>
<li><input type="radio" name="isVip" value="0" checked >免费
<input type="radio" name="isVip" value="1" >收费</li>
<b>是否收费:</b>
<li><input type="radio" name="isVip" value="0" checked>免费
<input type="radio" name="isVip" value="1">收费
</li>
<li style="margin-top: 10px"><input type="button" onclick="addBookContent()" name="btnRegister" value="提交"
id="btnRegister" class="btn_red">
<li style="margin-top: 10px"><input type="button" onclick="addBookContent()"
name="btnRegister" value="提交"
id="btnRegister" class="btn_red">
</li>
@ -113,9 +197,10 @@
var lock = false;
function addBookContent() {
if(lock){
if (lock) {
return;
}
lock = true;
@ -125,14 +210,14 @@
var indexName = $("#bookIndex").val();
if(!indexName){
if (!indexName) {
$("#LabErr").html("章节名不能为空");
lock = false;
return;
}
var content = $("#bookContent").val();
if(!content){
if (!content) {
$("#LabErr").html("章节内容不能为空");
lock = false;
return;
@ -142,17 +227,15 @@
var isVip = $("input:checked[name=isVip]").val();
$.ajax({
type: "POST",
url: "/author/addBookContent",
data: {'bookId':bookId,'indexName':indexName,'content':content,'isVip':isVip},
data: {'bookId': bookId, 'indexName': indexName, 'content': content, 'isVip': isVip},
dataType: "json",
success: function (data) {
if (data.code == 200) {
window.location.href = '/author/index_list.html?bookId='+bookId;
window.location.href = '/author/index_list.html?bookId=' + bookId;
} else {
@ -169,5 +252,110 @@
}
// 打字机效果函数
function typeWriter(textarea, text, speed = 50) {
let i = 0;
const timer = setInterval(() => {
if (i < text.length) {
textarea.val(textarea.val() + text.charAt(i));
i++;
// 滚动到底部
textarea.scrollTop(textarea[0].scrollHeight);
} else {
clearInterval(timer);
}
}, speed);
}
$('.ai-toolbar .ai-link').click(function(e){
e.preventDefault(); // 阻止默认链接行为
const type = $(this).data('type');
const textarea = $('#bookContent');
const selectedText = textarea.val().substring(textarea[0].selectionStart, textarea[0].selectionEnd);
// 检查是否选中文本
if (!selectedText) {
layer.msg('请先选中要处理的文本');
return;
}
const loading = layer.load(1, {shade: 0.3});
// 参数配置
let params = {text: selectedText};
if(type === 'expand' || type === 'condense'){
layer.prompt({
title: '请输入比例',
value: 2,
btn: ['确定', '取消'],
btn2: function(){
layer.close(loading);
},
cancel: function(){
layer.close(loading);
}
}, function(value, index){
if(isNaN(Number(value)) || isNaN(parseFloat(value))){
layer.msg('请输入正确的比例');
return;
}
if(type === 'expand' && value <= 1){
layer.msg('请输入正确的比例');
return;
}
if(type === 'condense' && (value <=0 || value >= 1)){
layer.msg('请输入正确的比例');
return;
}
params.ratio = parseFloat(value) * 100;
layer.close(index);
sendRequest(type, params, loading, textarea);
});
return;
}else if(type === 'continue'){
layer.prompt({
title: '请输入续写长度(字数)',
value: 200,
btn: ['确定', '取消'],
btn2: function(){
layer.close(loading);
},
cancel: function(){
layer.close(loading);
}
}, function(value, index){
if(!Number.isInteger(Number(value)) || value <= 0){
layer.msg('请输入正确的长度');
return;
}
params.length = parseInt(value);
layer.close(index);
sendRequest(type, params, loading, textarea);
});
return;
}
sendRequest(type, params, loading, textarea);
});
function sendRequest(type, params, loading, textarea){
$.ajax({
url: '/author/ai/' + type,
type: 'POST',
data: params,
success: function(res){
layer.close(loading);
// 将生成的内容追加到文本末尾
const newText = "\n\n" + res.data; // 添加换行符分隔
typeWriter(textarea, newText); // 使用打字机效果
},
error: function(){
layer.msg('请求失败,请稍后重试');
layer.close(loading);
}
});
}
</script>
</html>

View File

@ -6,8 +6,8 @@
<meta name="keywords" th:content="${application.website.name}+',小说,小说网,言情小说,都市小说,玄幻小说,穿越小说,青春小说,总裁豪门小说,网络小说,免费小说,全本小说,原创网络文学'"/>
<meta name="description"
th:content="${application.website.name}+'每日更新小说连载,小说排行榜,提供言情小说,都市小说,玄幻小说,穿越小说,青春小说,总裁豪门小说,网络小说,免费小说,全本小说,首发小说,最新章节免费小说阅读,精彩尽在'+${application.website.name}+'。'"/>
<link href="favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link href="/favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="/favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link rel="stylesheet" href="/css/main.css"/>
</head>
<body>

View File

@ -7,8 +7,8 @@
<meta name="keywords" th:content="${application.website.keyword}"/>
<meta name="description"
th:content="${application.website.description}"/>
<link href="favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link href="/favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="/favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link rel="stylesheet" href="/css/main.css"/>
</head>
<body>

View File

@ -20,6 +20,28 @@
<style type="text/css">
body {
-webkit-user-select: none; /* Chrome, Safari, Opera */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently supported by Chrome, Opera, and Firefox */
}
.line-limit-length {
@ -62,11 +84,21 @@
height: 180px;
}
#tipLayer {
display: none;
position: absolute;
background-color: rgba(255, 0, 0, 0.8);
color: white;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
z-index: 1000;
}
</style>
</head>
<body>
<input type="hidden" id="limit" th:value="${limit}"/>
<input type="hidden" id="curr" th:value="${curr}"/>
<input type="hidden" id="total" th:value="${total}"/>
@ -95,6 +127,10 @@
</div>
<div id="tipLayer"
style="display:none; position:absolute; background-color:rgba(255, 0, 0, 0.8); color:white; padding:5px; border-radius:3px; cursor:pointer;">
移出书架
</div>
<div id="books" style="text-align: center;"></div>
@ -105,11 +141,12 @@
<a name="buttom"></a>
</body>
<div th:replace="mobile/common/js :: js"></div>
<script>
var timeout, isLongPress = false;
search(1, 20);
function search(curr, limit) {
@ -136,22 +173,16 @@
book.bookDesc = book.bookDesc.replace(/<[^>]+>/g, "").replace(/\s+/g, "").replace(/&nbsp;/g, "");
}
bookListHtml += ("<div class=\"layui-row\" style=\"margin-bottom:10px;padding:10px;background: #f2f2f2\">\n" +
" <a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>\n" +
bookListHtml += ("<div id='"+book.bookId+"' onclick='read(\""+book.bookId+"\",\""+book.preContentId+"\")' class=\"item layui-row\" style=\"margin-bottom:10px;padding:10px;background: #f2f2f2\">\n" +
" <div class=\"layui-col-xs6 layui-col-sm3 layui-col-md2 layui-col-lg2\" style=\"text-align: center\">\n" +
" <img style='width: 130px;height: 180px' align=\"center\"\n" +
" src=\"" + book.picUrl + "\"/>\n" +
"\n" +
" </div>\n" +
" </a>\n" +
" \n" +
" <div style=\"padding: 10px\" class=\"layui-col-xs6 layui-col-sm8 layui-col-md8 layui-col-lg8\">\n" +
" <a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>\n" +
" <div class=\"line-limit-length\" style=\";color: #000;font-size: 15px\">" + book.bookName + "</div>\n" +
" </a>\n" +
" <div style=\";color: #4c6978;float: right;\"><i style=\"color: red\"></i></div>\n" +
" <a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>\n" +
" <div style=\";color: #a6a6a6;\" class=\"line-limit-length\">作者:" + book.authorName + "</div>\n" +
" </a>\n" +
" <div style=\"margin-top: 5px;color: #a6a6a6;\">类别:" + book.catName + "</div>\n" +
" <div style=\"margin-top: 5px;color: #a6a6a6;\">状态:" + (book.bookStatus == 0 ? '连载' : '完结') + "</div>\n" +
" <div style=\"margin-top: 5px;color: #a6a6a6;\">更新:<i style='color: red'>" + book.lastIndexUpdateTime.substr(0, 11) + "</i>\n" +
@ -165,6 +196,43 @@
}
$("#bookList").html(bookListHtml);
$(".item").on('touchstart', function(e) {
var element = $(this);
// 清除可能存在的定时器
clearTimeout(timeout);
isLongPress = false;
// 获取触摸点位置
var touch = e.originalEvent.touches[0];
// 设置一个定时器在500ms后触发可以根据需要调整时间
timeout = setTimeout(function() {
e.preventDefault();
showTip(touch, element);
}, 1000);
}).on('touchend', function(e) {
if (!isLongPress) {
// 如果没有发生长按,则执行点击事件的逻辑
clearTimeout(timeout);
}
}).on('touchmove', function() {
clearTimeout(timeout);
hideTip();
}).on('contextmenu', function(e) {
e.preventDefault();
});
$('#tipLayer').click(function() {
// 点击tips层时删除对应的.item元素
removeFromBookShelf($(this).data('target').attr("id"));
$(this).data('target').remove();
hideTip();
});
layui.use('laypage', function () {
var laypage = layui.laypage;
@ -203,6 +271,23 @@
}
function showTip(touchEvent, element) {
isLongPress = true;
// 根据触摸点位置设置弹出层的位置
$('#tipLayer')
.css({
top: touchEvent.pageY - 100, // 调整这个值以改变弹出层相对于触摸点的位置
left: touchEvent.pageX - ($('#tipLayer').outerWidth() / 2)
})
.data('target', element) // 存储目标元素以便后续操作
.show();
}
function hideTip() {
isLongPress = false;
$('#tipLayer').hide().removeData('target'); // 隐藏tips并清除数据
}
function searchByAllCondition(curr, limit, newKeyword) {
var toUrl = "/book/search?curr=" + curr + "&limit=" + limit;
@ -248,18 +333,35 @@
searchByAllCondition(1, 20, keywords);
}
function read(bookId,contentId){
if(isLongPress){
return false;
}
location.href = '/book/'+bookId+"/"+contentId+".html"
hideTip();
}
</script>
<script>
function toMyCollect() {
var token = localStorage.getItem("token");
if (token) {
window.location.href = "/book/search?token=" + token;
} else {
window.location.href = "/user/login.html";
}
}
function removeFromBookShelf(bookId) {
$.ajax({
type: "delete",
url: "/user/removeFromBookShelf/" + bookId,
data: {},
dataType: "json",
success: function (data) {
if (data.code == 200) {
$("#shelf" + bookId).remove();
}
}
});
}
</script>

View File

@ -53,7 +53,7 @@
更新时间
</th>
<th class="goread">
书签
操作
</th>
</tr>
</thead>
@ -94,7 +94,7 @@
var bookShelfListHtml = "";
for (var i = 0; i < bookShelfList.length; i++) {
var book = bookShelfList[i];
bookShelfListHtml += (" <tr class=\"book_list\" vals=\"291\">\n" +
bookShelfListHtml += (" <tr id='shelf" + book.bookId + "' class=\"book_list\" vals=\"291\">\n" +
" <td class=\"style bookclass\">\n" +
" <a href=\"/book/bookclass.html?c=" + book.catId + "\" >[" + book.catName + "]</a>\n" +
" </td>\n" +
@ -109,7 +109,8 @@
" " + book.lastIndexUpdateTime + "\n" +
" </td>\n" +
" <td class=\"goread\">\n" +
"<a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>继续阅读</a>" +
"<div style=''><a href='/book/" + book.bookId + "/" + book.preContentId + ".html'>继续阅读</a></div>" +
"<div style='line-height:8px;padding-bottom:13px'><a href='javascript:removeFromBookShelf(\"" + book.bookId + "\")'>移出书架</a></div>" +
" </td>\n" +
" </tr>");
}
@ -160,5 +161,21 @@
})
}
function removeFromBookShelf(bookId) {
$.ajax({
type: "delete",
url: "/user/removeFromBookShelf/" + bookId,
data: {},
dataType: "json",
success: function (data) {
if (data.code == 200) {
$("#shelf" + bookId).remove();
}
}
});
}
</script>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -6,8 +6,8 @@
<meta name="keywords" th:content="${application.website.name}+',小说,小说网,言情小说,都市小说,玄幻小说,穿越小说,青春小说,总裁豪门小说,网络小说,免费小说,全本小说,原创网络文学'"/>
<meta name="description"
th:content="${application.website.name}+'每日更新小说连载,小说排行榜,提供言情小说,都市小说,玄幻小说,穿越小说,青春小说,总裁豪门小说,网络小说,免费小说,全本小说,首发小说,最新章节免费小说阅读,精彩尽在'+${application.website.name}+'。'"/>
<link href="favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link href="/favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="/favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link rel="stylesheet" href="/css/main.css"/>
</head>
<body>

View File

@ -7,8 +7,8 @@
<meta name="keywords" th:content="${application.website.keyword}"/>
<meta name="description"
th:content="${application.website.description}"/>
<link href="favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link href="/favicon.ico" type="image/x-icon" rel="shortcut icon"/>
<link href="/favicon.ico" type="image/x-icon" rel="Bookmark"/>
<link rel="stylesheet" href="/css/main.css"/>
</head>
<body>