@ -0,0 +1,9 @@
|
||||
*.md linguist-language=Java
|
||||
*.yml linguist-language=Java
|
||||
*.html linguist-language=Java
|
||||
*.js linguist-language=Java
|
||||
*.xml linguist-language=Java
|
||||
*.css linguist-language=Java
|
||||
*.sql linguist-language=Java
|
||||
*.uml linguist-language=Java
|
||||
*.cmd linguist-language=Java
|
||||
@ -0,0 +1,7 @@
|
||||
**/logs
|
||||
**/.idea
|
||||
**/cachedata
|
||||
**/target
|
||||
**/*.iml
|
||||
**/.DS_Store
|
||||
|
||||
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@ -0,0 +1,82 @@
|
||||
PUT /novel
|
||||
{
|
||||
"mappings" : {
|
||||
"book" : {
|
||||
"properties" : {
|
||||
"id" : {
|
||||
"type" : "long"
|
||||
},
|
||||
"authorId" : {
|
||||
"type" : "long"
|
||||
},
|
||||
"authorName" : {
|
||||
"type" : "text",
|
||||
"analyzer": "ik_smart",
|
||||
"boost": 1.9
|
||||
},
|
||||
|
||||
"bookName" : {
|
||||
"type" : "text",
|
||||
"analyzer": "ik_smart",
|
||||
"boost": 2
|
||||
},
|
||||
|
||||
"bookDesc" : {
|
||||
"type" : "text",
|
||||
"analyzer": "ik_smart",
|
||||
"boost": 0.1
|
||||
},
|
||||
|
||||
"bookStatus" : {
|
||||
"type" : "short"
|
||||
},
|
||||
|
||||
"catId" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
|
||||
"catName" : {
|
||||
"type" : "text",
|
||||
"analyzer": "ik_smart",
|
||||
"boost": 1.0
|
||||
},
|
||||
|
||||
"lastIndexId" : {
|
||||
"type" : "long"
|
||||
},
|
||||
|
||||
"lastIndexName" : {
|
||||
"type" : "text",
|
||||
"analyzer": "ik_smart",
|
||||
"boost": 0.1
|
||||
},
|
||||
|
||||
"lastIndexUpdateTime" : {
|
||||
"type": "keyword"
|
||||
},
|
||||
|
||||
"picUrl" : {
|
||||
"type" : "keyword"
|
||||
},
|
||||
|
||||
"score" : {
|
||||
"type" : "float"
|
||||
},
|
||||
|
||||
"wordCount" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
|
||||
"workDirection" : {
|
||||
"type" : "short"
|
||||
},
|
||||
|
||||
"visitCount" : {
|
||||
"type": "long"
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
INSERT INTO `crawl_source` (`id`, `source_name`, `crawl_rule`, `source_status`, `create_time`, `update_time`) VALUES ('4', '书趣阁', '{\r\n \"bookListUrl\": \"http://m.shuquge.com/sort/{catId}/0_{page}.html\",\r\n \"catIdRule\": {\r\n \"catId1\": \"1\",\r\n \"catId2\": \"2\",\r\n \"catId3\": \"3\",\r\n \"catId4\": \"4\",\r\n \"catId5\": \"7\",\r\n \"catId6\": \"6\",\r\n \"catId7\": \"8\"\r\n },\r\n \"bookIdPatten\": \"href=\\\"/s/(\\\\d+)\\\\.html\\\"\",\r\n \"pagePatten\": \"第(\\\\d+)/\\\\d+页\",\r\n \"totalPagePatten\": \"第\\\\d+/(\\\\d+)页\",\r\n \"bookDetailUrl\": \"http://m.shuquge.com/s/{bookId}.html\",\r\n \"bookNamePatten\": \"<a\\\\s+href=\\\"/s/\\\\d+\\\\.html\\\"><h2>([^/]+)</h2></a>\",\r\n \"authorNamePatten\": \"<p>作者:([^/]+)</p>\",\r\n \"picUrlPatten\": \"src=\\\"(http://www.shuquge.com/files/article/image/\\\\d+/\\\\d+/\\\\d+s\\\\.jpg)\\\"\",\r\n \"statusPatten\": \"<p>状态:([^/]+)</p>\",\r\n \"bookStatusRule\": {\r\n \"连载中\": 0,\r\n \"完本\": 1\r\n },\r\n \"descStart\": \"<div class=\\\"intro_info\\\">\",\r\n \"descEnd\": \"最新章节推荐地址\",\r\n \"bookIndexUrl\": \"http://www.shuquge.com/txt/{bookId}/index.html\",\r\n \"bookIndexStart\": \"》正文\",\r\n \"indexIdPatten\": \"<dd><a\\\\s+href=\\\"(\\\\d+)\\\\.html\\\">[^/]+</a></dd>\",\r\n \"indexNamePatten\": \"<dd><a\\\\s+href=\\\"\\\\d+\\\\.html\\\">([^/]+)</a></dd>\",\r\n \"bookContentUrl\": \"http://www.shuquge.com/txt/{bookId}/{indexId}.html\",\r\n \"contentStart\": \"<div id=\\\"content\\\" class=\\\"showtxt\\\">\",\r\n \"contentEnd\": \"http://www.shuquge.com\"\r\n}', '1', '2020-05-18 12:02:34', '2020-05-18 12:02:34');
|
||||
INSERT INTO `crawl_source` (`id`, `source_name`, `crawl_rule`, `source_status`, `create_time`, `update_time`) VALUES ('5', '笔趣阁', '{\"bookListUrl\":\"http://m.mcmssc.com/xclass/{catId}/{page}.html\",\"catIdRule\":{\"catId1\":\"1\",\"catId2\":\"2\",\"catId3\":\"3\",\"catId4\":\"4\",\"catId5\":\"5\",\"catId6\":\"6\",\"catId7\":\"7\"},\"bookIdPatten\":\"href=\\\"/(\\\\d+_\\\\d+)/\\\"\",\"pagePatten\":\"class=\\\"page_txt\\\"\\\\s+value=\\\"(\\\\d+)/\\\\d+\\\"\\\\s+size=\",\"totalPagePatten\":\"class=\\\"page_txt\\\"\\\\s+value=\\\"\\\\d+/(\\\\d+)\\\"\\\\s+size=\",\"bookDetailUrl\":\"http://m.mcmssc.com/{bookId}/\",\"bookNamePatten\":\"<span\\\\s+class=\\\"title\\\">([^/]+)</span>\",\"authorNamePatten\":\"<a\\\\s+href=\\\"/author/\\\\d+/\\\">([^/]+)</a>\",\"picUrlPatten\":\"<img\\\\s+src=\\\"([^>]+)\\\"\\\\s+onerror=\",\"picUrlPrefix\":\"http://m.mcmssc.com/\",\"statusPatten\":\">状态:([^/]+)<\",\"bookStatusRule\":{\"连载\":0,\"全本\":1},\"visitCountPatten\":\">点击:(\\\\d+)<\",\"descStart\":\"<p class=\\\"review\\\">\",\"descEnd\":\"</p>\",\"bookIndexUrl\":\"http://m.mcmssc.com/{bookId}/all.html\",\"indexIdPatten\":\"<a\\\\s+href=\\\"/\\\\d+_\\\\d+/(\\\\d+)\\\\.html\\\">[^/]+</a>\",\"indexNamePatten\":\"<a\\\\s+href=\\\"/\\\\d+_\\\\d+/\\\\d+\\\\.html\\\">([^/]+)</a>\",\"bookContentUrl\":\"http://www.mcmssc.com/{bookId}/{indexId}.html\",\"contentStart\":\"</p>\",\"contentEnd\":\"<div align=\\\"center\\\">\"}', '1', '2020-05-18 15:57:41', '2020-05-18 15:57:41');
|
||||
UPDATE `crawl_source` SET `source_name` = '书趣阁', `crawl_rule` = '{\n \"bookListUrl\": \"http://m.shuquge.com/sort/{catId}/0_{page}.html\",\n \"catIdRule\": {\n \"catId1\": \"1\",\n \"catId2\": \"2\",\n \"catId3\": \"3\",\n \"catId4\": \"4\",\n \"catId5\": \"7\",\n \"catId6\": \"6\",\n \"catId7\": \"8\"\n },\n \"bookIdPatten\": \"href=\\\"/s/(\\\\d+)\\\\.html\\\"\",\n \"pagePatten\": \"第(\\\\d+)/\\\\d+页\",\n \"totalPagePatten\": \"第\\\\d+/(\\\\d+)页\",\n \"bookDetailUrl\": \"http://m.shuquge.com/s/{bookId}.html\",\n \"bookNamePatten\": \"<a\\\\s+href=\\\"/s/\\\\d+\\\\.html\\\"><h2>([^/]+)</h2></a>\",\n \"authorNamePatten\": \"<p>作者:([^/]+)</p>\",\n \"picUrlPatten\": \"src=\\\"(http://www.shuquge.com/files/article/image/\\\\d+/\\\\d+/\\\\d+s\\\\.jpg)\\\"\",\n \"statusPatten\": \"<p>状态:([^/]+)</p>\",\n \"bookStatusRule\": {\n \"连载中\": 0,\n \"完本\": 1\n },\n \"descStart\": \"<div class=\\\"intro_info\\\">\",\n \"descEnd\": \"最新章节推荐地址\",\n \"bookIndexUrl\": \"http://www.shuquge.com/txt/{bookId}/index.html\",\n \"bookIndexStart\": \"<dt>《\",\n \"indexIdPatten\": \"<dd><a\\\\s+href=\\\"(\\\\d+)\\\\.html\\\">[^/]+</a></dd>\",\n \"indexNamePatten\": \"<dd><a\\\\s+href=\\\"\\\\d+\\\\.html\\\">([^/]+)</a></dd>\",\n \"bookContentUrl\": \"http://www.shuquge.com/txt/{bookId}/{indexId}.html\",\n \"contentStart\": \"<div id=\\\"content\\\" class=\\\"showtxt\\\">\",\n \"contentEnd\": \"http://www.shuquge.com\"\n}', `source_status` = 1, `create_time` = '2020-05-18 12:02:34', `update_time` = '2020-05-18 12:02:34' WHERE `id` = 4;
|
||||
@ -0,0 +1,2 @@
|
||||
INSERT INTO `crawl_source` (`id`, `source_name`, `crawl_rule`, `source_status`, `create_time`, `update_time`) VALUES
|
||||
(6, '新笔趣阁', '{\n \"bookListUrl\": \"http://www.xbiquge.la/fenlei/{catId}_{page}.html\",\n \"catIdRule\": {\n \"catId1\": \"1\",\n \"catId2\": \"2\",\n \"catId3\": \"3\",\n \"catId4\": \"4\",\n \"catId5\": \"6\",\n \"catId6\": \"5\"\n },\n \"bookIdPatten\": \"<a\\\\s+href=\\\"http://www.xbiquge.la/(\\\\d+/\\\\d+)/\\\"\\\\s+target=\\\"_blank\\\">\",\n \"pagePatten\": \"<em\\\\s+id=\\\"pagestats\\\">(\\\\d+)/\\\\d+</em>\",\n \"totalPagePatten\": \"<em\\\\s+id=\\\"pagestats\\\">\\\\d+/(\\\\d+)</em>\",\n \"bookDetailUrl\": \"http://www.xbiquge.la/{bookId}/\",\n \"bookNamePatten\": \"<h1>([^/]+)</h1>\",\n \"authorNamePatten\": \"者:([^/]+)</p>\",\n \"picUrlPatten\": \"src=\\\"(http://www.xbiquge.la/files/article/image/\\\\d+/\\\\d+/\\\\d+s\\\\.jpg)\\\"\",\n \"bookStatusRule\": {},\n \"descStart\": \"<div id=\\\"intro\\\">\",\n \"descEnd\": \"</div>\",\n \"upadateTimePatten\": \"<p>最后更新:(\\\\d+-\\\\d+-\\\\d+\\\\s\\\\d+:\\\\d+:\\\\d+)</p>\",\n \"upadateTimeFormatPatten\": \"yyyy-MM-dd HH:mm:ss\",\n \"bookIndexUrl\": \"http://www.xbiquge.la/{bookId}/\",\n \"indexIdPatten\": \"<a\\\\s+href=\'/\\\\d+/\\\\d+/(\\\\d+)\\\\.html\'\\\\s+>[^/]+</a>\",\n \"indexNamePatten\": \"<a\\\\s+href=\'/\\\\d+/\\\\d+/\\\\d+\\\\.html\'\\\\s+>([^/]+)</a>\",\n \"bookContentUrl\": \"http://www.xbiquge.la/{bookId}/{indexId}.html\",\n \"contentStart\": \"<div id=\\\"content\\\">\",\n \"contentEnd\": \"<p>\"\n}', 0, '2020-05-23 22:46:58', '2020-05-23 22:46:58');
|
||||
@ -0,0 +1 @@
|
||||
alter table book add column `yesterday_buy` int(11) DEFAULT '0' COMMENT '昨日订阅数' after comment_count;
|
||||
@ -0,0 +1,32 @@
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (246, 241, '批量删除', NULL, 'novel:news:batchRemove', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (245, 241, '删除', NULL, 'novel:news:remove', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (244, 241, '修改', NULL, 'novel:news:edit', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (243, 241, '新增', NULL, 'novel:news:add', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (242, 241, '查看', NULL, 'novel:news:detail', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (241, 234, '新闻列表', 'novel/news', 'novel:news:news', 1, 'fa', 8, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (240, 235, '批量删除', NULL, 'novel:category:batchRemove', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (239, 235, '删除', NULL, 'novel:category:remove', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (238, 235, '修改', NULL, 'novel:category:edit', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (237, 235, '新增', NULL, 'novel:category:add', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (236, 235, '查看', NULL, 'novel:category:detail', 2, NULL, 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (235, 234, '类别管理', 'novel/category', 'novel:category:category', 1, 'fa', 6, NULL, NULL);
|
||||
INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`, `gmt_create`, `gmt_modified`) VALUES (234, 0, '新闻管理', '', '', 0, 'fa fa-newspaper-o', 8, NULL, NULL);
|
||||
|
||||
|
||||
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4889, 1, 246);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4890, 1, 245);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4891, 1, 244);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4892, 1, 243);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4893, 1, 242);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4899, 1, 241);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4894, 1, 240);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4895, 1, 239);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4896, 1, 238);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4897, 1, 237);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4898, 1, 236);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4900, 1, 235);
|
||||
INSERT INTO `sys_role_menu`(`id`, `role_id`, `menu_id`) VALUES (4888, 1, 234);
|
||||
|
||||
|
||||
delete from sys_menu where menu_id = 202;
|
||||
@ -0,0 +1 @@
|
||||
alter table news add column `read_count` BIGINT NOT NULL DEFAULT '0' COMMENT '阅读量' after content;
|
||||
@ -0,0 +1,3 @@
|
||||
alter table book_index add column storage_type varchar(10) NOT NULL DEFAULT 'db' COMMENT '存储方式' after book_price ;
|
||||
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
INSERT INTO sys_dict (name, value, type, description, sort, parent_id, create_by, create_date, update_by,
|
||||
update_date, remarks, del_flag)
|
||||
VALUES ('轮播图', '0', 'book_rec_type', '小说推荐类型', 0, null, null, null, null, null, '', null);
|
||||
INSERT INTO sys_dict (name, value, type, description, sort, parent_id, create_by, create_date, update_by,
|
||||
update_date, remarks, del_flag)
|
||||
VALUES ('顶部小说栏', '1', 'book_rec_type', '小说推荐类型', 1, null, null, null, null, null, '', null);
|
||||
INSERT INTO sys_dict (name, value, type, description, sort, parent_id, create_by, create_date, update_by,
|
||||
update_date, remarks, del_flag)
|
||||
VALUES ('本周强推', '2', 'book_rec_type', '小说推荐类型', 2, null, null, null, null, null, '', null);
|
||||
INSERT INTO sys_dict (name, value, type, description, sort, parent_id, create_by, create_date, update_by,
|
||||
update_date, remarks, del_flag)
|
||||
VALUES ('热门推荐', '3', 'book_rec_type', '小说推荐类型', 3, null, null, null, null, null, '', null);
|
||||
INSERT INTO sys_dict (name, value, type, description, sort, parent_id, create_by, create_date, update_by,
|
||||
update_date, remarks, del_flag)
|
||||
VALUES ('精品推荐', '4', 'book_rec_type', '小说推荐类型', 4, null, null, null, null, null, '', null);
|
||||
|
||||
|
||||
|
||||
INSERT INTO `sys_menu` (`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`)
|
||||
VALUES (320, '300', '小说推荐', 'novel/bookSetting', 'novel:bookSetting:bookSetting', '1', 'fa', '6');
|
||||
|
||||
INSERT INTO `sys_menu` (`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`)
|
||||
VALUES (321, '320', '查看', null, 'novel:bookSetting:detail', '2', null, '6');
|
||||
INSERT INTO `sys_menu` (`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`)
|
||||
VALUES (322, '320', '新增', null, 'novel:bookSetting:add', '2', null, '6');
|
||||
INSERT INTO `sys_menu` (`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`)
|
||||
VALUES (323, '320', '修改', null, 'novel:bookSetting:edit', '2', null, '6');
|
||||
INSERT INTO `sys_menu` (`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`)
|
||||
VALUES (324, '320', '删除', null, 'novel:bookSetting:remove', '2', null, '6');
|
||||
INSERT INTO `sys_menu` (`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`)
|
||||
VALUES (325, '320', '批量删除', null, 'novel:bookSetting:batchRemove', '2', null, '6');
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
VALUES (1, 320);
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
VALUES (1, 321);
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
VALUES (1, 322);
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
VALUES (1, 323);
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
VALUES (1, 324);
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
VALUES (1, 325);
|
||||
|
||||
|
||||
INSERT INTO `sys_menu` (`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`)
|
||||
VALUES (410, '400', '会员反馈', 'novel/userFeedback', 'novel:userFeedback:userFeedback', '1', 'fa', '16');
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
VALUES (1, 410);
|
||||
@ -0,0 +1,4 @@
|
||||
INSERT INTO `sys_menu` (`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`)
|
||||
VALUES (305, '301', '修改', null, 'novel:websiteInfo:edit', '2', null, '6');
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
VALUES (1, 305);
|
||||
@ -0,0 +1,3 @@
|
||||
update crawl_source
|
||||
set crawl_rule = replace(crawl_rule, 'ibiquge.net', 'ibiquzw.org')
|
||||
where id = 16;
|
||||
@ -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;
|
||||
@ -0,0 +1,2 @@
|
||||
FROM mysql:8.0
|
||||
COPY novel_plus.sql /docker-entrypoint-initdb.d/init.sql
|
||||
@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>novel</artifactId>
|
||||
<groupId>com.java2nb</groupId>
|
||||
<version>5.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>novel-crawl</artifactId>
|
||||
|
||||
<name>novel-crawl</name>
|
||||
<description>小说精品屋 爬虫管理程序</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.java2nb</groupId>
|
||||
<artifactId>novel-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<!-- 文件夹 -->
|
||||
<copy todir="${project.build.directory}/build/config" overwrite="true">
|
||||
<fileset dir="${basedir}/src/main/build/config">
|
||||
<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"/>
|
||||
|
||||
<fixcrlf srcdir="${basedir}/src/main/build/scripts" eol="unix"/>
|
||||
<copy todir="${project.build.directory}/build/bin">
|
||||
<fileset dir="${basedir}/src/main/build/scripts">
|
||||
<include name="*.sh"/>
|
||||
<include name="*.txt"/>
|
||||
<include name="*.bat"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
<zip destfile='${project.build.directory}/build/${project.artifactId}.zip'>
|
||||
<zipfileset filemode="755" dir='${project.build.directory}/build/'/>
|
||||
</zip>
|
||||
|
||||
<copy file="${basedir}/src/main/build/docker/Dockerfile"
|
||||
tofile="${project.build.directory}/build/Dockerfile"/>
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,9 @@
|
||||
FROM openjdk:8
|
||||
ADD novel-crawl.jar /root
|
||||
ENV dburl=""
|
||||
ENV username=""
|
||||
ENV password=""
|
||||
ENV redishost = ""
|
||||
ENV redisport = ""
|
||||
ENV redispwd = ""
|
||||
ENTRYPOINT ["sh","-c","java -Dspring.datasource.url=${dburl} -Dspring.datasource.username=${username} -Dspring.datasource.password=${password} -Dspring.redis.host=${redishost} -Dspring.redis.port=${redisport} -Dspring.redis.password=${redispwd} -jar /root/novel-crawl.jar"]
|
||||
@ -0,0 +1,39 @@
|
||||
package com.java2nb.novel;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableCaching
|
||||
@EnableScheduling
|
||||
@ServletComponentScan
|
||||
@MapperScan(basePackages = {"com.java2nb.novel.mapper"})
|
||||
@Slf4j
|
||||
public class CrawlNovelApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CrawlNovelApplication.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
|
||||
return args -> {
|
||||
log.info("项目启动啦,访问路径:{}", "http://" + InetAddress.getLocalHost().getHostAddress() + ":" + ctx.getEnvironment().getProperty("server.port"));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,159 @@
|
||||
package com.java2nb.novel.controller;
|
||||
|
||||
import com.java2nb.novel.core.cache.CacheKey;
|
||||
import com.java2nb.novel.core.cache.CacheService;
|
||||
import com.java2nb.novel.core.utils.HttpUtil;
|
||||
import io.github.xxyopen.model.page.PageBean;
|
||||
|
||||
import com.java2nb.novel.entity.CrawlSingleTask;
|
||||
import com.java2nb.novel.entity.CrawlSource;
|
||||
import com.java2nb.novel.service.CrawlService;
|
||||
import io.github.xxyopen.model.resp.RestResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("crawl")
|
||||
@RequiredArgsConstructor
|
||||
public class CrawlController {
|
||||
|
||||
private final CrawlService crawlService;
|
||||
|
||||
private final CacheService cacheService;
|
||||
/**
|
||||
* 新增爬虫源
|
||||
* */
|
||||
@PostMapping("addCrawlSource")
|
||||
public RestResult<Void> addCrawlSource(CrawlSource source){
|
||||
crawlService.addCrawlSource(source);
|
||||
|
||||
return RestResult.ok();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 爬虫源分页列表查询
|
||||
* */
|
||||
@GetMapping("listCrawlByPage")
|
||||
public RestResult<PageBean<CrawlSource>> listCrawlByPage(@RequestParam(value = "curr", defaultValue = "1") int page, @RequestParam(value = "limit", defaultValue = "10") int pageSize){
|
||||
|
||||
return RestResult.ok(crawlService.listCrawlByPage(page,pageSize));
|
||||
}
|
||||
/**
|
||||
* 获取爬虫源
|
||||
* */
|
||||
@GetMapping("getCrawlSource/{id}")
|
||||
public RestResult<CrawlSource> getCrawlSource(@PathVariable("id") Integer id){
|
||||
CrawlSource crawlSource= crawlService.getCrawlSource(id);
|
||||
return RestResult.ok(crawlSource);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试规则
|
||||
* @param rule
|
||||
* @param url
|
||||
* @param isRefresh
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("testParse")
|
||||
public RestResult<Object> testParse(String rule,String url,String isRefresh){
|
||||
|
||||
Map<String,Object> resultMap=new HashMap<>();
|
||||
String html =null;
|
||||
if(url.startsWith("https://")||url.startsWith("http://")){
|
||||
String refreshCache="1";
|
||||
if(!refreshCache.equals(isRefresh)) {
|
||||
Object cache = cacheService.getObject(CacheKey.BOOK_TEST_PARSE + url);
|
||||
if (cache == null) {
|
||||
isRefresh="1";
|
||||
}else {
|
||||
html = (String) cache;
|
||||
}
|
||||
}
|
||||
if(refreshCache.equals(isRefresh)){
|
||||
html = HttpUtil.getByHttpClientWithChrome(url);
|
||||
if (html != null) {
|
||||
cacheService.setObject(CacheKey.BOOK_TEST_PARSE + url, html, 60 * 10);
|
||||
}else{
|
||||
resultMap.put("msg","html is null");
|
||||
return RestResult.ok(resultMap);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
resultMap.put("html","url is null");
|
||||
return RestResult.ok(resultMap);
|
||||
}
|
||||
Pattern pattern = Pattern.compile(rule);
|
||||
Matcher matcher = pattern.matcher(html);
|
||||
boolean isFind = matcher.find();
|
||||
resultMap.put("是否匹配",isFind);
|
||||
if(isFind){
|
||||
resultMap.put("匹配结果",matcher.group(1));
|
||||
}
|
||||
// resultMap.put("url",url);
|
||||
return RestResult.ok(resultMap);
|
||||
}
|
||||
/**
|
||||
* 修改爬虫源
|
||||
* */
|
||||
@PostMapping("updateCrawlSource")
|
||||
public RestResult<Void> updateCrawlSource(CrawlSource source) {
|
||||
crawlService.updateCrawlSource(source);
|
||||
return RestResult.ok();
|
||||
|
||||
}
|
||||
/**
|
||||
* 开启或停止爬虫
|
||||
* */
|
||||
@PostMapping("openOrCloseCrawl")
|
||||
public RestResult<Void> openOrCloseCrawl(Integer sourceId,Byte sourceStatus){
|
||||
|
||||
crawlService.openOrCloseCrawl(sourceId,sourceStatus);
|
||||
|
||||
return RestResult.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增单本采集任务
|
||||
* */
|
||||
@PostMapping("addCrawlSingleTask")
|
||||
public RestResult<Void> addCrawlSingleTask(CrawlSingleTask singleTask){
|
||||
crawlService.addCrawlSingleTask(singleTask);
|
||||
|
||||
return RestResult.ok();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 单本采集任务分页列表查询
|
||||
* */
|
||||
@GetMapping("listCrawlSingleTaskByPage")
|
||||
public RestResult<PageBean<CrawlSingleTask>> listCrawlSingleTaskByPage(@RequestParam(value = "curr", defaultValue = "1") int page, @RequestParam(value = "limit", defaultValue = "10") int pageSize){
|
||||
|
||||
return RestResult.ok(crawlService.listCrawlSingleTaskByPage(page,pageSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除采集任务
|
||||
* */
|
||||
@DeleteMapping("delCrawlSingleTask/{id}")
|
||||
public RestResult<Void> delCrawlSingleTask(@PathVariable("id") Long id){
|
||||
|
||||
crawlService.delCrawlSingleTask(id);
|
||||
|
||||
return RestResult.ok();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.java2nb.novel.controller.page;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* @author 11797
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Controller
|
||||
public class PageController {
|
||||
|
||||
|
||||
@RequestMapping("{url}.html")
|
||||
public String module(@PathVariable("url") String url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
@RequestMapping("{module}/{url}.html")
|
||||
public String module2(@PathVariable("module") String module, @PathVariable("url") String url) {
|
||||
return module + "/" + url;
|
||||
}
|
||||
|
||||
@RequestMapping("{module}/{classify}/{url}.html")
|
||||
public String module3(@PathVariable("module") String module, @PathVariable("classify") String classify,
|
||||
@PathVariable("url") String url) {
|
||||
return module + "/" + classify + "/" + url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 首页
|
||||
*/
|
||||
@RequestMapping(path = {"/", "/index", "/index.html"})
|
||||
public String index() {
|
||||
return "crawl/crawlSource_list";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package com.java2nb.novel.core.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
/**
|
||||
* SpringSecurity配置
|
||||
*
|
||||
* @author Administrator
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfiguration {
|
||||
|
||||
@Value("${admin.username}")
|
||||
private String username;
|
||||
|
||||
@Value("${admin.password}")
|
||||
private String password;
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InMemoryUserDetailsManager userDetailsService() {
|
||||
UserDetails admin = User.builder()
|
||||
.username(username)
|
||||
.password(passwordEncoder().encode(password))
|
||||
.roles("ADMIN")
|
||||
.build();
|
||||
return new InMemoryUserDetailsManager(admin);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(csrf -> csrf.disable()) // 禁用 CSRF
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/css/**", "/favicon.ico").permitAll() // 允许访问静态资源
|
||||
.anyRequest().hasRole("ADMIN") // 其他请求需要 ADMIN 角色
|
||||
)
|
||||
.formLogin(form -> form
|
||||
.loginPage("/login.html") // 自定义登录页面
|
||||
.loginProcessingUrl("/login") // 登录处理 URL
|
||||
.permitAll()
|
||||
)
|
||||
.logout(logout -> logout
|
||||
.logoutUrl("/logout") // 登出 URL
|
||||
.logoutSuccessUrl("/") // 登出成功后跳转的页面
|
||||
)
|
||||
.httpBasic(Customizer.withDefaults()); // 启用 HTTP Basic 认证
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.java2nb.novel.core.crawl;
|
||||
|
||||
import com.java2nb.novel.entity.BookContent;
|
||||
import com.java2nb.novel.entity.BookIndex;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 章节数据封装bean
|
||||
* @author Administrator
|
||||
*/
|
||||
@Data
|
||||
public class ChapterBean {
|
||||
|
||||
/**
|
||||
* 章节索引集合
|
||||
* */
|
||||
List<BookIndex> bookIndexList;
|
||||
|
||||
/**
|
||||
* 章节内容集合
|
||||
* */
|
||||
List<BookContent> bookContentList;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.java2nb.novel.core.crawl;
|
||||
|
||||
import com.java2nb.novel.entity.Book;
|
||||
|
||||
/**
|
||||
* 爬虫小说章节内容处理器
|
||||
* */
|
||||
public interface CrawlBookChapterHandler {
|
||||
|
||||
void handle(ChapterBean chapterBean);
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.java2nb.novel.core.crawl;
|
||||
|
||||
import com.java2nb.novel.entity.Book;
|
||||
|
||||
/**
|
||||
* 爬虫小说处理器
|
||||
* */
|
||||
public interface CrawlBookHandler {
|
||||
|
||||
void handle(Book book);
|
||||
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.java2nb.novel.core.crawl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author xiongxiaoyang
|
||||
*/
|
||||
public class PatternFactory {
|
||||
|
||||
private static final Map<String, Pattern> PATTERN_CACHED_MAP = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 根据正则表达式获取一个预编译的Pattern对象
|
||||
*/
|
||||
public static Pattern getPattern(String regex) {
|
||||
Pattern pattern = PATTERN_CACHED_MAP.get(regex);
|
||||
if (Objects.isNull(pattern)) {
|
||||
pattern = Pattern.compile(regex);
|
||||
PATTERN_CACHED_MAP.put(regex, pattern);
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
package com.java2nb.novel.core.crawl;
|
||||
|
||||
import com.java2nb.novel.utils.Constants;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 爬虫解析规则bean
|
||||
*
|
||||
* @author Administrator
|
||||
*/
|
||||
@Data
|
||||
public class RuleBean {
|
||||
|
||||
/**
|
||||
* 网页字符编码
|
||||
*/
|
||||
private String charset = Constants.CRAWL_DEFAULT_CHARSET;
|
||||
|
||||
|
||||
/**
|
||||
* 小说更新列表url
|
||||
*/
|
||||
private String updateBookListUrl;
|
||||
|
||||
/**
|
||||
* 分类列表页URL规则
|
||||
*/
|
||||
private String bookListUrl;
|
||||
|
||||
private Map<String, String> catIdRule;
|
||||
|
||||
private Map<String, Byte> bookStatusRule;
|
||||
|
||||
private String bookIdPatten;
|
||||
private String pagePatten;
|
||||
private String totalPagePatten;
|
||||
private String bookDetailUrl;
|
||||
private String bookNamePatten;
|
||||
private String authorNamePatten;
|
||||
private String picUrlPatten;
|
||||
private String statusPatten;
|
||||
private String scorePatten;
|
||||
private String visitCountPatten;
|
||||
private String descStart;
|
||||
private String descEnd;
|
||||
private String upadateTimePatten;
|
||||
private String upadateTimeFormatPatten;
|
||||
private String bookIndexUrl;
|
||||
private String indexIdPatten;
|
||||
private String indexNamePatten;
|
||||
private String bookContentUrl;
|
||||
private String contentStart;
|
||||
private String contentEnd;
|
||||
|
||||
|
||||
private String picUrlPrefix;
|
||||
|
||||
private String bookIndexStart;
|
||||
|
||||
private String filterContent;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,134 @@
|
||||
package com.java2nb.novel.core.listener;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.java2nb.novel.core.crawl.CrawlParser;
|
||||
import com.java2nb.novel.core.crawl.RuleBean;
|
||||
import com.java2nb.novel.entity.Book;
|
||||
import com.java2nb.novel.entity.BookIndex;
|
||||
import com.java2nb.novel.entity.CrawlSingleTask;
|
||||
import com.java2nb.novel.entity.CrawlSource;
|
||||
import com.java2nb.novel.service.BookService;
|
||||
import com.java2nb.novel.service.CrawlService;
|
||||
import com.java2nb.novel.utils.Constants;
|
||||
import jakarta.servlet.ServletContext;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class StarterListener implements ServletContextInitializer {
|
||||
|
||||
private final BookService bookService;
|
||||
|
||||
private final CrawlService crawlService;
|
||||
|
||||
private final CrawlParser crawlParser;
|
||||
|
||||
@Value("${crawl.update.thread}")
|
||||
private int updateThreadCount;
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) {
|
||||
for (int i = 0; i < updateThreadCount; i++) {
|
||||
new Thread(() -> {
|
||||
log.info("程序启动,开始执行自动更新线程。。。");
|
||||
while (true) {
|
||||
try {
|
||||
//1.查询最新目录更新时间在一个月之内的前100条需要更新的数据
|
||||
Date currentDate = new Date();
|
||||
Date startDate = DateUtils.addDays(currentDate, -30);
|
||||
List<Book> bookList;
|
||||
synchronized (this) {
|
||||
bookList = bookService.queryNeedUpdateBook(startDate, 100);
|
||||
}
|
||||
for (Book needUpdateBook : bookList) {
|
||||
try {
|
||||
//查询爬虫源规则
|
||||
CrawlSource source = crawlService.queryCrawlSource(needUpdateBook.getCrawlSourceId());
|
||||
RuleBean ruleBean = new ObjectMapper().readValue(source.getCrawlRule(), RuleBean.class);
|
||||
//解析小说基本信息
|
||||
crawlParser.parseBook(ruleBean, needUpdateBook.getCrawlBookId(), book -> {
|
||||
//这里只做老书更新
|
||||
book.setId(needUpdateBook.getId());
|
||||
book.setWordCount(needUpdateBook.getWordCount());
|
||||
if (needUpdateBook.getPicUrl() != null && needUpdateBook.getPicUrl()
|
||||
.contains(Constants.LOCAL_PIC_PREFIX)) {
|
||||
//本地图片则不更新
|
||||
book.setPicUrl(null);
|
||||
}
|
||||
//查询已存在的章节
|
||||
Map<Integer, BookIndex> existBookIndexMap = bookService.queryExistBookIndexMap(
|
||||
needUpdateBook.getId());
|
||||
//解析章节目录
|
||||
crawlParser.parseBookIndexAndContent(needUpdateBook.getCrawlBookId(), book,
|
||||
ruleBean, existBookIndexMap, chapter -> {
|
||||
bookService.updateBookAndIndexAndContent(book, chapter.getBookIndexList(),
|
||||
chapter.getBookContentList(), existBookIndexMap);
|
||||
});
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
// 休眠10分钟
|
||||
TimeUnit.MINUTES.sleep(10);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
}).start();
|
||||
|
||||
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
log.info("程序启动,开始执行单本采集任务线程。。。");
|
||||
while (true) {
|
||||
CrawlSingleTask task = null;
|
||||
byte crawlStatus = 0;
|
||||
try {
|
||||
//获取采集任务
|
||||
task = crawlService.getCrawlSingleTask();
|
||||
|
||||
if (task != null) {
|
||||
//查询爬虫规则
|
||||
CrawlSource source = crawlService.queryCrawlSource(task.getSourceId());
|
||||
RuleBean ruleBean = new ObjectMapper().readValue(source.getCrawlRule(), RuleBean.class);
|
||||
|
||||
if (crawlService.parseBookAndSave(task.getCatId(), ruleBean, task.getSourceId(),
|
||||
task.getSourceBookId())) {
|
||||
//采集成功
|
||||
crawlStatus = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//休眠1分钟
|
||||
TimeUnit.MINUTES.sleep(1);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
if (task != null) {
|
||||
crawlService.updateCrawlSingleTask(task, crawlStatus);
|
||||
}
|
||||
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.java2nb.novel.mapper;
|
||||
|
||||
import com.java2nb.novel.entity.BookIndex;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
public interface CrawlBookIndexMapper extends BookIndexMapper {
|
||||
|
||||
|
||||
/**
|
||||
* 查询最后的章节
|
||||
* */
|
||||
BookIndex queryLastIndex(@Param("bookId") Long bookId);
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.java2nb.novel.mapper;
|
||||
|
||||
import com.java2nb.novel.entity.Book;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
public interface CrawlBookMapper extends BookMapper {
|
||||
|
||||
/**
|
||||
* 查询需要更新的小说数据
|
||||
* @param startDate 最新更新时间的起始时间
|
||||
* @param limit 查询条数
|
||||
* @return 小说集合
|
||||
* */
|
||||
List<Book> queryNeedUpdateBook(@Param("startDate") Date startDate, @Param("limit") int limit);
|
||||
|
||||
/**
|
||||
* 查询小说总字数
|
||||
* @param bookId 小说ID
|
||||
* @return 小说总字数
|
||||
* */
|
||||
Integer queryTotalWordCount(@Param("bookId") Long bookId);
|
||||
|
||||
/**
|
||||
* 批量更新小说最后抓取时间
|
||||
* @param books 需要更新的小说集合
|
||||
* @param currentDate 当前时间
|
||||
* */
|
||||
void updateCrawlLastTime(@Param("books") List<Book> books,@Param("currentDate") Date currentDate);
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.java2nb.novel.service;
|
||||
|
||||
import com.java2nb.novel.entity.BookContent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface BookContentService {
|
||||
|
||||
void saveBookContent(List<BookContent> bookContentList,Long bookId);
|
||||
|
||||
void saveBookContent(BookContent bookContent,Long bookId);
|
||||
|
||||
void updateBookContent(BookContent bookContent,Long bookId);
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,183 @@
|
||||
package com.java2nb.novel.service.impl;
|
||||
|
||||
import com.java2nb.novel.entity.Book;
|
||||
import com.java2nb.novel.entity.BookContent;
|
||||
import com.java2nb.novel.entity.BookIndex;
|
||||
import com.java2nb.novel.mapper.*;
|
||||
import com.java2nb.novel.service.BookContentService;
|
||||
import com.java2nb.novel.service.BookService;
|
||||
import com.java2nb.novel.utils.Constants;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.mybatis.dynamic.sql.render.RenderingStrategies;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.java2nb.novel.mapper.BookDynamicSqlSupport.crawlBookId;
|
||||
import static com.java2nb.novel.mapper.BookDynamicSqlSupport.crawlSourceId;
|
||||
import static com.java2nb.novel.mapper.CrawlSourceDynamicSqlSupport.id;
|
||||
import static org.mybatis.dynamic.sql.SqlBuilder.*;
|
||||
import static org.mybatis.dynamic.sql.select.SelectDSL.select;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class BookServiceImpl implements BookService {
|
||||
|
||||
private final CrawlBookMapper bookMapper;
|
||||
|
||||
private final BookCategoryMapper bookCategoryMapper;
|
||||
|
||||
private final CrawlBookIndexMapper bookIndexMapper;
|
||||
|
||||
private final Map<String, BookContentService> bookContentServiceMap;
|
||||
|
||||
@Value("${content.save.storage}")
|
||||
private String storageType;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean queryIsExistByBookNameAndAuthorName(String bookName, String authorName) {
|
||||
|
||||
return bookMapper.count(countFrom(BookDynamicSqlSupport.book).where(BookDynamicSqlSupport.bookName, isEqualTo(bookName))
|
||||
.and(BookDynamicSqlSupport.authorName, isEqualTo(authorName))
|
||||
.build()
|
||||
.render(RenderingStrategies.MYBATIS3)) > 0;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCrawlProperties(Long id, Integer sourceId, String bookId) {
|
||||
bookMapper.update(update(BookDynamicSqlSupport.book)
|
||||
.set(crawlSourceId)
|
||||
.equalTo(sourceId)
|
||||
.set(crawlBookId)
|
||||
.equalTo(bookId)
|
||||
.where(BookDynamicSqlSupport.id, isEqualTo(id))
|
||||
.build()
|
||||
.render(RenderingStrategies.MYBATIS3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String queryCatNameByCatId(int catId) {
|
||||
return bookCategoryMapper.selectMany(select(BookCategoryDynamicSqlSupport.name)
|
||||
.from(BookCategoryDynamicSqlSupport.bookCategory)
|
||||
.where(id, isEqualTo(catId))
|
||||
.build()
|
||||
.render(RenderingStrategies.MYBATIS3)).get(0).getName();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void saveBookAndIndexAndContent(Book book, List<BookIndex> bookIndexList, List<BookContent> bookContentList) {
|
||||
if (!queryIsExistByBookNameAndAuthorName(book.getBookName(), book.getAuthorName())) {
|
||||
|
||||
if (bookIndexList.size() > 0) {
|
||||
|
||||
//保存小说主表
|
||||
|
||||
book.setCreateTime(new Date());
|
||||
bookMapper.insertSelective(book);
|
||||
|
||||
//批量保存目录和内容
|
||||
bookIndexList.forEach(bookIndex -> {
|
||||
bookIndex.setStorageType(storageType);
|
||||
});
|
||||
bookIndexMapper.insertMultiple(bookIndexList);
|
||||
bookContentServiceMap.get(storageType).saveBookContent(bookContentList, book.getId());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Book> queryNeedUpdateBook(Date startDate, int limit) {
|
||||
List<Book> books = bookMapper.queryNeedUpdateBook(startDate, limit);
|
||||
if (books.size() > 0) {
|
||||
//更新最后抓取时间为当前时间
|
||||
bookMapper.updateCrawlLastTime(books, new Date());
|
||||
}
|
||||
return books;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, BookIndex> queryExistBookIndexMap(Long bookId) {
|
||||
List<BookIndex> bookIndexs = bookIndexMapper.selectMany(select(BookIndexDynamicSqlSupport.id, BookIndexDynamicSqlSupport.indexNum, BookIndexDynamicSqlSupport.indexName, BookIndexDynamicSqlSupport.wordCount, BookIndexDynamicSqlSupport.storageType)
|
||||
.from(BookIndexDynamicSqlSupport.bookIndex)
|
||||
.where(BookIndexDynamicSqlSupport.bookId, isEqualTo(bookId))
|
||||
.build()
|
||||
.render(RenderingStrategies.MYBATIS3));
|
||||
if (bookIndexs.size() > 0) {
|
||||
return bookIndexs.stream().collect(Collectors.toMap(BookIndex::getIndexNum, Function.identity()));
|
||||
}
|
||||
return new HashMap<>(0);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void updateBookAndIndexAndContent(Book book, List<BookIndex> bookIndexList, List<BookContent> bookContentList, Map<Integer, BookIndex> existBookIndexMap) {
|
||||
for (int i = 0; i < bookIndexList.size(); i++) {
|
||||
BookIndex bookIndex = bookIndexList.get(i);
|
||||
BookContent bookContent = bookContentList.get(i);
|
||||
|
||||
|
||||
if (!existBookIndexMap.containsKey(bookIndex.getIndexNum())) {
|
||||
//插入
|
||||
bookIndex.setStorageType(storageType);
|
||||
bookIndexMapper.insertSelective(bookIndex);
|
||||
bookContentServiceMap.get(storageType).saveBookContent(bookContent, book.getId());
|
||||
} else {
|
||||
//更新
|
||||
bookIndexMapper.updateByPrimaryKeySelective(bookIndex);
|
||||
bookContentServiceMap.get(existBookIndexMap.get(bookIndex.getIndexNum()).getStorageType()).updateBookContent(bookContent, book.getId());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//更新小说主表
|
||||
book.setBookName(null);
|
||||
book.setAuthorName(null);
|
||||
if (Constants.VISIT_COUNT_DEFAULT.equals(book.getVisitCount())) {
|
||||
book.setVisitCount(null);
|
||||
}
|
||||
bookMapper.updateByPrimaryKeySelective(book);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCrawlLastTime(Long bookId) {
|
||||
Book book = new Book();
|
||||
book.setId(bookId);
|
||||
book.setCrawlLastTime(new Date());
|
||||
bookMapper.updateByPrimaryKeySelective(book);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Book queryBookByBookNameAndAuthorName(String bookName, String authorName) {
|
||||
List<Book> books = bookMapper.selectMany(select(BookDynamicSqlSupport.id).from(BookDynamicSqlSupport.book)
|
||||
.where(BookDynamicSqlSupport.bookName, isEqualTo(bookName))
|
||||
.and(BookDynamicSqlSupport.authorName, isEqualTo(authorName))
|
||||
.build()
|
||||
.render(RenderingStrategies.MYBATIS3));
|
||||
|
||||
if (books.size() > 0) {
|
||||
return books.get(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.java2nb.novel.service.impl;
|
||||
|
||||
import com.java2nb.novel.entity.BookContent;
|
||||
import com.java2nb.novel.mapper.BookContentDynamicSqlSupport;
|
||||
import com.java2nb.novel.mapper.BookContentMapper;
|
||||
import com.java2nb.novel.service.BookContentService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.mybatis.dynamic.sql.render.RenderingStrategies;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo;
|
||||
import static org.mybatis.dynamic.sql.SqlBuilder.update;
|
||||
|
||||
|
||||
@Service(value = "db")
|
||||
@RequiredArgsConstructor
|
||||
public class DbBookContentServiceImpl implements BookContentService {
|
||||
|
||||
private final BookContentMapper bookContentMapper;
|
||||
|
||||
@Override
|
||||
public void saveBookContent(List<BookContent> bookContentList,Long bookId) {
|
||||
bookContentMapper.insertMultiple(bookContentList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveBookContent(BookContent bookContent,Long bookId) {
|
||||
bookContentMapper.insertSelective(bookContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBookContent(BookContent bookContent,Long bookId) {
|
||||
bookContentMapper.update(update(BookContentDynamicSqlSupport.bookContent)
|
||||
.set(BookContentDynamicSqlSupport.content)
|
||||
.equalTo(bookContent.getContent())
|
||||
.where(BookContentDynamicSqlSupport.indexId,isEqualTo(bookContent.getIndexId()))
|
||||
.build()
|
||||
.render(RenderingStrategies.MYBATIS3));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.java2nb.novel.service.impl;
|
||||
|
||||
import com.java2nb.novel.core.utils.FileUtil;
|
||||
import com.java2nb.novel.entity.BookContent;
|
||||
import com.java2nb.novel.service.BookContentService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service(value = "txt")
|
||||
@RequiredArgsConstructor
|
||||
public class FileBookContentServiceImpl implements BookContentService {
|
||||
|
||||
@Value("${content.save.path}")
|
||||
private String fileSavePath;
|
||||
|
||||
@Override
|
||||
public void saveBookContent(List<BookContent> bookContentList,Long bookId) {
|
||||
bookContentList.forEach(bookContent -> saveBookContent(bookContent,bookId));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveBookContent(BookContent bookContent,Long bookId) {
|
||||
FileUtil.writeContentToFile(fileSavePath,"/"+bookId+"/"+bookContent.getIndexId()+".txt",bookContent.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBookContent(BookContent bookContent,Long bookId) {
|
||||
FileUtil.writeContentToFile(fileSavePath,"/"+bookId+"/"+bookContent.getIndexId()+".txt",bookContent.getContent());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.java2nb.novel.utils;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
/**
|
||||
* 本地图片保存前缀
|
||||
*/
|
||||
public static final String LOCAL_PIC_PREFIX = "/localPic/";
|
||||
|
||||
/**
|
||||
* 访问量默认值
|
||||
*/
|
||||
public static final Long VISIT_COUNT_DEFAULT = 100L;
|
||||
|
||||
/**
|
||||
* 爬取小说http请求中无效的内容长度
|
||||
*/
|
||||
public static final int INVALID_HTML_LENGTH = 1500;
|
||||
|
||||
/**
|
||||
* 爬取小说http请求失败重试次数
|
||||
*/
|
||||
public static final Integer HTTP_FAIL_RETRY_COUNT = 3;
|
||||
|
||||
/**
|
||||
* 爬虫默认编码
|
||||
*/
|
||||
public static final String CRAWL_DEFAULT_CHARSET = "UTF-8";
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.java2nb.novel.utils;
|
||||
|
||||
import com.java2nb.novel.core.utils.HttpUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CrawlHttpClient {
|
||||
|
||||
@Value("${crawl.interval.min}")
|
||||
private Integer intervalMin;
|
||||
|
||||
@Value("${crawl.interval.max}")
|
||||
private Integer intervalMax;
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
private static final ThreadLocal<Integer> RETRY_COUNT = new ThreadLocal<>();
|
||||
|
||||
public String get(String url, String charset) {
|
||||
if (Objects.nonNull(intervalMin) && Objects.nonNull(intervalMax) && intervalMax > intervalMin) {
|
||||
try {
|
||||
Thread.sleep(random.nextInt(intervalMax - intervalMin + 1) + intervalMin);
|
||||
} catch (InterruptedException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
String body = HttpUtil.getByHttpClientWithChrome(url, charset);
|
||||
if (Objects.isNull(body) || body.length() < Constants.INVALID_HTML_LENGTH) {
|
||||
return processErrorHttpResult(url, charset);
|
||||
}
|
||||
//成功获得html内容
|
||||
return body;
|
||||
}
|
||||
|
||||
private String processErrorHttpResult(String url, String charset) {
|
||||
Integer count = RETRY_COUNT.get();
|
||||
if (count == null) {
|
||||
count = 0;
|
||||
}
|
||||
if (count < Constants.HTTP_FAIL_RETRY_COUNT) {
|
||||
RETRY_COUNT.set(++count);
|
||||
return get(url, charset);
|
||||
}
|
||||
RETRY_COUNT.remove();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package com.java2nb.novel.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.java2nb.novel.entity.CrawlSingleTask;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class CrawlSingleTaskVO extends CrawlSingleTask {
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm")
|
||||
private Date createTime;
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.java2nb.novel.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.java2nb.novel.entity.CrawlSource;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class CrawlSourceVO extends CrawlSource{
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm")
|
||||
private Date createTime;
|
||||
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm")
|
||||
private Date updateTime;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
spring:
|
||||
config:
|
||||
import: classpath:application-common-dev.yml
|
||||
@ -0,0 +1,3 @@
|
||||
spring:
|
||||
config:
|
||||
import: classpath:application-common-prod.yml
|
||||
@ -0,0 +1,3 @@
|
||||
spring:
|
||||
profiles:
|
||||
include: [common-test]
|
||||
|
After Width: | Height: | Size: 162 KiB |
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.java2nb.novel.mapper.CrawlBookIndexMapper">
|
||||
|
||||
<select id="queryLastIndex" parameterType="long" resultType="com.java2nb.novel.entity.BookIndex">
|
||||
|
||||
|
||||
select id,index_name,update_time
|
||||
from book_index where book_id = #{bookId}
|
||||
order by index_num DESC limit 1
|
||||
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.java2nb.novel.mapper.CrawlBookMapper">
|
||||
|
||||
<select id="queryNeedUpdateBook" resultType="com.java2nb.novel.entity.Book">
|
||||
|
||||
select id,crawl_source_id,crawl_book_id,crawl_last_time,pic_url,word_count
|
||||
from book where last_index_update_time > #{startDate} and crawl_source_id is not null
|
||||
order by crawl_last_time
|
||||
limit ${limit}
|
||||
|
||||
|
||||
</select>
|
||||
|
||||
<select id="queryTotalWordCount" parameterType="long" resultType="int">
|
||||
|
||||
select sum(word_count) from book_index where book_id = #{bookId}
|
||||
</select>
|
||||
|
||||
<update id="updateCrawlLastTime">
|
||||
update book set crawl_last_time = #{currentDate}
|
||||
where id in
|
||||
<foreach item="book" collection="books" open="(" separator="," close=")">
|
||||
#{book.id}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,967 @@
|
||||
@charset "utf-8";
|
||||
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, p, a, blockquote, th {
|
||||
margin: 0;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
ol, ul, li {
|
||||
list-style: none outside none
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapsse;
|
||||
border-spacing: 0
|
||||
}
|
||||
|
||||
fieldset, img {
|
||||
border: 0 none
|
||||
}
|
||||
|
||||
/*html { background: ##f5f5f5 }*/
|
||||
body {
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
font: 12px/1.5 PingFangSC-Regular, HelveticaNeue-Light, 'Helvetica Neue Light', 'Microsoft YaHei', sans-serif, "宋体";
|
||||
text-align: left
|
||||
}
|
||||
|
||||
input::-moz-focus-inner {
|
||||
border: none;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none
|
||||
}
|
||||
|
||||
a {
|
||||
outline: none;
|
||||
color: #333;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
a:hover, .topBar a:hover, .red, .record_list li:hover .read_link a {
|
||||
color: #3eaf7c
|
||||
}
|
||||
|
||||
.red1 {
|
||||
color: #ff4040
|
||||
}
|
||||
|
||||
.unlink {
|
||||
text-decoration: underline
|
||||
}
|
||||
|
||||
.blue {
|
||||
color: #5fc3f3
|
||||
}
|
||||
|
||||
.green {
|
||||
color: #360
|
||||
}
|
||||
|
||||
.black {
|
||||
color: #000
|
||||
}
|
||||
|
||||
.black3 {
|
||||
color: #333
|
||||
}
|
||||
|
||||
.black6 {
|
||||
color: #666
|
||||
}
|
||||
|
||||
.black9 {
|
||||
color: #999
|
||||
}
|
||||
|
||||
.ccc {
|
||||
color: #ccc
|
||||
}
|
||||
|
||||
.orange {
|
||||
color: #f60
|
||||
}
|
||||
|
||||
.font12 {
|
||||
font-size: 12px !important
|
||||
}
|
||||
|
||||
.font14 {
|
||||
font-size: 14px !important
|
||||
}
|
||||
|
||||
.font16 {
|
||||
font-size: 16px !important
|
||||
}
|
||||
|
||||
.font18 {
|
||||
font-size: 18px !important
|
||||
}
|
||||
|
||||
.font20 {
|
||||
font-size: 20px !important
|
||||
}
|
||||
|
||||
.font26 {
|
||||
font-size: 26px !important
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-break: keep-all
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: none;
|
||||
outline: none;
|
||||
border: 1px solid #CCC;
|
||||
font: 12px/1.8 "microsoft yahei", Arial;
|
||||
padding-left: 5px
|
||||
}
|
||||
|
||||
input {
|
||||
outline: none;
|
||||
border: none; /* padding-left: 5px; font-size: 13px;*/
|
||||
font-family: "microsoft yahei", Arial;
|
||||
*background: none
|
||||
}
|
||||
|
||||
i, em, cite {
|
||||
font-style: normal
|
||||
}
|
||||
|
||||
.layui-inline, input, label {
|
||||
vertical-align: middle
|
||||
}
|
||||
|
||||
button, input, optgroup, select, textarea {
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
outline: 0
|
||||
}
|
||||
|
||||
button, select {
|
||||
text-transform: none
|
||||
}
|
||||
|
||||
/*select { -webkit-appearance: none; border: none }*/
|
||||
input {
|
||||
line-height: normal
|
||||
}
|
||||
|
||||
input[type=checkbox], input[type=radio] {
|
||||
box-sizing: border-box;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button {
|
||||
height: auto
|
||||
}
|
||||
|
||||
input[type=search] {
|
||||
-webkit-appearance: textfield;
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box
|
||||
}
|
||||
|
||||
input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-decoration {
|
||||
-webkit-appearance: none
|
||||
}
|
||||
|
||||
input[type="submit"], input[type="reset"], input[type="button"], button {
|
||||
-webkit-appearance: none
|
||||
}
|
||||
|
||||
:-moz-placeholder {
|
||||
color: #999
|
||||
}
|
||||
|
||||
::-moz-placeholder {
|
||||
color: #999
|
||||
}
|
||||
|
||||
input:-ms-input-placeholder, textarea:-ms-input-placeholder {
|
||||
color: #999
|
||||
}
|
||||
|
||||
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
|
||||
color: #999
|
||||
}
|
||||
|
||||
.cf {
|
||||
zoom: 1
|
||||
}
|
||||
|
||||
.cf:before, .cf:after {
|
||||
content: "";
|
||||
display: table;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
height: 0;
|
||||
line-height: 0;
|
||||
clear: both;
|
||||
visibility: hidden
|
||||
}
|
||||
|
||||
.cf:after {
|
||||
clear: both
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both
|
||||
}
|
||||
|
||||
.tl {
|
||||
text-align: left
|
||||
}
|
||||
|
||||
.tc {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.tr {
|
||||
text-align: right
|
||||
}
|
||||
|
||||
.fl {
|
||||
float: left
|
||||
}
|
||||
|
||||
.fr {
|
||||
float: right
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block
|
||||
}
|
||||
|
||||
.none, .hidden {
|
||||
display: none
|
||||
}
|
||||
|
||||
/*base end*/
|
||||
.channelWrap {
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px
|
||||
}
|
||||
|
||||
.channelWrap.channelBanner {
|
||||
padding-bottom: 14px
|
||||
}
|
||||
|
||||
.wrap_left {
|
||||
width: 750px
|
||||
}
|
||||
|
||||
.wrap_right {
|
||||
width: 250px
|
||||
}
|
||||
|
||||
.wrap_inner {
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.wrap_bg {
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.pad20 {
|
||||
padding: 20px
|
||||
}
|
||||
|
||||
.pad20_nobt {
|
||||
padding: 20px 20px 0
|
||||
}
|
||||
|
||||
.topBar {
|
||||
width: 100%;
|
||||
background: #fbfaf8;
|
||||
border-bottom: 1px solid #eae6e2;
|
||||
height: 35px;
|
||||
line-height: 35px
|
||||
}
|
||||
|
||||
.box_center {
|
||||
width: 1020px;
|
||||
margin: 0 auto
|
||||
}
|
||||
|
||||
.top_l {
|
||||
float: left
|
||||
}
|
||||
|
||||
.top_r {
|
||||
float: right
|
||||
}
|
||||
|
||||
.topBar .line {
|
||||
display: inline-block;
|
||||
padding: 0 12px;
|
||||
color: #e5d9da
|
||||
}
|
||||
|
||||
.topBar a {
|
||||
display: inline-block;
|
||||
color: #8C8C8C
|
||||
}
|
||||
|
||||
.topBar a.on {
|
||||
color: #333
|
||||
}
|
||||
|
||||
.topMain {
|
||||
height: 92px;
|
||||
background: #fff;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 160px;
|
||||
float: left;
|
||||
padding: 23px 130px 0 0;
|
||||
display: block
|
||||
}
|
||||
|
||||
.logo img {
|
||||
width: auto;
|
||||
height: 48px
|
||||
}
|
||||
|
||||
.searchBar {
|
||||
width: 342px;
|
||||
margin-top: 27px;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.searchBar .search /*, .searchBar .hotword*/
|
||||
{
|
||||
width: 342px;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.searchBar .s_int {
|
||||
width: 250px;
|
||||
padding: 0 14px 0 18px;
|
||||
height: 36px;
|
||||
line-height: 36px \9;
|
||||
vertical-align: middle;
|
||||
border: 1px solid #3eaf7c;
|
||||
border-right: none;
|
||||
color: #333;
|
||||
float: left;
|
||||
border-radius: 20px 0 0 20px;
|
||||
font-size: 14px; /*background: #fff;*/
|
||||
background: 0 0
|
||||
}
|
||||
|
||||
/*.searchBar .s_btn { width: 78px; height: 38px; line-height: 38px; background: #f65167; color: #fff; font-size: 16px; text-align: center; float: left; cursor: pointer; padding: 0 }
|
||||
.searchBar .s_btn:hover { background:#E23249 }*/
|
||||
.searchBar .search_btn {
|
||||
float: left;
|
||||
width: 58px;
|
||||
height: 38px;
|
||||
text-align: center;
|
||||
border-radius: 0 20px 20px 0;
|
||||
background-color: #3eaf7c;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.searchBar .search_btn .icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: block;
|
||||
margin: 9px auto 0;
|
||||
background: url(../images/search.png) no-repeat;
|
||||
background-size: cover;
|
||||
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/search.png', sizingMethod='scale');
|
||||
}
|
||||
|
||||
/*.hotword { padding-top: 3px }
|
||||
.hotword a, .hotword span { color: #999; margin: 0 6px 0 5px }
|
||||
.hotword a:hover { color: #666 }*/
|
||||
.bookShelf {
|
||||
margin-top: 27px;
|
||||
padding-left: 20px;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.bookShelf .sj_link {
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
padding-left: 30px;
|
||||
font-size: 15px;
|
||||
color: #404040;
|
||||
background: url(../images/icon_sj.png) no-repeat 6px 50%;
|
||||
float: left
|
||||
}
|
||||
|
||||
.bookShelf .user_link {
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
padding-left: 20px;
|
||||
font-size: 15px;
|
||||
color: #404040;
|
||||
float: right
|
||||
}
|
||||
|
||||
.bookShelf .user_head {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 50%;
|
||||
float: left;
|
||||
margin: 6px 5px 0 0
|
||||
}
|
||||
|
||||
.bookShelf .user_name {
|
||||
max-width: 100px;
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
.bookShelf .line {
|
||||
float: left;
|
||||
color: #ccc
|
||||
}
|
||||
|
||||
/*.bookShelf img { position: absolute; top: 17px; left: 17px; z-index: 10 }*/
|
||||
.mainNav {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: #3eaf7c;
|
||||
margin-bottom: 20px
|
||||
}
|
||||
|
||||
.mainNav .nav li {
|
||||
float: left
|
||||
}
|
||||
|
||||
.mainNav .nav li a {
|
||||
float: left;
|
||||
height: 44px;
|
||||
line-height: 48px;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
margin: 0 34px;
|
||||
border-bottom: 2px solid #3eaf7c;
|
||||
transition: color .3s, background-color .3s, border .3s
|
||||
}
|
||||
|
||||
.mainNav .nav li.on a, .mainNav .nav li a:hover {
|
||||
border-bottom: 2px solid rgba(255, 255, 255, .8)
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 0 0 20px; /*margin-top: 20px; background: #fbfaf8; border-top: 1px solid #e0e0e0; */
|
||||
text-align: center;
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.copyright ul li {
|
||||
color: #999;
|
||||
line-height: 26px
|
||||
}
|
||||
|
||||
.copyright .menu {
|
||||
padding: 2px 0 6px;
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.copyright .line {
|
||||
display: inline-block;
|
||||
padding: 0 12px;
|
||||
color: #e5d9da
|
||||
}
|
||||
|
||||
.copyright p {
|
||||
margin-top: 10px;
|
||||
color: #999
|
||||
}
|
||||
|
||||
.code_bar img {
|
||||
margin-left: 66px
|
||||
}
|
||||
|
||||
.rBar {
|
||||
float: right;
|
||||
width: 268px
|
||||
}
|
||||
|
||||
.btn_gray, .btn_red, .btn_ora, .btn_ora_white, .btn_red1 {
|
||||
border-radius: 20px;
|
||||
font-size: 15px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer; /*padding: 0 34px; height: 34px; line-height: 34px;*/
|
||||
padding: 11px 36px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.btn_gray {
|
||||
border: 1px solid #dedede;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.btn_red, .btn_ora {
|
||||
border: 1px solid #3eaf7c;
|
||||
background: #3eaf7c;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.btn_red1 {
|
||||
border: 1px solid #ff4040;
|
||||
background: #ff4040;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.btn_ora_white {
|
||||
border: 1px solid #3eaf7c;
|
||||
color: #3eaf7c
|
||||
}
|
||||
|
||||
.btn_ora_white:hover {
|
||||
background: #fefaf6
|
||||
}
|
||||
|
||||
.btn_link {
|
||||
padding: 2px 6px;
|
||||
background: #3eaf7c;
|
||||
color: #fff;
|
||||
border-radius: 2px
|
||||
}
|
||||
|
||||
.btn_gray:hover {
|
||||
background: #f0f0f0;
|
||||
color: #333
|
||||
}
|
||||
|
||||
.btn_ora:hover, .btn_red:hover, .btn_link:hover {
|
||||
background: #3eaf7c;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.btn_red1:hover {
|
||||
background: #fc2525;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.pay_Checkout .btn_red, .btn_big {
|
||||
font-size: 16px;
|
||||
padding: 15px 0;
|
||||
border-radius: 4px;
|
||||
width: 196px;
|
||||
}
|
||||
|
||||
i.vip {
|
||||
width: 26px;
|
||||
height: 14px;
|
||||
text-align: center;
|
||||
line-height: 14px;
|
||||
font-size: 11px;
|
||||
color: #fff;
|
||||
background: #fe8034;
|
||||
border-radius: 2px;
|
||||
margin: 13px 0 0 3px;
|
||||
display: inline-block;
|
||||
transform: scale(0.88);
|
||||
}
|
||||
|
||||
i.vip_b {
|
||||
width: 36px;
|
||||
height: 22px;
|
||||
text-align: center;
|
||||
line-height: 22px;
|
||||
font-size: 15px;
|
||||
color: #fff;
|
||||
background: #3eaf7c;
|
||||
border-radius: 4px;
|
||||
margin-left: 5px;
|
||||
display: inline-block;
|
||||
vertical-align: 3px
|
||||
}
|
||||
|
||||
.pageBox {
|
||||
text-align: center;
|
||||
padding: 20px 0
|
||||
}
|
||||
|
||||
.pageBox a, .pageBox span {
|
||||
display: inline-block;
|
||||
color: #999;
|
||||
padding: 6px 10px;
|
||||
margin: 0 5px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
line-height: 1
|
||||
}
|
||||
|
||||
.pageBox .current, .pageBox a:hover {
|
||||
background: #3eaf7c;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.top_nearread {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin-right: 10px;
|
||||
float: left
|
||||
}
|
||||
|
||||
.top_nearread .nearread {
|
||||
padding: 0 9px
|
||||
}
|
||||
|
||||
.top_nearread .nearread.on {
|
||||
border-left: 1px solid #d9d9d9;
|
||||
border-right: 1px solid #d9d9d9;
|
||||
background: #FFF;
|
||||
padding: 0 8px;
|
||||
height: 36px;
|
||||
position: relative;
|
||||
z-index: 8
|
||||
}
|
||||
|
||||
.icon_down {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: 2px 0 0 5px;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
overflow: hidden;
|
||||
border-width: 4px;
|
||||
border-style: solid dashed dashed;
|
||||
border-color: #7f7f7f transparent transparent;
|
||||
}
|
||||
|
||||
.book_record {
|
||||
width: 382px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9
|
||||
}
|
||||
|
||||
.record_box {
|
||||
width: 380px;
|
||||
background: #fff;
|
||||
margin-top: 35px;
|
||||
border: 1px solid #d9d9d9
|
||||
}
|
||||
|
||||
.book_record .sp {
|
||||
width: 77px;
|
||||
height: 6px;
|
||||
background: #fff;
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
right: 1px
|
||||
}
|
||||
|
||||
.record_title {
|
||||
padding: 14px 10px
|
||||
}
|
||||
|
||||
.record_title a {
|
||||
border: 1px solid #dedede;
|
||||
background: #fafafa;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
line-height: 1;
|
||||
margin-right: 14px
|
||||
}
|
||||
|
||||
.record_title a.on {
|
||||
border: 1px solid #f65167;
|
||||
background: #f65167;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.record_box .all {
|
||||
display: block;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
background: #f6f6f6
|
||||
}
|
||||
|
||||
.record_list ul {
|
||||
margin-bottom: 10px
|
||||
}
|
||||
|
||||
.record_list li {
|
||||
clear: both;
|
||||
padding: 10px;
|
||||
line-height: 1;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.record_list li:hover {
|
||||
background: #f6f6f6
|
||||
}
|
||||
|
||||
.record_list li .cover {
|
||||
width: 50px;
|
||||
height: 63px;
|
||||
background: #f6f6f6
|
||||
}
|
||||
|
||||
.record_list li .cover img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.record_list a {
|
||||
display: inline;
|
||||
color: #333
|
||||
}
|
||||
|
||||
.record_list .book_intro {
|
||||
width: 300px;
|
||||
height: 65px;
|
||||
padding-left: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.record_list .book_intro p {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
overflow: hidden;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.record_list .book_intro .p1 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.record_list .book_intro .p2 {
|
||||
margin: 2px 0;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis
|
||||
}
|
||||
|
||||
.record_list .book_intro .p3 {
|
||||
}
|
||||
|
||||
.record_list .book_intro i.vip {
|
||||
margin: 0 0 0 3px
|
||||
}
|
||||
|
||||
.record_list .read_link a {
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.manBody {
|
||||
}
|
||||
|
||||
.manBody .mainNav {
|
||||
background: #3e3d43
|
||||
}
|
||||
|
||||
.manBody .searchBar .s_int {
|
||||
border: 1px solid #878689;
|
||||
border-right: none;
|
||||
background-position: 8px -22px
|
||||
}
|
||||
|
||||
.manBody .mainNav .nav li.on a, .manBody .mainNav .nav li a:hover {
|
||||
background: #313035
|
||||
}
|
||||
|
||||
.nav_sub {
|
||||
margin-bottom: 16px
|
||||
}
|
||||
|
||||
.nav_sub a {
|
||||
padding: 0 6px
|
||||
}
|
||||
|
||||
.copyright .menu a {
|
||||
color: #666;
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.copyright .menu a:hover, .bookShelf .sj_link:hover {
|
||||
color: #3eaf7c
|
||||
}
|
||||
|
||||
.rightList .more, .more_bar {
|
||||
margin: 1px 0;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
border-radius: 1px;
|
||||
background-color: #f7f7f7;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.rightList .more a, .more_bar a {
|
||||
display: block;
|
||||
color: #666
|
||||
}
|
||||
|
||||
.header, .footer {
|
||||
min-width: 1020px
|
||||
}
|
||||
|
||||
/*base*/
|
||||
.noborder {
|
||||
border: 0 !important
|
||||
}
|
||||
|
||||
.nomargin {
|
||||
margin: 0 !important
|
||||
}
|
||||
|
||||
.ml {
|
||||
margin-left: 12px
|
||||
}
|
||||
|
||||
.mr {
|
||||
margin-right: 12px
|
||||
}
|
||||
|
||||
.ml5 {
|
||||
margin-left: 5px
|
||||
}
|
||||
|
||||
.ml10 {
|
||||
margin-left: 10px
|
||||
}
|
||||
|
||||
.ml15 {
|
||||
margin-left: 15px
|
||||
}
|
||||
|
||||
.ml20 {
|
||||
margin-left: 20px
|
||||
}
|
||||
|
||||
.mr5 {
|
||||
margin-right: 5px
|
||||
}
|
||||
|
||||
.mr10 {
|
||||
margin-right: 10px
|
||||
}
|
||||
|
||||
.mr15 {
|
||||
margin-right: 15px
|
||||
}
|
||||
|
||||
.mr20 {
|
||||
margin-right: 20px
|
||||
}
|
||||
|
||||
.mt5 {
|
||||
margin-top: 5px
|
||||
}
|
||||
|
||||
.mt10 {
|
||||
margin-top: 10px
|
||||
}
|
||||
|
||||
.mt15 {
|
||||
margin-top: 15px
|
||||
}
|
||||
|
||||
.mt20 {
|
||||
margin-top: 20px
|
||||
}
|
||||
|
||||
.mb5 {
|
||||
margin-bottom: 5px
|
||||
}
|
||||
|
||||
.mb10 {
|
||||
margin-bottom: 10px
|
||||
}
|
||||
|
||||
.mb15 {
|
||||
margin-bottom: 15px
|
||||
}
|
||||
|
||||
.mb20 {
|
||||
margin-bottom: 20px
|
||||
}
|
||||
|
||||
.mb50 {
|
||||
margin-bottom: 50px
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.notindent {
|
||||
text-indent: inherit !important
|
||||
}
|
||||
|
||||
.vm {
|
||||
vertical-align: middle !important
|
||||
}
|
||||
|
||||
.border_t {
|
||||
border-top: 1px solid #eee
|
||||
}
|
||||
|
||||
.border_b {
|
||||
border-bottom: 1px solid #eee
|
||||
}
|
||||
|
||||
.border_l {
|
||||
border-left: 1px solid #eee
|
||||
}
|
||||
|
||||
.border_r {
|
||||
border-right: 1px solid #eee
|
||||
}
|
||||
|
||||
.layui-laypage-curr {
|
||||
background: #3eaf7c;
|
||||
}
|
||||
|
||||
.layui-laypage-curr em {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.layui-disabled, .layui-disabled:hover {
|
||||
color: #d2d2d2 !important;
|
||||
cursor: not-allowed !important
|
||||
}
|
||||
|
||||
#noFeedbackNote {
|
||||
line-height: 400px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
#txtDescription {
|
||||
/*width: 900px;*/
|
||||
height: 288px;
|
||||
margin: 20px auto 20px;
|
||||
padding: 10px;
|
||||
|
||||
|
||||
/*新增样式*/
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #eee;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.userBox {
|
||||
margin: 0 auto
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.form-signin {
|
||||
max-width: 330px;
|
||||
padding: 15px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.form-signin .form-signin-heading,
|
||||
.form-signin .checkbox {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.form-signin .checkbox {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.form-signin .form-control {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.form-signin .form-control:focus {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.form-signin input[type="email"] {
|
||||
margin-bottom: -1px;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.form-signin input[type="password"] {
|
||||
margin-bottom: 10px;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
@ -0,0 +1,643 @@
|
||||
@charset "utf-8";
|
||||
.updateTable .style a {
|
||||
color: #999
|
||||
}
|
||||
|
||||
.updateTable .author a {
|
||||
color: #999;
|
||||
cursor: text
|
||||
}
|
||||
|
||||
.bind, .updateTable .style a:hover {
|
||||
color: #f65167
|
||||
}
|
||||
|
||||
.userBox { /*width: 998px; border: 1px solid #eaeaea;*/
|
||||
margin: 0 auto 50px;
|
||||
background: #fff;
|
||||
border-radius: 6px
|
||||
}
|
||||
|
||||
.channelViewhistory .userBox {
|
||||
margin: 0 auto
|
||||
}
|
||||
|
||||
.user_l {
|
||||
width: 350px;
|
||||
float: left;
|
||||
padding: 50px 50px
|
||||
}
|
||||
|
||||
.user_l h3 {
|
||||
font-size: 23px;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.user_l #LabErr {
|
||||
color: #ff4040;
|
||||
display: block;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.user_l .log_list {
|
||||
width: 350px
|
||||
}
|
||||
|
||||
.user_l .s_input {
|
||||
margin-bottom: 25px;
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.s_input {
|
||||
width: 348px;
|
||||
height: 30px;
|
||||
line-height: 38px \9;
|
||||
vertical-align: middle;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px
|
||||
}
|
||||
|
||||
.icon_name, .icon_key, .icon_code {
|
||||
width: 312px;
|
||||
padding-left: 36px
|
||||
}
|
||||
|
||||
.icon_key {
|
||||
background-position: 13px -51px
|
||||
}
|
||||
|
||||
.icon_code {
|
||||
background-position: 13px -117px;
|
||||
width: 200px;
|
||||
float: left
|
||||
}
|
||||
|
||||
.code_pic {
|
||||
height: 38px;
|
||||
float: right
|
||||
}
|
||||
|
||||
.btn_phone {
|
||||
height: 40px;
|
||||
width: 100px;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
background: #dfdfdf
|
||||
}
|
||||
|
||||
.log_code {
|
||||
*padding-bottom: 25px
|
||||
}
|
||||
|
||||
.user_l .btn_red {
|
||||
width: 100%;
|
||||
font-size: 19px;
|
||||
padding: 12px
|
||||
}
|
||||
|
||||
.autologin {
|
||||
color: #999;
|
||||
line-height: 1;
|
||||
margin-bottom: 18px
|
||||
}
|
||||
|
||||
.autologin em {
|
||||
vertical-align: 2px;
|
||||
margin-left: 4px
|
||||
}
|
||||
|
||||
.user_r {
|
||||
width: 259px;
|
||||
margin: 80px 0;
|
||||
padding: 20px 70px;
|
||||
border-left: 1px dotted #e3e3e3;
|
||||
float: right;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.user_r .tit {
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
padding: 6px 0 25px
|
||||
}
|
||||
|
||||
.user_r .btn_ora {
|
||||
padding: 10px 34px
|
||||
}
|
||||
|
||||
.fast_login {
|
||||
padding: 60px 0 0
|
||||
}
|
||||
|
||||
.fast_list {
|
||||
text-align: center;
|
||||
padding: 0.5rem
|
||||
}
|
||||
|
||||
.fast_list li {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
zoom: 1
|
||||
}
|
||||
|
||||
.fast_list li .img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 20px 0 5px
|
||||
}
|
||||
|
||||
.fast_list li a:hover {
|
||||
opacity: 0.8;
|
||||
filter: alpha(opacity=80);
|
||||
-moz-opacity: 0.8
|
||||
}
|
||||
|
||||
.fast_list li span {
|
||||
display: block
|
||||
}
|
||||
|
||||
.fast_list .login_qq {
|
||||
margin: 0 42px
|
||||
}
|
||||
|
||||
.fast_list .login_wb a {
|
||||
color: #f55c5b
|
||||
}
|
||||
|
||||
.fast_list .login_qq a {
|
||||
color: #51b7ff
|
||||
}
|
||||
|
||||
.fast_list .login_wx a {
|
||||
color: #66d65e
|
||||
}
|
||||
|
||||
.fast_tit {
|
||||
position: relative;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.fast_tit .lines {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
line-height: 1;
|
||||
background: #eaeaea
|
||||
}
|
||||
|
||||
.fast_tit .title {
|
||||
background: #fff;
|
||||
font-size: 16px;
|
||||
padding: 3px 14px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
z-index: 999
|
||||
}
|
||||
|
||||
/*userinfo*/
|
||||
.my_l {
|
||||
width: 198px;
|
||||
float: left;
|
||||
font-size: 13px;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.my_l li a {
|
||||
display: block;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
padding-left: 62px;
|
||||
border-left: 4px solid #fff;
|
||||
margin-bottom: 5px;
|
||||
color: #666
|
||||
}
|
||||
|
||||
.my_l li .on {
|
||||
background-color: #fafafa;
|
||||
border-left: 2px solid #3eaf7c;
|
||||
color: #000;
|
||||
border-radius: 0 2px 2px 0
|
||||
}
|
||||
|
||||
.my_l .link_1 {
|
||||
background-position: 32px -188px
|
||||
}
|
||||
|
||||
.my_l .link_2 {
|
||||
background-position: 32px -230px
|
||||
}
|
||||
|
||||
.my_l .link_3 {
|
||||
background-position: 32px -272px
|
||||
}
|
||||
|
||||
.my_l .link_4 {
|
||||
background-position: 32px -314px
|
||||
}
|
||||
|
||||
.my_l .link_5 {
|
||||
background-position: 32px -356px
|
||||
}
|
||||
|
||||
.my_l .link_6 {
|
||||
background-position: 32px -397px
|
||||
}
|
||||
|
||||
.my_l .link_7 {
|
||||
background-position: 32px -440px
|
||||
}
|
||||
|
||||
.my_l .link_8 {
|
||||
background-position: 32px -481px
|
||||
}
|
||||
|
||||
.my_r {
|
||||
width: 739px;
|
||||
padding: 0 30px 30px;
|
||||
float: right;
|
||||
border-left: 1px solid #efefef;
|
||||
min-height: 470px
|
||||
}
|
||||
|
||||
.my_info {
|
||||
padding: 30px 0 5px
|
||||
}
|
||||
|
||||
.user_big_head { /*width:110px; height:110px; padding:4px; border:1px solid #eaeaea;*/
|
||||
margin-right: 30px;
|
||||
float: left;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.my_r .my_name {
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
padding: 5px 0 12px 0
|
||||
}
|
||||
|
||||
.my_r .s_input {
|
||||
width: 318px;
|
||||
padding: 0 10px
|
||||
}
|
||||
|
||||
.my_list li {
|
||||
line-height: 28px
|
||||
}
|
||||
|
||||
.my_list li i, .my_list li em.red {
|
||||
margin-right: 6px
|
||||
}
|
||||
|
||||
.my_list .binded {
|
||||
color: #999;
|
||||
margin-left: 6px
|
||||
}
|
||||
|
||||
.my_list .btn_link {
|
||||
margin-left: 12px
|
||||
}
|
||||
|
||||
.mytab_list li {
|
||||
line-height: 30px;
|
||||
padding: 10px 0;
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.mytab_list li .tit {
|
||||
width: 70px;
|
||||
color: #aaa;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
margin-right: 18px
|
||||
}
|
||||
|
||||
.mytab_list .user_img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
vertical-align: middle;
|
||||
border-radius: 50%
|
||||
}
|
||||
|
||||
.my_bookshelf .title {
|
||||
padding: 20px 0 15px;
|
||||
line-height: 30px
|
||||
}
|
||||
|
||||
.my_bookshelf h4 {
|
||||
font-size: 14px;
|
||||
color: #666
|
||||
}
|
||||
|
||||
.my_bookshelf h2 {
|
||||
font-size: 18px;
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
.updateTable {
|
||||
width: 739px;
|
||||
color: #999
|
||||
}
|
||||
|
||||
.updateTable table {
|
||||
width: 100%;
|
||||
margin-bottom: 14px
|
||||
}
|
||||
|
||||
.updateTable th, .updateTable td {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
vertical-align: middle;
|
||||
padding-left: 6px;
|
||||
font-weight: normal;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
.updateTable th {
|
||||
background: #f9f9f9;
|
||||
color: #333;
|
||||
border-top: 1px solid #eee
|
||||
}
|
||||
|
||||
.updateTable td {
|
||||
height: 40px;
|
||||
line-height: 40px
|
||||
}
|
||||
|
||||
.updateTable .style {
|
||||
width: 80px;
|
||||
padding-left: 10px
|
||||
}
|
||||
|
||||
.updateTable .name {
|
||||
width: 178px;
|
||||
padding-right: 10px
|
||||
}
|
||||
|
||||
.updateTable .name a, .updateTable .chapter a {
|
||||
max-width: 168px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.updateTable .chapter {
|
||||
padding-right: 5px
|
||||
}
|
||||
|
||||
.updateTable .chapter a {
|
||||
max-width: 220px;
|
||||
float: left
|
||||
}
|
||||
|
||||
.updateTable .author {
|
||||
width: 72px;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
.updateTable .goread {
|
||||
width: 80px;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.updateTable .time {
|
||||
width: 86px
|
||||
}
|
||||
|
||||
.updateTable .word {
|
||||
width: 64px;
|
||||
padding-right: 10px;
|
||||
text-align: right
|
||||
}
|
||||
|
||||
.updateTable .rank {
|
||||
width: 30px;
|
||||
padding-right: 10px;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.updateTable .name a, .updateTable .chapter a, .updateTable .author a {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
display: inline-block;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.updateTable tr:nth-child(2n) td {
|
||||
background: #fafafa
|
||||
}
|
||||
|
||||
.dataTable {
|
||||
width: 739px
|
||||
}
|
||||
|
||||
.dataTable table {
|
||||
width: 100%;
|
||||
margin-bottom: 14px;
|
||||
border-collapse: collapse
|
||||
}
|
||||
|
||||
.dataTable th, .dataTable td {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
vertical-align: middle;
|
||||
padding: 0 10px;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
border: 1px solid #eaeaea
|
||||
}
|
||||
|
||||
.dataTable th {
|
||||
background: #f8f8f8
|
||||
}
|
||||
|
||||
.nodate {
|
||||
border-top: 1px solid #eaeaea;
|
||||
padding: 60px 0
|
||||
}
|
||||
|
||||
.viewhistoryBox { /*padding: 0 30px 30px; */
|
||||
padding: 0 20px 10px
|
||||
}
|
||||
|
||||
.viewhistoryBox .updateTable {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
/*.btn_gray, .btn_red, .btn_ora { font-size:14px; padding:8px 28px }*/
|
||||
.book_tit {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
margin: 0 14px;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.book_tit .fl {
|
||||
font-size: 14px;
|
||||
color: #999
|
||||
}
|
||||
|
||||
.book_tit .fl h3 {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
font-weight: normal;
|
||||
margin-right: 5px;
|
||||
display: inline
|
||||
}
|
||||
|
||||
.book_tit .fr {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.commentBar, .feedback_list {
|
||||
border-top: 1px solid #eee;
|
||||
margin-bottom: 15px
|
||||
}
|
||||
|
||||
/*.comment_list { padding: 16px 0; border-bottom: 1px solid #eee }
|
||||
.comment_list .user_head { width:54px; height:54px; border-radius:50%; float: left; margin-right: 14px }
|
||||
.comment_list .li_1 { overflow: hidden }
|
||||
.comment_list .user_name { color: #ed4259 }
|
||||
.comment_list .li_2 { padding:3px 0; color:#999 }
|
||||
.comment_list .li_3, .comment_list .li_4 { margin-left:68px }
|
||||
.comment_list .reply { padding-left: 12px }
|
||||
.comment_list .num { color: #ed4259; margin: 0 3px }
|
||||
.comment_list .li_4 { line-height:34px; padding-top:8px; margin-top:15px; border-top:1px solid #eaeaea }
|
||||
.comment_list .li_4 .more { background:#f7f7f7; border-radius:2px; color:#ed4259; text-align:center }*/
|
||||
.no_contet {
|
||||
padding: 190px 0 40px;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
border-top: 1px solid #eee
|
||||
}
|
||||
|
||||
.no_comment {
|
||||
background: url(../images/no_comment.png) no-repeat center 80px
|
||||
}
|
||||
|
||||
.comment_list {
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid #eee
|
||||
}
|
||||
|
||||
.comment_list:last-child {
|
||||
border: none
|
||||
}
|
||||
|
||||
.comment_list .user_heads { /*width: 54px; height: 54px; float: left;*/
|
||||
position: relative;
|
||||
margin-right: 20px
|
||||
}
|
||||
|
||||
.comment_list .user_head {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: #f6f6f6
|
||||
}
|
||||
|
||||
.comment_list .user_heads span {
|
||||
display: block;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
bottom: 0
|
||||
}
|
||||
|
||||
.comment_list ul { /*width: 640px;*/
|
||||
width: 660px;
|
||||
}
|
||||
|
||||
.comment_list .li_0 {
|
||||
font-family: "宋体"
|
||||
}
|
||||
|
||||
.comment_list .li_0 strong {
|
||||
font-size: 14px;
|
||||
color: #f00
|
||||
}
|
||||
|
||||
.comment_list .li_1 {
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.comment_list .user_name {
|
||||
color: #ed4259
|
||||
}
|
||||
|
||||
.comment_list .li_2 {
|
||||
padding: 6px 0
|
||||
}
|
||||
|
||||
.comment_list .li_3 {
|
||||
color: #999
|
||||
}
|
||||
|
||||
.comment_list .reply {
|
||||
padding-left: 12px
|
||||
}
|
||||
|
||||
.comment_list .num {
|
||||
color: #ed4259;
|
||||
margin: 0 3px
|
||||
}
|
||||
|
||||
.comment_list .li_4 {
|
||||
line-height: 34px;
|
||||
padding-top: 8px;
|
||||
margin-top: 15px;
|
||||
border-top: 1px solid #eaeaea
|
||||
}
|
||||
|
||||
.pl_bar li {
|
||||
display: block
|
||||
}
|
||||
|
||||
.pl_bar .name {
|
||||
color: #666;
|
||||
padding-top: 2px;
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.pl_bar .dec {
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
padding: 12px 0
|
||||
}
|
||||
|
||||
.pl_bar .other {
|
||||
line-height: 24px;
|
||||
color: #999;
|
||||
font-size: 13px
|
||||
}
|
||||
|
||||
.pl_bar .other a {
|
||||
display: inline-block;
|
||||
color: #999
|
||||
}
|
||||
|
||||
.pl_bar .reply {
|
||||
padding-left: 22px;
|
||||
background: url(../images/icon_reply.png) no-repeat 0 2px
|
||||
}
|
||||
|
||||
/*.no_comment { padding: 70px 14px 115px; color: #CCCCCC; text-align: center; font-size: 14px; }*/
|
||||
.reply_bar {
|
||||
background: #f9f9f9;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
var UserUtil = {
|
||||
msgStyle: 'background-color:#333; color:#fff; text-align:center; border:none; font-size:20px; padding:10px;',
|
||||
GetFavoritesNew: function () {
|
||||
var bIdList = "";
|
||||
$(".book_list").each(function () {
|
||||
bIdList += "," + $(this).attr("vals");
|
||||
});
|
||||
if (bIdList != "") {
|
||||
}
|
||||
},
|
||||
GetHistory: function () {
|
||||
var bIdList = "";
|
||||
$(".book_list").each(function () {
|
||||
bIdList += "," + $(this).attr("vals");
|
||||
});
|
||||
if (bIdList != "") {
|
||||
}
|
||||
},
|
||||
GetChapterInfo: function () {
|
||||
var cIdList = "";
|
||||
$(".showCName").each(function () {
|
||||
cIdList += "," + $(this).attr("vals");
|
||||
});
|
||||
if (cIdList != "") {
|
||||
}
|
||||
},
|
||||
SignDay: function () {
|
||||
if (!signed) {
|
||||
signed = true;
|
||||
}
|
||||
},
|
||||
SignDayStatus: function () {
|
||||
},
|
||||
RegSendSms: function () {
|
||||
var mob = $("#txtUName").val();
|
||||
var cCode = $("#TxtChkCode").val();
|
||||
if (mob != "" && cCode != "") {
|
||||
$("#btnSendSms").attr("disabled", "disabled");
|
||||
$("#txtUName").attr("readonly", "true");
|
||||
}
|
||||
else {
|
||||
layer.open({
|
||||
content: '手机号码和验证码必须填写',
|
||||
style: UserUtil.msgStyle,
|
||||
time: 2
|
||||
});
|
||||
}
|
||||
},
|
||||
GetPassSendSms: function () {
|
||||
var mob = $("#txtMobile").val();
|
||||
var cCode = $("#TxtChkCode").val();
|
||||
if (mob != "" && cCode != "") {
|
||||
$("#btnSendSms").attr("disabled", "disabled");
|
||||
$("#txtMobile").attr("readonly", "true");
|
||||
}
|
||||
else {
|
||||
layer.open({
|
||||
content: '手机号码和验证码必须填写',
|
||||
style: UserUtil.msgStyle,
|
||||
time: 2
|
||||
});
|
||||
}
|
||||
},
|
||||
RegSmsWait: function () {
|
||||
if (secondStep > 0) {
|
||||
$("#btnSendSms").val("重新发送(" + secondStep + ")");
|
||||
secondStep--;
|
||||
setTimeout("UserUtil.RegSmsWait()", 1000);
|
||||
}
|
||||
else {
|
||||
secondStep = 180;
|
||||
$("#btnSendSms").val("重新获取验证码");
|
||||
$("#btnSendSms").removeAttr("disabled");
|
||||
$("#txtUName").removeAttr("readonly");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
/** layui-v2.4.5 MIT License By https://www.layui.com */
|
||||
html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none}
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,919 @@
|
||||
/** layui-v2.4.5 MIT License By https://www.layui.com */
|
||||
.layui-layer-imgbar, .layui-layer-imgtit a, .layui-layer-tab .layui-layer-title span, .layui-layer-title {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
html #layuicss-layer {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 1989px
|
||||
}
|
||||
|
||||
.layui-layer, .layui-layer-shade {
|
||||
position: fixed;
|
||||
_position: absolute;
|
||||
pointer-events: auto
|
||||
}
|
||||
|
||||
.layui-layer-shade {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
_height: expression(document.body.offsetHeight+"px")
|
||||
}
|
||||
|
||||
.layui-layer {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
top: 150px;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
-webkit-background-clip: content;
|
||||
border-radius: 2px;
|
||||
box-shadow: 1px 1px 50px rgba(0, 0, 0, .3)
|
||||
}
|
||||
|
||||
.layui-layer-close {
|
||||
position: absolute
|
||||
}
|
||||
|
||||
.layui-layer-content {
|
||||
position: relative
|
||||
}
|
||||
|
||||
.layui-layer-border {
|
||||
border: 1px solid #B2B2B2;
|
||||
border: 1px solid rgba(0, 0, 0, .1);
|
||||
box-shadow: 1px 1px 5px rgba(0, 0, 0, .2)
|
||||
}
|
||||
|
||||
.layui-layer-load {
|
||||
background: url(loading-1.gif) center center no-repeat #eee
|
||||
}
|
||||
|
||||
.layui-layer-ico {
|
||||
background: url(icon.png) no-repeat
|
||||
}
|
||||
|
||||
.layui-layer-btn a, .layui-layer-dialog .layui-layer-ico, .layui-layer-setwin a {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
vertical-align: top
|
||||
}
|
||||
|
||||
.layui-layer-move {
|
||||
display: none;
|
||||
position: fixed;
|
||||
*position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: move;
|
||||
opacity: 0;
|
||||
filter: alpha(opacity=0);
|
||||
background-color: #fff;
|
||||
z-index: 2147483647
|
||||
}
|
||||
|
||||
.layui-layer-resize {
|
||||
position: absolute;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
cursor: se-resize
|
||||
}
|
||||
|
||||
.layer-anim {
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
-webkit-animation-duration: .3s;
|
||||
animation-duration: .3s
|
||||
}
|
||||
|
||||
@-webkit-keyframes layer-bounceIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(.5);
|
||||
transform: scale(.5)
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-bounceIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(.5);
|
||||
-ms-transform: scale(.5);
|
||||
transform: scale(.5)
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
transform: scale(1)
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-00 {
|
||||
-webkit-animation-name: layer-bounceIn;
|
||||
animation-name: layer-bounceIn
|
||||
}
|
||||
|
||||
@-webkit-keyframes layer-zoomInDown {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(.1) translateY(-2000px);
|
||||
transform: scale(.1) translateY(-2000px);
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(.475) translateY(60px);
|
||||
transform: scale(.475) translateY(60px);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-zoomInDown {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(.1) translateY(-2000px);
|
||||
-ms-transform: scale(.1) translateY(-2000px);
|
||||
transform: scale(.1) translateY(-2000px);
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(.475) translateY(60px);
|
||||
-ms-transform: scale(.475) translateY(60px);
|
||||
transform: scale(.475) translateY(60px);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-01 {
|
||||
-webkit-animation-name: layer-zoomInDown;
|
||||
animation-name: layer-zoomInDown
|
||||
}
|
||||
|
||||
@-webkit-keyframes layer-fadeInUpBig {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(2000px);
|
||||
transform: translateY(2000px)
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-fadeInUpBig {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(2000px);
|
||||
-ms-transform: translateY(2000px);
|
||||
transform: translateY(2000px)
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
transform: translateY(0)
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-02 {
|
||||
-webkit-animation-name: layer-fadeInUpBig;
|
||||
animation-name: layer-fadeInUpBig
|
||||
}
|
||||
|
||||
@-webkit-keyframes layer-zoomInLeft {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(.1) translateX(-2000px);
|
||||
transform: scale(.1) translateX(-2000px);
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(.475) translateX(48px);
|
||||
transform: scale(.475) translateX(48px);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-zoomInLeft {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(.1) translateX(-2000px);
|
||||
-ms-transform: scale(.1) translateX(-2000px);
|
||||
transform: scale(.1) translateX(-2000px);
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
animation-timing-function: ease-in-out
|
||||
}
|
||||
60% {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(.475) translateX(48px);
|
||||
-ms-transform: scale(.475) translateX(48px);
|
||||
transform: scale(.475) translateX(48px);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-03 {
|
||||
-webkit-animation-name: layer-zoomInLeft;
|
||||
animation-name: layer-zoomInLeft
|
||||
}
|
||||
|
||||
@-webkit-keyframes layer-rollIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateX(-100%) rotate(-120deg);
|
||||
transform: translateX(-100%) rotate(-120deg)
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateX(0) rotate(0);
|
||||
transform: translateX(0) rotate(0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-rollIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateX(-100%) rotate(-120deg);
|
||||
-ms-transform: translateX(-100%) rotate(-120deg);
|
||||
transform: translateX(-100%) rotate(-120deg)
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateX(0) rotate(0);
|
||||
-ms-transform: translateX(0) rotate(0);
|
||||
transform: translateX(0) rotate(0)
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-04 {
|
||||
-webkit-animation-name: layer-rollIn;
|
||||
animation-name: layer-rollIn
|
||||
}
|
||||
|
||||
@keyframes layer-fadeIn {
|
||||
0% {
|
||||
opacity: 0
|
||||
}
|
||||
100% {
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-05 {
|
||||
-webkit-animation-name: layer-fadeIn;
|
||||
animation-name: layer-fadeIn
|
||||
}
|
||||
|
||||
@-webkit-keyframes layer-shake {
|
||||
0%, 100% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0)
|
||||
}
|
||||
10%, 30%, 50%, 70%, 90% {
|
||||
-webkit-transform: translateX(-10px);
|
||||
transform: translateX(-10px)
|
||||
}
|
||||
20%, 40%, 60%, 80% {
|
||||
-webkit-transform: translateX(10px);
|
||||
transform: translateX(10px)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-shake {
|
||||
0%, 100% {
|
||||
-webkit-transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
transform: translateX(0)
|
||||
}
|
||||
10%, 30%, 50%, 70%, 90% {
|
||||
-webkit-transform: translateX(-10px);
|
||||
-ms-transform: translateX(-10px);
|
||||
transform: translateX(-10px)
|
||||
}
|
||||
20%, 40%, 60%, 80% {
|
||||
-webkit-transform: translateX(10px);
|
||||
-ms-transform: translateX(10px);
|
||||
transform: translateX(10px)
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-06 {
|
||||
-webkit-animation-name: layer-shake;
|
||||
animation-name: layer-shake
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0
|
||||
}
|
||||
100% {
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
.layui-layer-title {
|
||||
padding: 0 80px 0 20px;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
background-color: #F8F8F8;
|
||||
border-radius: 2px 2px 0 0
|
||||
}
|
||||
|
||||
.layui-layer-setwin {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
*right: 0;
|
||||
top: 15px;
|
||||
font-size: 0;
|
||||
line-height: initial
|
||||
}
|
||||
|
||||
.layui-layer-setwin a {
|
||||
position: relative;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
_overflow: hidden
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-min cite {
|
||||
position: absolute;
|
||||
width: 14px;
|
||||
height: 2px;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
margin-top: -1px;
|
||||
background-color: #2E2D3C;
|
||||
cursor: pointer;
|
||||
_overflow: hidden
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-min:hover cite {
|
||||
background-color: #2D93CA
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-max {
|
||||
background-position: -32px -40px
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-max:hover {
|
||||
background-position: -16px -40px
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-maxmin {
|
||||
background-position: -65px -40px
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-maxmin:hover {
|
||||
background-position: -49px -40px
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-close1 {
|
||||
background-position: 1px -40px;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-close1:hover {
|
||||
opacity: .7
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-close2 {
|
||||
position: absolute;
|
||||
right: -28px;
|
||||
top: -28px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-left: 0;
|
||||
background-position: -149px -31px;
|
||||
*right: -18px;
|
||||
_display: none
|
||||
}
|
||||
|
||||
.layui-layer-setwin .layui-layer-close2:hover {
|
||||
background-position: -180px -31px
|
||||
}
|
||||
|
||||
.layui-layer-btn {
|
||||
text-align: right;
|
||||
padding: 0 15px 12px;
|
||||
pointer-events: auto;
|
||||
user-select: none;
|
||||
-webkit-user-select: none
|
||||
}
|
||||
|
||||
.layui-layer-btn a {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
margin: 5px 5px 0;
|
||||
padding: 0 15px;
|
||||
border: 1px solid #dedede;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
border-radius: 2px;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
.layui-layer-btn a:hover {
|
||||
opacity: .9;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
.layui-layer-btn a:active {
|
||||
opacity: .8
|
||||
}
|
||||
|
||||
.layui-layer-btn .layui-layer-btn0 {
|
||||
border-color: #3eaf7c;
|
||||
background-color: #3eaf7c;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.layui-layer-btn-l {
|
||||
text-align: left
|
||||
}
|
||||
|
||||
.layui-layer-btn-c {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.layui-layer-dialog {
|
||||
min-width: 260px
|
||||
}
|
||||
|
||||
.layui-layer-dialog .layui-layer-content {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
line-height: 24px;
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
font-size: 14px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto
|
||||
}
|
||||
|
||||
.layui-layer-dialog .layui-layer-content .layui-layer-ico {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 15px;
|
||||
_left: -40px;
|
||||
width: 30px;
|
||||
height: 30px
|
||||
}
|
||||
|
||||
.layui-layer-ico1 {
|
||||
background-position: -30px 0
|
||||
}
|
||||
|
||||
.layui-layer-ico2 {
|
||||
background-position: -60px 0
|
||||
}
|
||||
|
||||
.layui-layer-ico3 {
|
||||
background-position: -90px 0
|
||||
}
|
||||
|
||||
.layui-layer-ico4 {
|
||||
background-position: -120px 0
|
||||
}
|
||||
|
||||
.layui-layer-ico5 {
|
||||
background-position: -150px 0
|
||||
}
|
||||
|
||||
.layui-layer-ico6 {
|
||||
background-position: -180px 0
|
||||
}
|
||||
|
||||
.layui-layer-rim {
|
||||
border: 6px solid #8D8D8D;
|
||||
border: 6px solid rgba(0, 0, 0, .3);
|
||||
border-radius: 5px;
|
||||
box-shadow: none
|
||||
}
|
||||
|
||||
.layui-layer-msg {
|
||||
min-width: 180px;
|
||||
border: 1px solid #D3D4D3;
|
||||
box-shadow: none
|
||||
}
|
||||
|
||||
.layui-layer-hui {
|
||||
min-width: 100px;
|
||||
background-color: #000;
|
||||
filter: alpha(opacity=60);
|
||||
background-color: rgba(0, 0, 0, .6);
|
||||
color: #fff;
|
||||
border: none
|
||||
}
|
||||
|
||||
.layui-layer-hui .layui-layer-content {
|
||||
padding: 12px 25px;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.layui-layer-dialog .layui-layer-padding {
|
||||
padding: 20px 20px 20px 55px;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
.layui-layer-page .layui-layer-content {
|
||||
position: relative;
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
.layui-layer-iframe .layui-layer-btn, .layui-layer-page .layui-layer-btn {
|
||||
padding-top: 10px
|
||||
}
|
||||
|
||||
.layui-layer-nobg {
|
||||
background: 0 0
|
||||
}
|
||||
|
||||
.layui-layer-iframe iframe {
|
||||
display: block;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.layui-layer-loading {
|
||||
border-radius: 100%;
|
||||
background: 0 0;
|
||||
box-shadow: none;
|
||||
border: none
|
||||
}
|
||||
|
||||
.layui-layer-loading .layui-layer-content {
|
||||
width: 60px;
|
||||
height: 24px;
|
||||
background: url(loading-0.gif) no-repeat
|
||||
}
|
||||
|
||||
.layui-layer-loading .layui-layer-loading1 {
|
||||
width: 37px;
|
||||
height: 37px;
|
||||
background: url(loading-1.gif) no-repeat
|
||||
}
|
||||
|
||||
.layui-layer-ico16, .layui-layer-loading .layui-layer-loading2 {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: url(loading-2.gif) no-repeat
|
||||
}
|
||||
|
||||
.layui-layer-tips {
|
||||
background: 0 0;
|
||||
box-shadow: none;
|
||||
border: none
|
||||
}
|
||||
|
||||
.layui-layer-tips .layui-layer-content {
|
||||
position: relative;
|
||||
line-height: 22px;
|
||||
min-width: 12px;
|
||||
padding: 8px 15px;
|
||||
font-size: 12px;
|
||||
_float: left;
|
||||
border-radius: 2px;
|
||||
box-shadow: 1px 1px 3px rgba(0, 0, 0, .2);
|
||||
background-color: #000;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.layui-layer-tips .layui-layer-close {
|
||||
right: -2px;
|
||||
top: -1px
|
||||
}
|
||||
|
||||
.layui-layer-tips i.layui-layer-TipsG {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-width: 8px;
|
||||
border-color: transparent;
|
||||
border-style: dashed;
|
||||
*overflow: hidden
|
||||
}
|
||||
|
||||
.layui-layer-tips i.layui-layer-TipsB, .layui-layer-tips i.layui-layer-TipsT {
|
||||
left: 5px;
|
||||
border-right-style: solid;
|
||||
border-right-color: #000
|
||||
}
|
||||
|
||||
.layui-layer-tips i.layui-layer-TipsT {
|
||||
bottom: -8px
|
||||
}
|
||||
|
||||
.layui-layer-tips i.layui-layer-TipsB {
|
||||
top: -8px
|
||||
}
|
||||
|
||||
.layui-layer-tips i.layui-layer-TipsL, .layui-layer-tips i.layui-layer-TipsR {
|
||||
top: 5px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: #000
|
||||
}
|
||||
|
||||
.layui-layer-tips i.layui-layer-TipsR {
|
||||
left: -8px
|
||||
}
|
||||
|
||||
.layui-layer-tips i.layui-layer-TipsL {
|
||||
right: -8px
|
||||
}
|
||||
|
||||
.layui-layer-lan[type=dialog] {
|
||||
min-width: 280px
|
||||
}
|
||||
|
||||
.layui-layer-lan .layui-layer-title {
|
||||
background: #4476A7;
|
||||
color: #fff;
|
||||
border: none
|
||||
}
|
||||
|
||||
.layui-layer-lan .layui-layer-btn {
|
||||
padding: 5px 10px 10px;
|
||||
text-align: right;
|
||||
border-top: 1px solid #E9E7E7
|
||||
}
|
||||
|
||||
.layui-layer-lan .layui-layer-btn a {
|
||||
background: #fff;
|
||||
border-color: #E9E7E7;
|
||||
color: #333
|
||||
}
|
||||
|
||||
.layui-layer-lan .layui-layer-btn .layui-layer-btn1 {
|
||||
background: #C9C5C5
|
||||
}
|
||||
|
||||
.layui-layer-molv .layui-layer-title {
|
||||
background: #009f95;
|
||||
color: #fff;
|
||||
border: none
|
||||
}
|
||||
|
||||
.layui-layer-molv .layui-layer-btn a {
|
||||
background: #009f95;
|
||||
border-color: #009f95
|
||||
}
|
||||
|
||||
.layui-layer-molv .layui-layer-btn .layui-layer-btn1 {
|
||||
background: #92B8B1
|
||||
}
|
||||
|
||||
.layui-layer-iconext {
|
||||
background: url(icon-ext.png) no-repeat
|
||||
}
|
||||
|
||||
.layui-layer-prompt .layui-layer-input {
|
||||
display: block;
|
||||
width: 230px;
|
||||
height: 36px;
|
||||
margin: 0 auto;
|
||||
line-height: 30px;
|
||||
padding-left: 10px;
|
||||
border: 1px solid #e6e6e6;
|
||||
color: #333
|
||||
}
|
||||
|
||||
.layui-layer-prompt textarea.layui-layer-input {
|
||||
width: 300px;
|
||||
height: 100px;
|
||||
line-height: 20px;
|
||||
padding: 6px 10px
|
||||
}
|
||||
|
||||
.layui-layer-prompt .layui-layer-content {
|
||||
padding: 20px
|
||||
}
|
||||
|
||||
.layui-layer-prompt .layui-layer-btn {
|
||||
padding-top: 0
|
||||
}
|
||||
|
||||
.layui-layer-tab {
|
||||
box-shadow: 1px 1px 50px rgba(0, 0, 0, .4)
|
||||
}
|
||||
|
||||
.layui-layer-tab .layui-layer-title {
|
||||
padding-left: 0;
|
||||
overflow: visible
|
||||
}
|
||||
|
||||
.layui-layer-tab .layui-layer-title span {
|
||||
position: relative;
|
||||
float: left;
|
||||
min-width: 80px;
|
||||
max-width: 260px;
|
||||
padding: 0 20px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.layui-layer-tab .layui-layer-title span.layui-this {
|
||||
height: 43px;
|
||||
border-left: 1px solid #eee;
|
||||
border-right: 1px solid #eee;
|
||||
background-color: #fff;
|
||||
z-index: 10
|
||||
}
|
||||
|
||||
.layui-layer-tab .layui-layer-title span:first-child {
|
||||
border-left: none
|
||||
}
|
||||
|
||||
.layui-layer-tabmain {
|
||||
line-height: 24px;
|
||||
clear: both
|
||||
}
|
||||
|
||||
.layui-layer-tabmain .layui-layer-tabli {
|
||||
display: none
|
||||
}
|
||||
|
||||
.layui-layer-tabmain .layui-layer-tabli.layui-this {
|
||||
display: block
|
||||
}
|
||||
|
||||
.layui-layer-photos {
|
||||
-webkit-animation-duration: .8s;
|
||||
animation-duration: .8s
|
||||
}
|
||||
|
||||
.layui-layer-photos .layui-layer-content {
|
||||
overflow: hidden;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.layui-layer-photos .layui-layer-phimg img {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
vertical-align: top
|
||||
}
|
||||
|
||||
.layui-layer-imgbar, .layui-layer-imguide {
|
||||
display: none
|
||||
}
|
||||
|
||||
.layui-layer-imgnext, .layui-layer-imgprev {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 27px;
|
||||
_width: 44px;
|
||||
height: 44px;
|
||||
margin-top: -22px;
|
||||
outline: 0;
|
||||
blr: expression(this.onFocus=this.blur())
|
||||
}
|
||||
|
||||
.layui-layer-imgprev {
|
||||
left: 10px;
|
||||
background-position: -5px -5px;
|
||||
_background-position: -70px -5px
|
||||
}
|
||||
|
||||
.layui-layer-imgprev:hover {
|
||||
background-position: -33px -5px;
|
||||
_background-position: -120px -5px
|
||||
}
|
||||
|
||||
.layui-layer-imgnext {
|
||||
right: 10px;
|
||||
_right: 8px;
|
||||
background-position: -5px -50px;
|
||||
_background-position: -70px -50px
|
||||
}
|
||||
|
||||
.layui-layer-imgnext:hover {
|
||||
background-position: -33px -50px;
|
||||
_background-position: -120px -50px
|
||||
}
|
||||
|
||||
.layui-layer-imgbar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
background-color: rgba(0, 0, 0, .8);
|
||||
background-color: #000 \9;
|
||||
filter: Alpha(opacity=80);
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
font-size: 0
|
||||
}
|
||||
|
||||
.layui-layer-imgtit * {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
vertical-align: top;
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.layui-layer-imgtit a {
|
||||
max-width: 65%;
|
||||
overflow: hidden;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
.layui-layer-imgtit a:hover {
|
||||
color: #fff;
|
||||
text-decoration: underline
|
||||
}
|
||||
|
||||
.layui-layer-imgtit em {
|
||||
padding-left: 10px;
|
||||
font-style: normal
|
||||
}
|
||||
|
||||
@-webkit-keyframes layer-bounceOut {
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(.7);
|
||||
transform: scale(.7)
|
||||
}
|
||||
30% {
|
||||
-webkit-transform: scale(1.05);
|
||||
transform: scale(1.05)
|
||||
}
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-bounceOut {
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(.7);
|
||||
-ms-transform: scale(.7);
|
||||
transform: scale(.7)
|
||||
}
|
||||
30% {
|
||||
-webkit-transform: scale(1.05);
|
||||
-ms-transform: scale(1.05);
|
||||
transform: scale(1.05)
|
||||
}
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
transform: scale(1)
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-close {
|
||||
-webkit-animation-name: layer-bounceOut;
|
||||
animation-name: layer-bounceOut;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
-webkit-animation-duration: .2s;
|
||||
animation-duration: .2s
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1100px) {
|
||||
.layui-layer-iframe {
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 701 B |
|
After Width: | Height: | Size: 1.7 KiB |
@ -0,0 +1,87 @@
|
||||
{
|
||||
"code": 0,
|
||||
"pages": 1,
|
||||
"data": [
|
||||
{
|
||||
"id": 76,
|
||||
"content": "申请添加你为好友",
|
||||
"uid": 168,
|
||||
"from": 166488,
|
||||
"from_group": 0,
|
||||
"type": 1,
|
||||
"remark": "有问题要问",
|
||||
"href": null,
|
||||
"read": 1,
|
||||
"time": "刚刚",
|
||||
"user": {
|
||||
"id": 166488,
|
||||
"avatar": "http://q.qlogo.cn/qqapp/101235792/B704597964F9BD0DB648292D1B09F7E8/100",
|
||||
"username": "李彦宏",
|
||||
"sign": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"content": "申请添加你为好友",
|
||||
"uid": 168,
|
||||
"from": 347592,
|
||||
"from_group": 0,
|
||||
"type": 1,
|
||||
"remark": "你好啊!",
|
||||
"href": null,
|
||||
"read": 1,
|
||||
"time": "刚刚",
|
||||
"user": {
|
||||
"id": 347592,
|
||||
"avatar": "http://q.qlogo.cn/qqapp/101235792/B78751375E0531675B1272AD994BA875/100",
|
||||
"username": "麻花疼",
|
||||
"sign": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 62,
|
||||
"content": "雷军 拒绝了你的好友申请",
|
||||
"uid": 168,
|
||||
"from": null,
|
||||
"from_group": null,
|
||||
"type": 1,
|
||||
"remark": null,
|
||||
"href": null,
|
||||
"read": 1,
|
||||
"time": "10天前",
|
||||
"user": {
|
||||
"id": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 60,
|
||||
"content": "马小云 已经同意你的好友申请",
|
||||
"uid": 168,
|
||||
"from": null,
|
||||
"from_group": null,
|
||||
"type": 1,
|
||||
"remark": null,
|
||||
"href": null,
|
||||
"read": 1,
|
||||
"time": "10天前",
|
||||
"user": {
|
||||
"id": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 61,
|
||||
"content": "贤心 已经同意你的好友申请",
|
||||
"uid": 168,
|
||||
"from": null,
|
||||
"from_group": null,
|
||||
"type": 1,
|
||||
"remark": null,
|
||||
"href": null,
|
||||
"read": 1,
|
||||
"time": "10天前",
|
||||
"user": {
|
||||
"id": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |