├── public
├── favicon.ico
├── img
│ ├── hbewm.png
│ ├── header.png
│ ├── read.png
│ └── donatezfb.png
├── manifest.json
├── index.html
└── bookSource.html
├── src
├── App.test.js
├── component
│ ├── scrollToTop.js
│ ├── theme.js
│ ├── main.js
│ ├── sourceRule.js
│ ├── toolbar.js
│ ├── home.js
│ └── edit.js
├── index.css
├── App.js
├── svg
│ ├── hint.js
│ └── github.js
├── App.css
├── index.js
├── utils
│ └── HttpUtil.js
├── logo.svg
└── serviceWorker.js
├── package.json
├── README.md
└── .gitignore
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zsakvo/yuedu-website/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/img/hbewm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zsakvo/yuedu-website/HEAD/public/img/hbewm.png
--------------------------------------------------------------------------------
/public/img/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zsakvo/yuedu-website/HEAD/public/img/header.png
--------------------------------------------------------------------------------
/public/img/read.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zsakvo/yuedu-website/HEAD/public/img/read.png
--------------------------------------------------------------------------------
/public/img/donatezfb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zsakvo/yuedu-website/HEAD/public/img/donatezfb.png
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/component/scrollToTop.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withRouter } from "react-router-dom";
3 |
4 | class ScrollToTop extends React.Component {
5 | componentDidUpdate(prevProps) {
6 | if (this.props.location !== prevProps.location) {
7 | window.scrollTo(0, 0);
8 | }
9 | }
10 | render() {
11 | return this.props.children;
12 | }
13 | }
14 | export default withRouter(ScrollToTop);
15 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/component/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from "@material-ui/core/styles";
2 |
3 | const AppTheme = createMuiTheme({
4 | palette: {
5 | primary: {
6 | main: "#2196f3",
7 | light: "#4dabf5",
8 | dark: "#1769aa"
9 | },
10 | secondary: {
11 | main: "#f50057",
12 | light: "#f73378",
13 | dark: "#ab003c"
14 | }
15 | },
16 | typography: {
17 | useNextVariants: true
18 | }
19 | });
20 |
21 | export default AppTheme;
22 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { MuiThemeProvider } from "@material-ui/core/styles";
3 | import AppTheme from "./component/theme";
4 | import Toolbar from "./component/toolbar";
5 | import Main from "./component/main";
6 |
7 | class App extends Component {
8 | render() {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 | }
19 |
20 | export default App;
21 |
--------------------------------------------------------------------------------
/src/svg/hint.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import SvgIcon from "@material-ui/core/SvgIcon";
3 |
4 | class Icon extends React.Component {
5 | render() {
6 | return (
7 |
8 | {" "}
9 |
10 | );
11 | }
12 | }
13 |
14 | export default Icon;
15 |
--------------------------------------------------------------------------------
/src/component/main.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Switch, Route } from "react-router-dom";
3 | import Editor from "./edit";
4 | import Home from "./home";
5 | import SourceRule from "./sourceRule";
6 |
7 | import ScrollToTop from "./scrollToTop";
8 |
9 | const Main = () => (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 |
21 | export default Main;
22 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 40vmin;
8 | }
9 |
10 | .App-header {
11 | background-color: #282c34;
12 | min-height: 100vh;
13 | display: flex;
14 | flex-direction: column;
15 | align-items: center;
16 | justify-content: center;
17 | font-size: calc(10px + 2vmin);
18 | color: white;
19 | }
20 |
21 | .App-link {
22 | color: #61dafb;
23 | }
24 |
25 | @keyframes App-logo-spin {
26 | from {
27 | transform: rotate(0deg);
28 | }
29 | to {
30 | transform: rotate(360deg);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 | 阅读
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | // import React from 'react';
2 | // import ReactDOM from 'react-dom';
3 | // import './index.css';
4 | // import App from './App';
5 | // import * as serviceWorker from './serviceWorker';
6 |
7 | // ReactDOM.render(, document.getElementById('root'));
8 |
9 | // serviceWorker.unregister();
10 |
11 | import React from "react";
12 | import { render } from "react-dom";
13 | import { BrowserRouter } from "react-router-dom";
14 | import App from "./App";
15 |
16 | import "./index.css";
17 |
18 | render(
19 |
20 |
21 | ,
22 | document.getElementById("root")
23 | );
24 |
--------------------------------------------------------------------------------
/src/svg/github.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import SvgIcon from "@material-ui/core/SvgIcon";
3 |
4 | class Icon extends React.Component {
5 | render() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 | }
13 |
14 | export default Icon;
15 |
--------------------------------------------------------------------------------
/src/utils/HttpUtil.js:
--------------------------------------------------------------------------------
1 | var HTTPUtil = {};
2 |
3 | // const APIBaseURL = "http://qr.daguduiyuan.xyz/";
4 |
5 | HTTPUtil.get = function(param) {
6 | // var url = APIBaseURL + param;
7 | return fetch(param, {
8 | method: "GET",
9 | mode: "cors"
10 | }).then(function(response) {
11 | return response.json();
12 | });
13 | };
14 |
15 | HTTPUtil.post = function(param, formData) {
16 | return fetch(param, {
17 | method: "POST",
18 | mode: "cors",
19 | credentials: "include",
20 | headers: {
21 | Accept: "application/json",
22 | "Content-Type": "application/x-www-form-urlencoded"
23 | },
24 | body: formData
25 | }).then(response => {
26 | return response.json();
27 | });
28 | };
29 |
30 | export default HTTPUtil;
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yuedu-website",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^3.6.2",
7 | "@material-ui/icons": "^3.0.1",
8 | "copy-to-clipboard": "^3.0.8",
9 | "qrcode.react": "^0.8.0",
10 | "react": "^16.6.3",
11 | "react-dom": "^16.6.3",
12 | "react-router": "^4.3.1",
13 | "react-router-dom": "^4.3.1",
14 | "react-scripts": "2.1.1"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": "react-app"
24 | },
25 | "browserslist": [
26 | ">0.2%",
27 | "not dead",
28 | "not ie <= 11",
29 | "not op_mini all"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/src/component/sourceRule.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { withStyles } from "@material-ui/core/styles";
4 |
5 | const styles = () => ({
6 | ruleRoot: {
7 | paddingTop: 56,
8 | paddingLeft: 40,
9 | paddingRight: 40,
10 | height: "100%"
11 | },
12 | ruleFrame: {
13 | paddingTop: 24
14 | }
15 | });
16 |
17 | class sourceRule extends React.Component {
18 | state = {};
19 |
20 | render() {
21 | const { classes } = this.props;
22 | return (
23 |
37 | );
38 | }
39 | }
40 |
41 | sourceRule.propTypes = {
42 | classes: PropTypes.object.isRequired
43 | };
44 |
45 | export default withStyles(styles)(sourceRule);
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/node,macos,visualstudiocode
3 | # Edit at https://www.gitignore.io/?templates=node,macos,visualstudiocode
4 |
5 | ### macOS ###
6 | # General
7 | .DS_Store
8 | .AppleDouble
9 | .LSOverride
10 |
11 | # Icon must end with two \r
12 | Icon
13 |
14 | # Thumbnails
15 | ._*
16 |
17 | # Files that might appear in the root of a volume
18 | .DocumentRevisions-V100
19 | .fseventsd
20 | .Spotlight-V100
21 | .TemporaryItems
22 | .Trashes
23 | .VolumeIcon.icns
24 | .com.apple.timemachine.donotpresent
25 |
26 | # Directories potentially created on remote AFP share
27 | .AppleDB
28 | .AppleDesktop
29 | Network Trash Folder
30 | Temporary Items
31 | .apdisk
32 |
33 | ### Node ###
34 | # Logs
35 | logs
36 | *.log
37 | npm-debug.log*
38 | yarn-debug.log*
39 | yarn-error.log*
40 |
41 | # Runtime data
42 | pids
43 | *.pid
44 | *.seed
45 | *.pid.lock
46 |
47 | # Directory for instrumented libs generated by jscoverage/JSCover
48 | lib-cov
49 |
50 | # Coverage directory used by tools like istanbul
51 | coverage
52 |
53 | # nyc test coverage
54 | .nyc_output
55 |
56 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
57 | .grunt
58 |
59 | # Bower dependency directory (https://bower.io/)
60 | bower_components
61 |
62 | # node-waf configuration
63 | .lock-wscript
64 |
65 | # Compiled binary addons (https://nodejs.org/api/addons.html)
66 | build/Release
67 |
68 | # Dependency directories
69 | node_modules/
70 | jspm_packages/
71 |
72 | # TypeScript v1 declaration files
73 | typings/
74 |
75 | # Optional npm cache directory
76 | .npm
77 |
78 | # Optional eslint cache
79 | .eslintcache
80 |
81 | # Optional REPL history
82 | .node_repl_history
83 |
84 | # Output of 'npm pack'
85 | *.tgz
86 |
87 | # Yarn Integrity file
88 | .yarn-integrity
89 |
90 | # dotenv environment variables file
91 | .env
92 |
93 | # parcel-bundler cache (https://parceljs.org/)
94 | .cache
95 |
96 | # next.js build output
97 | .next
98 |
99 | # nuxt.js build output
100 | .nuxt
101 |
102 | # vuepress build output
103 | .vuepress/dist
104 |
105 | # Serverless directories
106 | .serverless/
107 |
108 | # FuseBox cache
109 | .fusebox/
110 |
111 | #DynamoDB Local files
112 | .dynamodb/
113 |
114 | ### VisualStudioCode ###
115 | .vscode/*
116 | !.vscode/settings.json
117 | !.vscode/tasks.json
118 | !.vscode/launch.json
119 | !.vscode/extensions.json
120 |
121 | ### VisualStudioCode Patch ###
122 | # Ignore all local history of files
123 | .history
124 |
125 | # End of https://www.gitignore.io/api/node,macos,visualstudiocode
126 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read http://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/component/toolbar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { withStyles } from "@material-ui/core/styles";
4 | import AppBar from "@material-ui/core/AppBar";
5 | import Toolbar from "@material-ui/core/Toolbar";
6 | import Typography from "@material-ui/core/Typography";
7 | import IconButton from "@material-ui/core/IconButton";
8 | import MenuIcon from "@material-ui/icons/Menu";
9 | import GithubSvg from "../svg/github";
10 |
11 | import Drawer from "@material-ui/core/Drawer";
12 |
13 | import List from "@material-ui/core/List";
14 | import Divider from "@material-ui/core/Divider";
15 | import IndexIcon from "@material-ui/icons/School";
16 | import CropIcon from "@material-ui/icons/Crop";
17 | import EditIcon from "@material-ui/icons/Edit";
18 | import ListItemIcon from "@material-ui/core/ListItemIcon";
19 |
20 | import MenuItem from "@material-ui/core/MenuItem";
21 |
22 | import { Link } from "react-router-dom";
23 |
24 | import HintSvg from "../svg/hint";
25 |
26 | import Button from "@material-ui/core/Button";
27 | import Dialog from "@material-ui/core/Dialog";
28 | import DialogActions from "@material-ui/core/DialogActions";
29 | import DialogContent from "@material-ui/core/DialogContent";
30 | import DialogContentText from "@material-ui/core/DialogContentText";
31 | import DialogTitle from "@material-ui/core/DialogTitle";
32 |
33 | const styles = {
34 | root: {},
35 | grow: {
36 | flexGrow: 1,
37 | userSelect: "none"
38 | },
39 | menuButton: {
40 | marginLeft: -12,
41 | marginRight: 20
42 | },
43 | sourceRule: {
44 | width: "100%",
45 | paddingTop: "80px"
46 | },
47 | dialogToolbar: {
48 | position: "fixed",
49 | width: "100%"
50 | },
51 | rootToolbar: {
52 | position: "fixed",
53 | width: "100%"
54 | },
55 | mdRoot: {
56 | paddingLeft: "32px",
57 | paddingRight: "32px"
58 | },
59 | list: {
60 | width: 300
61 | },
62 | fullList: {
63 | width: "auto"
64 | },
65 | header: {
66 | width: 300,
67 | height: 175,
68 | background: "url(../img/header.png)",
69 | backgroundSize: "300px 175px"
70 | },
71 | menuItem: {
72 | marginTop: 8,
73 | marginBottom: 8
74 | },
75 | links: {
76 | textDecoration: "none"
77 | },
78 | hintContent: {
79 | marginTop: "12px",
80 | marginBottom: "12px"
81 | }
82 | };
83 |
84 | class MenuAppBar extends React.Component {
85 | state = {
86 | auth: true,
87 | anchorEl: null,
88 | left: false,
89 | selectedIndex: 0,
90 | open: false,
91 | scroll: "paper"
92 | };
93 |
94 | jumpUrl = url => {
95 | window.location.href = url;
96 | };
97 |
98 | handleClickOpen = () => {
99 | this.setState({ open: true });
100 | };
101 |
102 | handleClose = () => {
103 | this.setState({ open: false });
104 | };
105 |
106 | openDrawer = () => {
107 | this.setState({
108 | left: true
109 | });
110 | };
111 |
112 | toggleDrawer = (side, open) => () => {
113 | this.setState({
114 | [side]: open
115 | });
116 | };
117 |
118 | handleMenuItemClick = (event, index) => {
119 | this.setState({ selectedIndex: index });
120 | };
121 |
122 | componentDidMount() {
123 | var selectIndex = 0;
124 | switch (window.location.pathname) {
125 | case "/":
126 | selectIndex = 0;
127 | break;
128 | case "/rule":
129 | selectIndex = 1;
130 | break;
131 | case "/editor":
132 | selectIndex = 2;
133 | break;
134 | default:
135 | selectIndex = 0;
136 | break;
137 | }
138 | this.setState({
139 | selectedIndex: selectIndex
140 | });
141 | }
142 |
143 | render() {
144 | const { classes } = this.props;
145 | const icons = [, , ];
146 | const links = ["/", "/rule", "editor"];
147 | const sideList = (
148 |
149 |
150 |
151 | {["首页", "书源规则", "书源编辑器"].map((text, index) => (
152 |
153 |
162 |
163 | ))}
164 |
165 |
166 | );
167 |
168 | return (
169 |
170 |
174 |
175 | {/*
*/}
176 | {/*
让阅读成为一种习惯
*/}
177 |
178 |
184 | {sideList}
185 |
186 |
187 |
188 |
189 |
190 |
196 |
197 |
198 |
199 | 阅读
200 |
201 |
202 |
203 |
204 |
205 | this.jumpUrl("https://github.com/zsakvo")}
208 | >
209 |
210 |
211 |
212 |
213 |
214 |
245 |
246 | );
247 | }
248 | }
249 |
250 | MenuAppBar.propTypes = {
251 | classes: PropTypes.object.isRequired
252 | };
253 |
254 | export default withStyles(styles)(MenuAppBar);
255 |
--------------------------------------------------------------------------------
/src/component/home.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { withStyles } from "@material-ui/core/styles";
4 |
5 | import Typography from "@material-ui/core/Typography";
6 |
7 | import Fab from "@material-ui/core/Fab";
8 | import DownIcon from "@material-ui/icons/CloudDownload";
9 |
10 | import SpaIcon from "@material-ui/icons/Spa";
11 | import AllIcon from "@material-ui/icons/AllInclusive";
12 | import NearIcon from "@material-ui/icons/NearMe";
13 | import EditIcon from "@material-ui/icons/Edit";
14 | import TuneIcon from "@material-ui/icons/Tune";
15 | import LayersIcon from "@material-ui/icons/Layers";
16 |
17 | import Button from "@material-ui/core/Button";
18 |
19 | const styles = () => ({
20 | homeRoot: {
21 | paddingTop: 64,
22 | display: "flex",
23 | flexDirection: "row",
24 | flexWrap: "wrap",
25 | justifyContent: "space-around"
26 | },
27 | logo: {
28 | marginTop: "100px",
29 | width: "256px",
30 | height: "256px",
31 | backgroundRepeat: "no-repeat",
32 | backgroundSize: "contain",
33 | background: "url(../img/read.png)"
34 | },
35 | typographyH4: {
36 | paddingTop: "64px",
37 | width: "100%"
38 | },
39 | typographyH6: {
40 | paddingTop: 18,
41 | paddingLeft: "4%",
42 | paddingRight: "4%",
43 | width: "100%"
44 | },
45 | fabDiv: {
46 | marginTop: 64,
47 | width: "100%",
48 | textAlign: "center"
49 | },
50 | fab: {
51 | marginRight: "-18px"
52 | },
53 | extendedIcon: {
54 | marginRight: 16
55 | },
56 | cardDiv: {
57 | marginTop: "120px",
58 | paddingTop: "84px",
59 | marginBottom: "120px",
60 | paddingBottom: "84px",
61 | width: "100%",
62 | display: "flex",
63 | flexDirection: "row",
64 | flexWrap: "wrap",
65 | backgroundColor: "rgba(226,225,228,0.3)",
66 | justifyContent: "space-around"
67 | },
68 | detailDiv: {
69 | width: "40%",
70 | minWidth: "400px",
71 | display: "flex",
72 | flexDirection: "row",
73 | marginLeft: "5%",
74 | marginRight: "5%",
75 | marginTop: "30px"
76 | },
77 | detailIcon: {
78 | width: "30px",
79 | height: "30px",
80 | marginRight: "20px"
81 | },
82 | detailTextDiv: {
83 | display: "flex",
84 | flexDirection: "column",
85 | marginBottom: "48px"
86 | },
87 | detailTitle: {},
88 | detailContent: {},
89 | itemempty: {
90 | height: "0px",
91 | width: "33%",
92 | minWidth: "420px"
93 | },
94 | titleTypography: {
95 | marginBottom: "14px"
96 | },
97 | detailTypography: {},
98 | bottomDiv: {
99 | width: "100%",
100 | background: "#424242",
101 | display: "flex",
102 | flexDirection: "column"
103 | },
104 | divider: {
105 | width: "90%",
106 | height: "1px",
107 | marginRight: "5%",
108 | marginLeft: "5%",
109 | background: "#878787"
110 | },
111 | bottomContent: {
112 | display: "flex",
113 | flexDirection: "row",
114 | justifyContent: "space-around",
115 | paddingLeft: "10%",
116 | paddingRight: "10%",
117 | flexWrap: "wrap"
118 | },
119 | community: {
120 | flexGrow: 1,
121 | marginTop: "64px"
122 | },
123 | resource: {
124 | flexGrow: 1
125 | },
126 | donate: {
127 | flexGrow: 2
128 | },
129 | bottomTextTitle: {
130 | color: "#ffffff",
131 | fontWeight: "900",
132 | fontSize: "20px",
133 | marginBottom: "24px"
134 | },
135 | bottomTextContent: {
136 | width: "100%",
137 | fontWeight: "600",
138 | marginTop: "12px",
139 | color: "#9e9e9e"
140 | },
141 | donateButton: {},
142 | copyRight: {
143 | paddingTop: "24px",
144 | paddingBottom: "24px",
145 | color: "#9e9e9e",
146 | textAlign: "center"
147 | },
148 | donateMe: {
149 | display: "flex",
150 | flexDirection: "row",
151 | marginTop: "36px",
152 | marginBottom: "36px"
153 | },
154 | qrcode: {
155 | width: "136px",
156 | height: "136px",
157 | marginLeft: "40px"
158 | },
159 | links: {
160 | textDecoration: "none"
161 | }
162 | });
163 |
164 | class home extends React.Component {
165 | state = {
166 | qrcodeDisplay: "none",
167 | qrcodeState: false,
168 | donateButtonText: "显示捐赠二维码"
169 | };
170 |
171 | showQrcode = () => {
172 | this.setState({
173 | qrcodeDisplay: "block",
174 | qrcodeState: true,
175 | donateButtonText: "隐藏捐赠二维码"
176 | });
177 | };
178 |
179 | hideQrcode = () => {
180 | this.setState({
181 | qrcodeDisplay: "none",
182 | qrcodeState: false,
183 | donateButtonText: "显示捐赠二维码"
184 | });
185 | };
186 |
187 | clickDonateButton = () => {
188 | if (this.state.qrcodeState) {
189 | this.hideQrcode();
190 | } else {
191 | this.showQrcode();
192 | }
193 | };
194 |
195 | render() {
196 | const { classes } = this.props;
197 | return (
198 |
199 |
200 |
207 | 阅 读
208 |
209 |
215 | 阅读是一款提供网络文学搜索的工具,为广大网络文学爱好者提供一种方便、快捷舒适的试读体验。
216 |
217 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
241 | 界面简洁,专注阅读
242 |
243 |
248 | 点击直接打开书架,无广告,无多余功能。拒绝各种附加服务的打扰,只为阅读文字而生
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
262 | 体积虽小,功能齐全
263 |
264 |
269 | 6.41MB
270 | 的体积中容纳了强大的书源解析引擎,支持多途径的书源管理,书籍的并行缓存等服务,您亦可以自行设置规则,对书籍内指定字符进行过滤和替换
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
284 | 发现版块,寻书简便
285 |
286 |
291 | 从多个网站获取最新的书籍排行信息,力求提供最全面、最真实的排名资讯,茫茫书海,任你遨游。
292 |
293 |
294 |
295 |
296 |
299 |
300 |
305 | 书源丰富,全网尽收
306 |
307 |
312 | 拥有由爱好者共同分享维护的数百个书源,覆盖网文,出版书,轻小说等各个类型的书籍,一个软件,全网通吃
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
326 | 编辑书源,自给自足
327 |
328 |
333 | 书源引擎支持 css select,JsonPath,XPath
334 | 等多种语法,同时可以使用 JavaScript
335 | 语句对返回值进行处理,为不同的网站带来相同的阅读体验
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
349 | 自定排版,功能丰富
350 |
351 |
356 | 从字体到间距,从阅读背景到翻页动画,均可自行设定相关参数,适合自己的才是最好的。
357 |
358 |
359 |
360 |
361 |
362 |
363 |
382 |
395 |
396 |
397 |
赞助该项目
398 |
399 | 本软件为开源软件, 没有上架 Google Play, 没有在任何地方售卖,
400 | 如果想支持我请通过软件里的捐赠, 不要在任何地方购买!
401 |
402 |
403 |
411 |

417 |

423 |
424 |
425 |
426 |
427 |
428 |
429 | This page is powered by zsakvo .
430 |
431 |
432 |
433 | );
434 | }
435 | }
436 |
437 | home.propTypes = {
438 | classes: PropTypes.object.isRequired
439 | };
440 |
441 | export default withStyles(styles)(home);
442 |
--------------------------------------------------------------------------------
/src/component/edit.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { withStyles } from "@material-ui/core/styles";
4 | import TextField from "@material-ui/core/TextField";
5 | import colorTheme from "./theme";
6 |
7 | import Fab from "@material-ui/core/Fab";
8 | import CheckIcon from "@material-ui/icons/Check";
9 | import Icon from "@material-ui/core/Icon";
10 |
11 | import Button from "@material-ui/core/Button";
12 | import Dialog from "@material-ui/core/Dialog";
13 | import DialogActions from "@material-ui/core/DialogActions";
14 | import DialogContent from "@material-ui/core/DialogContent";
15 | import copy from "copy-to-clipboard";
16 | import DialogTitle from "@material-ui/core/DialogTitle";
17 |
18 | import HttpUtil from "../utils/HttpUtil";
19 |
20 | var QRCode = require("qrcode.react");
21 |
22 | const styles = theme => ({
23 | container: {
24 | display: "flex",
25 | flexWrap: "wrap"
26 | },
27 | textField: {
28 | marginLeft: theme.spacing.unit * 2,
29 | marginRight: theme.spacing.unit * 2,
30 | width: "30%",
31 | minWidth: "300px"
32 | },
33 | dense: {
34 | marginTop: 19
35 | },
36 | menu: {
37 | width: 200
38 | },
39 | root: {
40 | display: "block",
41 | paddingTop: "48px"
42 | },
43 | sourceInfo: {
44 | display: "flex",
45 | flexDirection: "row",
46 | flexWrap: "wrap",
47 | justifyContent: "space-around",
48 | paddingLeft: "6%",
49 | paddingRight: "6%"
50 | },
51 | find: {
52 | display: "flex",
53 | flexDirection: "row",
54 | flexWrap: "wrap",
55 | justifyContent: "space-around",
56 | paddingLeft: "6%",
57 | paddingRight: "6%"
58 | },
59 | search: {
60 | display: "flex",
61 | flexDirection: "row",
62 | flexWrap: "wrap",
63 | justifyContent: "space-around",
64 | paddingLeft: "6%",
65 | paddingRight: "6%"
66 | },
67 | detail: {
68 | display: "flex",
69 | flexDirection: "row",
70 | flexWrap: "wrap",
71 | justifyContent: "space-around",
72 | paddingLeft: "6%",
73 | paddingRight: "6%",
74 | marginBottom: "120px"
75 | },
76 | chip: {
77 | width: 200
78 | },
79 | divider: {
80 | marginTop: 70,
81 | marginBottom: 10,
82 | userSelect: "none",
83 | width: "100%",
84 | color: colorTheme.palette.primary.main
85 | },
86 | itemempty: {
87 | height: "0px",
88 | marginLeft: theme.spacing.unit * 2,
89 | marginRight: theme.spacing.unit * 2,
90 | width: "30%",
91 | minWidth: "300px"
92 | },
93 | fab: {
94 | position: "fixed",
95 | bottom: theme.spacing.unit * 4,
96 | right: theme.spacing.unit * 4,
97 | zIndex: 888
98 | },
99 | extendedIcon: {
100 | marginRight: theme.spacing.unit
101 | },
102 | qrContent: {
103 | paddingLeft: "48px",
104 | paddingRight: "48px"
105 | }
106 | });
107 |
108 | class TextFields extends React.Component {
109 | state = { open: false, bookSource: "", scroll: "paper", bookSourceUrl: "" };
110 |
111 | handleClickOpen = () => {
112 | this.setState({ open: true });
113 | };
114 |
115 | handleClose = () => {
116 | this.setState({ open: false });
117 | };
118 |
119 | copySource = () => {
120 | copy(this.state.bookSource);
121 | this.handleClose();
122 | };
123 |
124 | buildJson = () => {
125 | this.setState({
126 | fabDisabled: true
127 | });
128 | var json = {};
129 | var bookSourceName = this.bookSourceName.value;
130 | var bookSourceGroup = this.bookSourceGroup.value;
131 | var bookSourceUrl = this.bookSourceUrl.value;
132 | var loginUrl = this.loginUrl.value;
133 | var checkUrl = this.checkUrl.value;
134 | var httpUserAgent = this.httpUserAgent.value;
135 | var ruleFindUrl = this.ruleFindUrl.value;
136 | var ruleSearchUrl = this.ruleSearchUrl.value;
137 | var ruleSearchList = this.ruleSearchList.value;
138 | var ruleSearchName = this.ruleSearchName.value;
139 | var ruleSearchAuthor = this.ruleSearchAuthor.value;
140 | var ruleSearchCoverUrl = this.ruleSearchCoverUrl.value;
141 | var ruleSearchKind = this.ruleSearchKind.value;
142 | var ruleSearchLastChapter = this.ruleSearchLastChapter.value;
143 | var ruleSearchNoteUrl = this.ruleSearchNoteUrl.value;
144 | var ruleBookUrlPattern = this.ruleBookUrlPattern.value;
145 | var ruleBookName = this.ruleBookName.value;
146 | var ruleBookAuthor = this.ruleBookAuthor.value;
147 | var ruleCoverUrl = this.ruleCoverUrl.value;
148 | var ruleBookKind = this.ruleBookKind.value;
149 | var ruleBookLastChapter = this.ruleBookLastChapter.value;
150 | var ruleChapterUrl = this.ruleChapterUrl.value;
151 | var ruleChapterUrlNext = this.ruleChapterUrlNext.value;
152 | var ruleIntroduce = this.ruleIntroduce.value;
153 | var ruleChapterList = this.ruleChapterList.value;
154 | var ruleChapterName = this.ruleChapterName.value;
155 | var ruleContentUrl = this.ruleContentUrl.value;
156 | var ruleContentUrlNext = this.ruleContentUrlNext.value;
157 | var ruleBookContent = this.ruleBookContent.value;
158 | json.bookSourceName = bookSourceName;
159 | json.bookSourceGroup = bookSourceGroup;
160 | json.bookSourceUrl = bookSourceUrl;
161 | json.loginUrl = loginUrl;
162 | json.checkUrl = checkUrl;
163 | json.httpUserAgent = httpUserAgent;
164 | json.ruleFindUrl = ruleFindUrl;
165 | json.ruleSearchUrl = ruleSearchUrl;
166 | json.ruleSearchList = ruleSearchList;
167 | json.ruleSearchName = ruleSearchName;
168 | json.ruleSearchAuthor = ruleSearchAuthor;
169 | json.ruleSearchCoverUrl = ruleSearchCoverUrl;
170 | json.ruleSearchKind = ruleSearchKind;
171 | json.ruleSearchLastChapter = ruleSearchLastChapter;
172 | json.ruleSearchNoteUrl = ruleSearchNoteUrl;
173 | json.ruleBookUrlPattern = ruleBookUrlPattern;
174 | json.ruleBookName = ruleBookName;
175 | json.ruleBookAuthor = ruleBookAuthor;
176 | json.ruleCoverUrl = ruleCoverUrl;
177 | json.ruleBookKind = ruleBookKind;
178 | json.ruleBookLastChapter = ruleBookLastChapter;
179 | json.ruleChapterUrl = ruleChapterUrl;
180 | json.ruleChapterUrlNext = ruleChapterUrlNext;
181 | json.ruleIntroduce = ruleIntroduce;
182 | json.ruleChapterList = ruleChapterList;
183 | json.ruleChapterName = ruleChapterName;
184 | json.ruleContentUrl = ruleContentUrl;
185 | json.ruleContentUrlNext = ruleContentUrlNext;
186 | json.ruleBookContent = ruleBookContent;
187 | HttpUtil.post("/createQr", "type=book&json=" + JSON.stringify(json))
188 | .then(data => {
189 | console.log(data);
190 | console.log(data.result);
191 | console.log(data.id);
192 | if (data.result === "success") {
193 | this.setState(
194 | {
195 | fabDisabled: false,
196 | dialogTitle: "生成完毕",
197 | qrShow: "block",
198 | bookSource: JSON.stringify(json),
199 | bookSourceUrl: "http://qr.daguduiyuan.xyz/getJson?id=" + data.id
200 | },
201 | () => {
202 | this.handleClickOpen();
203 | }
204 | );
205 | } else {
206 | this.setState(
207 | {
208 | fabDisabled: false,
209 | dialogTitle: "活码生成失败",
210 | qrShow: "none",
211 | bookSource: JSON.stringify(json)
212 | },
213 | () => {
214 | this.handleClickOpen();
215 | }
216 | );
217 | }
218 | })
219 | .catch(exception => {
220 | console.log(exception);
221 | this.setState(
222 | {
223 | fabDisabled: false,
224 | dialogTitle: "活码生成失败",
225 | qrShow: "none",
226 | bookSource: JSON.stringify(json)
227 | },
228 | () => {
229 | this.handleClickOpen();
230 | }
231 | );
232 | });
233 | };
234 |
235 | render() {
236 | const { classes } = this.props;
237 |
238 | return (
239 |
240 |
247 |
248 |
249 |
250 |
251 |
252 |
书源基础信息
253 |
(this.bookSourceName = el)}
259 | />
260 | (this.bookSourceGroup = el)}
266 | />
267 | (this.bookSourceUrl = el)}
273 | />
274 | (this.loginUrl = el)}
280 | />
281 | (this.checkUrl = el)}
287 | />
288 | (this.httpUserAgent = el)}
294 | />
295 |
296 |
297 |
298 |
299 |
300 |
301 |
发现规则
302 |
(this.ruleFindUrl = el)}
309 | />
310 |
311 |
312 |
313 |
314 |
315 |
316 |
搜索规则
317 |
(this.ruleSearchUrl = el)}
323 | />
324 | (this.ruleSearchList = el)}
330 | />
331 | (this.ruleSearchName = el)}
337 | />
338 | (this.ruleSearchAuthor = el)}
344 | />
345 | (this.ruleSearchKind = el)}
351 | />
352 | (this.ruleSearchLastChapter = el)}
358 | />
359 | (this.ruleSearchCoverUrl = el)}
365 | />
366 | (this.ruleSearchNoteUrl = el)}
373 | />
374 |
375 |
376 |
377 |
378 |
379 |
380 |
书籍内容规则
381 |
(this.ruleBookUrlPattern = el)}
387 | />
388 | (this.ruleBookName = el)}
394 | />
395 | (this.ruleBookAuthor = el)}
401 | />
402 | (this.ruleCoverUrl = el)}
408 | />
409 | (this.ruleBookKind = el)}
415 | />
416 | (this.ruleBookLastChapter = el)}
422 | />
423 | (this.ruleChapterUrl = el)}
429 | />
430 | (this.ruleChapterUrlNext = el)}
436 | />
437 | (this.ruleIntroduce = el)}
443 | />
444 | (this.ruleChapterList = el)}
450 | />
451 | (this.ruleChapterName = el)}
457 | />
458 | (this.ruleContentUrl = el)}
464 | />
465 | (this.ruleContentUrlNext = el)}
471 | />
472 | (this.ruleBookContent = el)}
479 | />
480 |
481 |
482 |
483 |
484 |
485 |
486 |
505 |
506 | );
507 | }
508 | }
509 |
510 | TextFields.propTypes = {
511 | classes: PropTypes.object.isRequired
512 | };
513 |
514 | export default withStyles(styles)(TextFields);
515 |
--------------------------------------------------------------------------------
/public/bookSource.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bookSource
6 |
903 |
904 |
905 |
书源制作教程
906 |
907 |
908 | 第三方教程:
909 | https://www.hostfans.cn/make-course
910 | https://www.52pojie.cn/thread-758541-1-1.html
911 |
912 |
913 |
规则更新日志
914 |
915 |
2.18.120813 版本:
916 |
917 |
918 | 支持 XPath 语法, 以 @XPath: 开头, 语法见 http://www.w3school.com.cn/xpath/index.asp
919 | 支持 JSonPath 语法, 以 @JSon: 开头, 语法见 https://blog.csdn.net/koflance/article/details/63262484
920 | XPath 语法测试地址 http://www.bejson.com/testtools/xpath/
921 | JSonPath 语法测试地址 http://jsonpath.herokuapp.com
922 | 支持用 js 处理结果, 以 @js: 开头, 结果变量为 result 如
923 |
924 |
925 |
@JSon:$.link@js:"http://chapterup.zhuishushenqi.com/chapter/" + encodeURIComponent(result)
926 |
927 |
928 | 注意 JSon 的值如果不是 String, 用 js 处理时需自己进行类型转换
929 | 注意 #替换规则在新语法下无法使用, 新的语法用 js 处理结果
930 | 原有的规则不变, 见下方
931 |
932 |
933 |
书源规则说明
934 |
935 |
936 | 书源规则基于 HTML 标记, 如 class,id,tag 等
937 | 想要写规则先要打开网页源代码, 在里面找到想要获取内容对应的标签
938 | Chrome 可以在网页上右击点击检查可以方便的查看标签
939 |
940 |
941 |
基本写法
942 |
943 |
基本规则
944 |
945 |
946 | @为分隔符, 用来分隔获取规则
947 | 每段规则可分为 3 段
948 | 第一段是类型, 如 class,id,tag,text,children 等, children 获取所有子标签, 不需要第二段和第三段, text 可以根据文本内容获取
949 | 第二段是名称, text 第二段为文本内容的一部分
950 | 第三段是位置, class,tag 会获取到多个, 所以要加位置, id 类型不要加
951 | 如不加位置会获取所有
952 | 位置正数从 0 开始, 0 是第一个, 如为负数则是取倒数的值, -1 为最倒数第一个,-2 为倒数第二个
953 | ! 是排除, 有些位置不符合需要排除用!, 后面的序号用: 隔开 0 是第 1 个, 负数为倒数序号,-1 最后一个,-2 倒数第 2 个, 依次
954 | 获取列表的最前面加上负号 - 可以使列表倒置, 有些网站目录列表是倒的, 前面加个负号可变为正的
955 | @ 的最后一段为获取内容, 如 text,textNodes,href,src,html 等
956 | 如果有不同网页的规则可以用 | 或 & 分隔 或 %
957 | | 会以第一个取到值的为准
958 | & 会合并所有规则取到的值
959 | % 会依次取数, 如三个列表, 先取列表 1 的第一个, 再取列表 2 的第一个, 再取列表 3 的第一个, 再取列表 1 的第 2 个.......
960 | 如需要正则替换在最后加上 #正则表达式 #替换为
961 | 例:class.odd.0@tag.a.0@text|tag.dd.0@tag.h1@text# 全文阅读
962 | 例:class.odd.0@tag.a.0@text&tag.dd.0@tag.h1@text# 全文阅读
963 |
964 |
965 |
名词解释
966 |
967 |
968 | BookSourceUrl 书源网址
969 | BookSourceName 书源名称
970 | BookSourceGroup 书源分组
971 | RuleSearchUrl 搜索网址
972 |
973 | 例:http://www.gxwztv.com/search.htm?keyword=searchKey&pn=searchPage-1
974 | ? 为 get,@为 post
975 | searchKey 为关键字标识, 运行时会替换为搜索关键字,
976 | searchPage 为搜索页数, 从 0 开始的用 searchPage-1
977 | page 规则还可以写成 {index(第一页), indexSecond(第二页), indexThird(第三页), index-searchPage+1 或 index-searchPage-1 或 index-searchPage}
978 | 要添加转码编码在最后加 |char=gbk
979 | |char=escape 会模拟 js escape 方法进行编码
980 |
981 | ruleFindUrl 发现规则
982 |
983 | 发现规则分为两段, 名称和 url 用:: 分开, 如
984 | 起点风云榜::https://www.qidian.com/rank/yuepiao?page=searchPage
985 | url 规则和搜索规则一样, 多个规则用 && 或换行分开, 如
986 | 起点风云榜::https://www.qidian.com/rank/yuepiao?page=searchPage&& 原创风云榜::https://www.qidian.com/rank/yuepiao?style=1&page=searchPage
987 | 也可以每行写一个, 域名可以省略, 如省略会调用书源域名
988 | 起点风云榜::/rank/yuepiao?page=searchPage
989 | 原创风云榜::/rank/yuepiao?style=1&page=searchPage
990 |
991 | RuleSearchList 搜索列表
992 |
993 | 例: class.list-group-item!0:%
994 |
995 | RuleSearchAuthor 搜索里的作者
996 |
997 | 例: class.col-xs-2.0@text
998 |
999 | RuleSearchKind 搜索里的类型
1000 |
1001 | 例: class.col-xs-1.0@text
1002 |
1003 | RuleSearchLastChapter 搜索里的最新章节
1004 |
1005 | 例:class.col-xs-4.0@tag.a.0@text
1006 |
1007 | RuleSearchName 获取搜索里的书名
1008 |
1009 | 例:class.col-xs-3.0@tag.a.0@text
1010 |
1011 | RuleSearchNoteUrl 搜索里的书链接
1012 |
1013 | 例:class.col-xs-3.0@tag.a.0@href
1014 |
1015 | RuleSearchCoverUrl 搜索里的书封面
1016 |
1017 | 例: tag.img.0@src
1018 |
1019 | RuleBookUrlPattern 书籍页面里的 URL 正则
1020 |
1021 | 例: https?://www.piaotian.com/bookinfo/.*"
1022 |
1023 | RuleBookName 书籍页面里的书名称
1024 |
1025 | 例: class.active.0@text
1026 |
1027 | RuleBookAuthor 书籍页面里的作者
1028 |
1029 | 例:class.col-xs-12.0@tag.small.0@text
1030 |
1031 | RuleIntroduce 书籍页面里的简介
1032 |
1033 | 例: class.panel panel-default mt20.0@id.shot@text
1034 |
1035 | RuleCoverUrl 书籍页面里的封面
1036 |
1037 | 例:class.panel-body.0@class.img-thumbnail.0@src
1038 |
1039 | RuleBookKind 书籍页面里的分类
1040 |
1041 | 例:id.centerm@tag.td.3@text#.*:
1042 |
1043 | RuleBookLastChapter 书籍页面里的最新章节
1044 |
1045 | 例 class.grid.0@tag.td.0@tag.a@text
1046 |
1047 | RuleChapterUrl 书籍页面里的目录地址
1048 |
1049 | 如果目录地址和书籍地址一样则留空
1050 | 例: class.list-group-item tac.0@tag.a.0@href
1051 |
1052 | RuleChapterUrlNext 目录页面里的下一页目录地址
1053 |
1054 | 如果没有下一页则留空
1055 | 例: class.page.0@text.下一页.0@href 也可以获取所有目录页 >url 的列表, 会依次获取
1056 | 例:class.chapter.0@tag.a@href (class.chapter.0) 里包含第 1 页第 2 页第三页等
1057 |
1058 | RuleChapterList 目录页面的目录列表
1059 |
1060 | 前面加 - 号倒序排列
1061 | 例:id.chapters-list@tag.a
1062 | 例:-id.chapters-list@tag.a
1063 |
1064 | RuleChapterName 目录列表的章节名称
1065 |
1066 | 例: text
1067 |
1068 | RuleContentUrl 目录列表的章节链接
1069 |
1070 | 例: href
1071 |
1072 | RuleBookContent 章节内容
1073 |
1074 | 例: id.txtContent@textNodes
1075 | 如需获取 AJAX 动态内容前面加 $
1076 | 例:$id.txtContent@textNodes
1077 |
1078 |
1079 |
1080 |
1081 |
1082 |
1083 |
--------------------------------------------------------------------------------