├── service
├── .eslintignore
├── .eslintrc
├── jsconfig.json
├── .gitignore
├── app
│ ├── router.js
│ ├── middleware
│ │ └── adminauth.js
│ ├── router
│ │ ├── default.js
│ │ └── admin.js
│ └── controller
│ │ ├── default
│ │ └── home.js
│ │ └── admin
│ │ └── main.js
├── .travis.yml
├── appveyor.yml
├── config
│ ├── plugin.js
│ └── config.default.js
├── .autod.conf.js
├── test
│ └── app
│ │ └── controller
│ │ └── home.test.js
├── README.md
└── package.json
├── admin_app
├── src
│ ├── react-app-env.d.ts
│ ├── static
│ │ └── css
│ │ │ ├── ArticleList.css
│ │ │ ├── BBDList.css
│ │ │ ├── Login.css
│ │ │ ├── AdminIndex.css
│ │ │ └── AddArticle.css
│ ├── index.js
│ ├── pages
│ │ ├── Main.js
│ │ ├── Login.js
│ │ ├── AdminIndex.js
│ │ ├── ArticleList.js
│ │ ├── BBDList.js
│ │ └── AddArticle.js
│ ├── config
│ │ └── apiUrl.js
│ └── components
│ │ └── tocify.tsx
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .gitignore
├── tsconfig.json
├── package.json
└── README.md
├── blog
├── static
│ ├── favicon.ico
│ ├── style
│ │ ├── components
│ │ │ ├── footer.css
│ │ │ ├── advert.css
│ │ │ ├── studyLine.css
│ │ │ ├── rightmi.css
│ │ │ ├── author.css
│ │ │ └── header.css
│ │ └── pages
│ │ │ ├── bibidao.css
│ │ │ ├── list.css
│ │ │ ├── index.css
│ │ │ ├── comm.css
│ │ │ ├── detailed.css
│ │ │ └── han.css
│ └── test.md
├── now.json
├── pages
│ ├── _app.js
│ ├── bibidao.js
│ ├── list.js
│ ├── detailed.js
│ └── index.js
├── next.config.js
├── components
│ ├── Footer.js
│ ├── Heading.js
│ ├── Advert.js
│ ├── HeadingBlock.js
│ ├── CodeBlock.js
│ ├── StudyLine.js
│ ├── tocify.tsx
│ ├── LearningRoute.js
│ ├── Header.js
│ ├── Author.js
│ └── Rightmi.js
├── .babelrc
├── .gitignore
├── config
│ └── apiUrl.js
└── package.json
├── .gitignore
└── README.md
/service/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 |
--------------------------------------------------------------------------------
/service/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-egg"
3 | }
4 |
--------------------------------------------------------------------------------
/service/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "**/*"
4 | ]
5 | }
--------------------------------------------------------------------------------
/admin_app/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/admin_app/src/static/css/ArticleList.css:
--------------------------------------------------------------------------------
1 | .list-div{
2 | width: 100%;
3 | }
--------------------------------------------------------------------------------
/admin_app/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/admin_app/src/static/css/BBDList.css:
--------------------------------------------------------------------------------
1 | .cart-list-url{
2 | word-wrap:break-word
3 |
4 | }
--------------------------------------------------------------------------------
/blog/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shenghy/react_blog/HEAD/blog/static/favicon.ico
--------------------------------------------------------------------------------
/admin_app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shenghy/react_blog/HEAD/admin_app/public/favicon.ico
--------------------------------------------------------------------------------
/admin_app/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shenghy/react_blog/HEAD/admin_app/public/logo192.png
--------------------------------------------------------------------------------
/admin_app/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shenghy/react_blog/HEAD/admin_app/public/logo512.png
--------------------------------------------------------------------------------
/blog/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [{ "src": "next.config.js", "use": "@now/next" }]
4 | }
--------------------------------------------------------------------------------
/admin_app/src/static/css/Login.css:
--------------------------------------------------------------------------------
1 | .login-div{
2 | margin: 150px auto;
3 | width: 400px;
4 | }
5 | body{
6 | background-color: #f0f0f0;
7 | }
--------------------------------------------------------------------------------
/blog/pages/_app.js:
--------------------------------------------------------------------------------
1 | import App from 'next/app'
2 |
3 | import 'antd/dist/antd.css'
4 | import '../static/style/pages/comm.css'
5 |
6 | export default App
--------------------------------------------------------------------------------
/blog/static/style/components/footer.css:
--------------------------------------------------------------------------------
1 | .footer-div{
2 | text-align: center;
3 | width: 100%;
4 | padding: 1rem;
5 | color:#888;
6 | height: 100px;
7 | }
--------------------------------------------------------------------------------
/blog/static/style/components/advert.css:
--------------------------------------------------------------------------------
1 | .ad-div{
2 | margin-top: .5rem;
3 | }
4 | .ad-div div{
5 | border-radius: .3rem;
6 | margin-bottom: .2rem;
7 | overflow: hidden;
8 | }
--------------------------------------------------------------------------------
/admin_app/src/static/css/AdminIndex.css:
--------------------------------------------------------------------------------
1 | .logo {
2 | height: 32px;
3 | background: rgba(255, 255, 255, 0.2);
4 | margin: 16px;
5 | font-size: 19px;
6 | color: #ccc;
7 | text-align: center;
8 |
9 | }
--------------------------------------------------------------------------------
/blog/next.config.js:
--------------------------------------------------------------------------------
1 | const withCss = require('@zeit/next-css')
2 |
3 | if(typeof require !== 'undefined'){
4 | require.extensions['.css']=file=>{}
5 | }
6 |
7 | module.exports = withCss({
8 | target: 'serverless'
9 | })
--------------------------------------------------------------------------------
/admin_app/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Main from './pages/Main'
4 |
5 |
6 |
7 |
8 | ReactDOM.render(, document.getElementById('root'));
9 |
10 |
11 |
--------------------------------------------------------------------------------
/service/.gitignore:
--------------------------------------------------------------------------------
1 | logs/
2 | npm-debug.log
3 | yarn-error.log
4 | node_modules/
5 | package-lock.json
6 | yarn.lock
7 | coverage/
8 | .idea/
9 | run/
10 | .DS_Store
11 | *.sw*
12 | *.un~
13 | typings/
14 | .nyc_output/
15 |
--------------------------------------------------------------------------------
/service/app/router.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /**
3 | * @param {Egg.Application} app - egg application
4 | */
5 | module.exports = app => {
6 |
7 | require('./router/default')(app)
8 | require('./router/admin')(app)
9 | };
10 |
--------------------------------------------------------------------------------
/service/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - '10'
5 | before_install:
6 | - npm i npminstall -g
7 | install:
8 | - npminstall
9 | script:
10 | - npm run ci
11 | after_script:
12 | - npminstall codecov && codecov
13 |
--------------------------------------------------------------------------------
/blog/components/Footer.js:
--------------------------------------------------------------------------------
1 | import '../static/style/components/footer.css'
2 | const Footer = ()=>(
3 |
4 |
系统由 React+Node+Ant Desgin驱动
5 |
JSPang.com
6 |
7 | )
8 |
9 | export default Footer
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /service/node_modules
2 | /service/logs
3 | /service/run
4 | /service/package-lock.json
5 | /service/yarn.lock
6 | /blog/node_modules
7 | /blog/out
8 | /blog/.next
9 | /blog/package-lock.json
10 | /blog/yarn.lock
11 | /admin_app/node_modules
12 | /admin_app/yarn.lock
13 |
--------------------------------------------------------------------------------
/blog/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets":["next/babel"], //Next.js的总配置文件,相当于继承了它本身的所有配置
3 | "plugins":[ //增加新的插件,这个插件就是让antd可以按需引入,包括CSS
4 | [
5 | "import",
6 | {
7 | "libraryName":"antd"
8 | }
9 | ]
10 | ]
11 | }
--------------------------------------------------------------------------------
/service/app/middleware/adminauth.js:
--------------------------------------------------------------------------------
1 | module.exports = options =>{
2 | return async function adminauth(ctx,next){
3 | console.log(ctx.session.openId)
4 | if(ctx.session.openId){
5 | await next()
6 | }else{
7 | ctx.body={data:'没有登录'}
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/service/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | matrix:
3 | - nodejs_version: '10'
4 |
5 | install:
6 | - ps: Install-Product node $env:nodejs_version
7 | - npm i npminstall && node_modules\.bin\npminstall
8 |
9 | test_script:
10 | - node --version
11 | - npm --version
12 | - npm run test
13 |
14 | build: off
15 |
--------------------------------------------------------------------------------
/blog/static/style/pages/bibidao.css:
--------------------------------------------------------------------------------
1 | .bbd-img{
2 | border-radius: 5px;
3 | display:inline-block;
4 | }
5 | .bbd-title{
6 | margin-top:10px;
7 | }
8 | .bbd-zi{
9 | color: #888;
10 | }
11 | .item-div{
12 | width: 100%;
13 | overflow: hidden;
14 | }
15 | .img-wrapper{
16 | display: block;
17 |
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/blog/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | .env*
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
--------------------------------------------------------------------------------
/blog/components/Heading.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const elements = {
4 | h1: "h1",
5 | h2: "h2",
6 | h3: "h3",
7 | h4: "h4",
8 | h5: "h5",
9 | h6: "h6"
10 | };
11 |
12 | function Heading({ level, children, ...props }) {
13 | return React.createElement(elements[level] || elements.h1, props, children);
14 | }
15 |
16 | Heading.defaultProps = {
17 | type: "h1"
18 | };
19 |
20 | export default Heading;
--------------------------------------------------------------------------------
/admin_app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 | # ffddf
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/service/config/plugin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /** @type Egg.EggPlugin */
4 | // module.exports = {
5 | // // had enabled by egg
6 | // // static: {
7 | // // enable: true,
8 | // // }
9 | // };
10 |
11 | //配置插件
12 | exports.mysql = {
13 | enable: true,
14 | package: 'egg-mysql'
15 | }
16 |
17 | exports.cors = {
18 | enable: true,
19 | package: 'egg-cors'
20 | }
21 |
22 | exports.origin = {
23 | enable: true,
24 | package: 'egg-origin',
25 | }
--------------------------------------------------------------------------------
/blog/components/Advert.js:
--------------------------------------------------------------------------------
1 | import '../static/style/components/advert.css'
2 |
3 | const Advert = ()=>{
4 | return (
5 |
14 | )
15 | }
16 |
17 | export default Advert
--------------------------------------------------------------------------------
/service/.autod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | write: true,
5 | prefix: '^',
6 | plugin: 'autod-egg',
7 | test: [
8 | 'test',
9 | 'benchmark',
10 | ],
11 | dep: [
12 | 'egg',
13 | 'egg-scripts',
14 | ],
15 | devdep: [
16 | 'egg-ci',
17 | 'egg-bin',
18 | 'egg-mock',
19 | 'autod',
20 | 'autod-egg',
21 | 'eslint',
22 | 'eslint-config-egg',
23 | ],
24 | exclude: [
25 | './test/fixtures',
26 | './dist',
27 | ],
28 | };
29 |
30 |
--------------------------------------------------------------------------------
/blog/static/style/components/studyLine.css:
--------------------------------------------------------------------------------
1 | .sl-row{
2 | padding:5px 0px;
3 | border-bottom:1px solid #eee;
4 | }
5 | .sl-title{
6 | padding-left: 5px;
7 | font-size: 1rem;
8 | color:#888 !important;
9 | }
10 | .sl-title1{
11 | padding-top: 5px;;
12 | font-size: 0.8rem;
13 | color: #ccc;
14 | }
15 | .sl-icon{
16 | padding-top: 5px;
17 | }
18 | .ls-main-title{
19 | font-size: 1rem;
20 | text-align: center;
21 | color: #888;
22 | padding: 6px;
23 | border-bottom: 1px solid #eee;
24 |
25 | }
--------------------------------------------------------------------------------
/admin_app/src/pages/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { BrowserRouter as Router, Route} from "react-router-dom";
3 |
4 | import Login from './Login'
5 | import AdminIndex from './AdminIndex'
6 | function Main(){
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | )
17 | }
18 | export default Main
19 |
20 |
21 |
--------------------------------------------------------------------------------
/blog/static/style/components/rightmi.css:
--------------------------------------------------------------------------------
1 | .rightmi-div{
2 | margin-top: 8px !important;
3 | margin-bottom: 8px !important;
4 | }
5 | .miquan-img img{
6 | width: 100%;
7 | border-radius: 3px;
8 | }
9 | .miquan-price{
10 | text-align: center;
11 | margin: 5px;
12 | color: #1890ff;
13 | font-size: 1.0rem;
14 | }
15 | .miquan-text{
16 | padding: 5px;
17 | line-height: 14px;
18 | color:#999;
19 |
20 | }
21 | .miquan-text span{
22 | color:#999;
23 | }
24 | .quan-button{
25 | text-align: center;
26 | margin-bottom: 10px;
27 | }
--------------------------------------------------------------------------------
/service/test/app/controller/home.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { app, assert } = require('egg-mock/bootstrap');
4 |
5 | describe('test/app/controller/home.test.js', () => {
6 | it('should assert', () => {
7 | const pkg = require('../../../package.json');
8 | assert(app.config.keys.startsWith(pkg.name));
9 |
10 | // const ctx = app.mockContext({});
11 | // yield ctx.service.xx();
12 | });
13 |
14 | it('should GET /', () => {
15 | return app.httpRequest()
16 | .get('/')
17 | .expect('hi, egg')
18 | .expect(200);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/blog/config/apiUrl.js:
--------------------------------------------------------------------------------
1 | let ipUrl = 'http://127.0.0.1:7001/default/'
2 | //let ipUrl = 'http://192.168.0.105:7001/default/'
3 |
4 | let servicePath = {
5 | getArticleList:ipUrl + 'getArticleList' , // 首页文章列表接口
6 | getArticleById:ipUrl + 'getArticleById/', // 文章详细页内容接口 ,需要接收参数
7 | getTypeInfo:ipUrl + 'getTypeInfo', // 文章分类信息
8 | getListById:ipUrl + 'getListById/', // 根据类别ID获得文章列表
9 | getAllPartCount:ipUrl + 'getAllPartCount', // 获得所有集数和访问数
10 | getListBBD:ipUrl + 'getListBBD', // 大胖逼逼叨的列表
11 |
12 | }
13 |
14 | export default servicePath;
--------------------------------------------------------------------------------
/blog/static/style/components/author.css:
--------------------------------------------------------------------------------
1 | .author-div{
2 | text-align: center;
3 | padding: 1rem !important;
4 |
5 | }
6 | .author-div div{
7 | margin-bottom: 1rem;
8 |
9 | }
10 | .author-introduction{
11 | font-size:.8rem;
12 | color: #999;
13 |
14 | }
15 | .account{
16 | background-color: #999;
17 | margin-left: .5rem !important;
18 | margin-right: .5rem !important;
19 | }
20 | .author-name{
21 | font-size: 1rem;
22 | color: #1890ff;
23 | }
24 | .author-tag{
25 | line-height: 27px;
26 | }
27 |
28 | .ant-avatar-image{
29 | background: #ccc !important;
30 | }
--------------------------------------------------------------------------------
/admin_app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "react"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/admin_app/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 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/service/README.md:
--------------------------------------------------------------------------------
1 | # service
2 |
3 |
4 |
5 | ## QuickStart
6 |
7 |
8 |
9 | see [egg docs][egg] for more detail.
10 |
11 | ### Development
12 |
13 | ```bash
14 | $ npm i
15 | $ npm run dev
16 | $ open http://localhost:7001/
17 | ```
18 |
19 | ### Deploy
20 |
21 | ```bash
22 | $ npm start
23 | $ npm stop
24 | ```
25 |
26 | ### npm scripts
27 |
28 | - Use `npm run lint` to check code style.
29 | - Use `npm test` to run unit test.
30 | - Use `npm run autod` to auto detect dependencies upgrade, see [autod](https://www.npmjs.com/package/autod) for more detail.
31 |
32 |
33 | [egg]: https://eggjs.org
--------------------------------------------------------------------------------
/service/app/router/default.js:
--------------------------------------------------------------------------------
1 | module.exports = app =>{
2 | const {router,controller} = app
3 | router.get('/default/index',controller.default.home.index)
4 | router.get('/default/getArticleList',controller.default.home.getArticleList)
5 | router.get('/default/getArticleById/:id',controller.default.home.getArticleById)
6 | router.get('/default/getTypeInfo',controller.default.home.getTypeInfo)
7 | router.get('/default/getListById/:id',controller.default.home.getListById)
8 | router.get('/default/getAllPartCount',controller.default.home.getAllPartCount)
9 | router.get('/default/getListBBD',controller.default.home.getListBBD)
10 | }
--------------------------------------------------------------------------------
/blog/static/style/components/header.css:
--------------------------------------------------------------------------------
1 | .header{
2 | background-color: #fff;
3 | padding: .4rem;
4 | overflow: hidden;
5 | height: 3.2rem;
6 | border-bottom:1px solid #eee;
7 |
8 | }
9 | .header-center{
10 | max-width: 1100px;
11 | margin: 0 auto;
12 | }
13 | .header-logo{
14 | color:#1e90ff;
15 | font-size: 1.4rem;
16 | text-align: left;
17 | }
18 | .header-txt{
19 | font-size: 0.6rem;
20 | color: #999;
21 | display: inline-block;
22 | padding-left: 0.3rem;
23 | }
24 |
25 | .memu-div{
26 | padding-top: 10px;
27 | }
28 | .memu-div a{
29 | color:#999 !important;
30 | font-size: .9rem;
31 | }
32 |
--------------------------------------------------------------------------------
/blog/static/test.md:
--------------------------------------------------------------------------------
1 | ## API
2 |
3 | ### PreviewLayout
4 |
5 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
6 | | :------- | :------------------------- | :--: | :----: | :---: |
7 | | children | 传递的组件,可以是任意组件 | jsx | null | 0.1.0 |
8 |
9 | ### MdPreviewer
10 |
11 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
12 | | :--- | :------------ | :----: | :----: | :---: |
13 | | md | markdown 文档 | string | null | 0.1.0 |
14 |
15 | ### CodePreviewer
16 |
17 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
18 | | :------- | :------------- | :----: | :----: | :---: |
19 | | code | 要显示的代码 | string | null | 0.0.1 |
20 | | showCode | 是否要展示代码 | bool | true | 0.1.0 |
--------------------------------------------------------------------------------
/blog/static/style/pages/list.css:
--------------------------------------------------------------------------------
1 | .bread-div{
2 | padding: .5rem;
3 | border-bottom:1px solid #eee;
4 | background-color: #e1f0ff;
5 | }
6 |
7 | pre{
8 | display: block;
9 | background-color: #283646 !important;
10 | padding: .5rem !important;
11 | overflow-y: auto;
12 | font-weight: 300;
13 | font-family: Menlo, monospace;
14 | border-radius: .3rem;
15 | }
16 |
17 | pre >code{
18 | border:0px !important;
19 | background-color: #283646 !important;
20 | color:#FFF;
21 |
22 | }
23 | code {
24 | display: inline-block ;
25 | background-color:#fff5f5;
26 | border-radius:3px;
27 | padding-left: 5px;
28 | padding-right: 5px;
29 | color:#ff502c;
30 | margin: 0px 3px;
31 | line-height: 1.1rem;
32 | font-size: .87rem;
33 |
34 | }
--------------------------------------------------------------------------------
/admin_app/src/config/apiUrl.js:
--------------------------------------------------------------------------------
1 | let ipUrl = 'http://127.0.0.1:7001/admin/'
2 |
3 | let servicePath = {
4 | getTypeInfo:ipUrl + 'getTypeInfo' , // 获得文章类别信息
5 | addArticle:ipUrl + 'addArticle' , // 添加文章
6 | updateArticle:ipUrl + 'updateArticle' , // 添加文章
7 | getArticleList:ipUrl + 'getArticleList' , // 文章列表
8 | delArticle:ipUrl + 'delArticle/' , // 删除文章
9 | getArticleById:ipUrl + 'getArticleById/' , // 根据ID获得文章详情
10 | checkLogin:ipUrl + 'checkLogin' , // 检查用户名密码是否正确
11 | checkOpenId:ipUrl + 'checkOpenId' , // 检查OPendId是否和服务器一样
12 | outLogin:ipUrl + 'outLogin' , // 退出登录
13 | addBBD:ipUrl + 'addBBD' , // 增加大胖逼逼叨视频
14 | getListBBD:ipUrl + 'getListBBD' , // 大胖逼逼叨列表
15 | delBBDbyId:ipUrl + 'delBBDbyId/' , // 删除大胖逼逼叨单一项
16 | updateIsTop:ipUrl + 'updateIsTop' , // 修改文章是否置顶
17 | }
18 |
19 | export default servicePath;
--------------------------------------------------------------------------------
/blog/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "blog",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "set NODE_ENV=production next start -p 3000 ",
9 | "export": "npm run build && next export"
10 | },
11 | "dependencies": {
12 | "@zeit/next-css": "^1.0.1",
13 | "antd": "^3.23.3",
14 | "axios": "^0.19.0",
15 | "babel-plugin-import": "^1.12.1",
16 | "highlight.js": "^9.15.10",
17 | "loadash": "^1.0.0",
18 | "markdown-navbar": "^1.0.7",
19 | "marked": "^0.7.0",
20 | "next": "9.0.6",
21 | "react": "16.9.0",
22 | "react-countup": "^4.2.2",
23 | "react-dom": "16.9.0",
24 | "react-highlight": "^0.12.0",
25 | "react-markdown": "^4.2.2",
26 | "react-syntax-highlighter": "^11.0.2"
27 | },
28 | "devDependencies": {
29 | "raw-loader": "^3.1.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/blog/components/HeadingBlock.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react";
2 | import PropTypes from "prop-types";
3 | import Heading from "./Heading";
4 |
5 | class HeadingBlock extends PureComponent {
6 | renderHtml = () => {
7 | const { level, children } = this.props;
8 |
9 | if (children && children.length > 0) {
10 | const nodeValue = children[0].props.value;
11 | return (
12 | <>
13 |
14 |
15 | #
16 |
17 | {children}
18 |
19 |
20 |
21 |
22 | >
23 | );
24 | } else {
25 | return <>{children}>;
26 | }
27 | };
28 |
29 |
30 |
31 | render() {
32 | return (<>
33 | {this.renderHtml()}
34 |
35 | >);
36 | }
37 | }
38 |
39 | export default HeadingBlock;
--------------------------------------------------------------------------------
/admin_app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "admin",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@types/lodash": "^4.14.149",
7 | "antd": "^3.25.0",
8 | "axios": "^0.19.0",
9 | "highlight.js": "^9.16.2",
10 | "loadash": "^1.0.0",
11 | "marked": "^0.7.0",
12 | "react": "^16.11.0",
13 | "react-dom": "^16.11.0",
14 | "react-router-dom": "^5.1.2",
15 | "react-scripts": "3.2.0",
16 | "typescript": "^3.7.3"
17 | },
18 | "scripts": {
19 | "start": "react-scripts start",
20 | "build": "react-scripts build",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": "react-app"
26 | },
27 | "browserslist": {
28 | "production": [
29 | ">0.2%",
30 | "not dead",
31 | "not op_mini all"
32 | ],
33 | "development": [
34 | "last 1 chrome version",
35 | "last 1 firefox version",
36 | "last 1 safari version"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/blog/components/CodeBlock.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from "react";
2 | import PropTypes from "prop-types";
3 | import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
4 | // 设置高亮样式
5 | import { coy } from "react-syntax-highlighter/dist/esm/styles/prism";
6 | // 设置高亮的语言
7 | import { jsx, javascript, sass, scss ,css} from "react-syntax-highlighter/dist/esm/languages/prism";
8 |
9 | class CodeBlock extends PureComponent {
10 | static propTypes = {
11 | value: PropTypes.string.isRequired,
12 | language: PropTypes.string
13 | };
14 |
15 | static defaultProps = {
16 | language: null
17 | };
18 |
19 | componentWillMount() {
20 | // 注册要高亮的语法,
21 | // 注意:如果不设置打包后供第三方使用是不起作用的
22 | SyntaxHighlighter.registerLanguage("jsx", jsx);
23 | SyntaxHighlighter.registerLanguage("javascript", javascript);
24 | SyntaxHighlighter.registerLanguage("css", css);
25 | }
26 |
27 | render() {
28 | // const { language, value } = this.props;
29 | return (
30 |
31 | d
32 | {value}
33 |
34 |
35 | );
36 | }
37 | }
38 |
39 | export default CodeBlock;
--------------------------------------------------------------------------------
/blog/static/style/pages/index.css:
--------------------------------------------------------------------------------
1 | pre{
2 | display: block;
3 | background-color:#f3f3f3;
4 | padding: .5rem !important;
5 | overflow-y: auto;
6 | font-weight: 300;
7 | font-family: Menlo, monospace;
8 | border-radius: .3rem;
9 | }
10 | pre{
11 | background-color: #283646 !important;
12 | }
13 | pre >code{
14 | border:0px !important;
15 | background-color: #283646 !important;
16 | color:#FFF;
17 |
18 | }
19 | code {
20 | display: inline-block ;
21 | background-color:#fff5f5;
22 | border-radius:3px;
23 | padding-left: 5px;
24 | padding-right: 5px;
25 | color:#ff502c;
26 | margin: 0px 3px;
27 | line-height: 1.1rem;
28 | font-size: .87rem;
29 |
30 | }
31 | .list-context{
32 | font-size: 1.0rem;
33 | color: #999;
34 | line-height: 1.6rem;
35 | }
36 |
37 | .list-context img{
38 | width:100% ;
39 | border-radius:5px;
40 | border:1px solid #ccc;
41 | max-width:1000px !important;
42 | display: block;
43 | margin:8px auto ;
44 |
45 |
46 | }
47 | .bibidao-title{
48 | display: flex;
49 | flex-direction: row;
50 | }
51 | .bibidao-title > div{
52 | width: 50%;
53 | }
54 | .right{
55 | text-align: right;
56 | padding-right:10px ;
57 | font-size: 0.9rem;
58 | }
--------------------------------------------------------------------------------
/service/app/router/admin.js:
--------------------------------------------------------------------------------
1 | module.exports = app =>{
2 | const {router,controller} = app
3 | var adminauth = app.middleware.adminauth()
4 | router.get('/admin/index',controller.admin.main.index)
5 | router.get('/admin/getTypeInfo',adminauth ,controller.admin.main.getTypeInfo)
6 | router.post('/admin/addArticle',adminauth,controller.admin.main.addArticle)
7 | router.post('/admin/updateArticle',adminauth,controller.admin.main.updateArticle)
8 | router.get('/admin/getArticleList',adminauth,controller.admin.main.getArticleList)
9 | router.get('/admin/delArticle/:id',adminauth,controller.admin.main.delArticle)
10 | router.get('/admin/getArticleById/:id',adminauth,controller.admin.main.getArticleById)
11 | router.post('/admin/checkLogin',controller.admin.main.checkLogin)
12 | router.post('/admin/checkOpenId',controller.admin.main.checkOpenId)
13 | router.get('/admin/outLogin',adminauth,controller.admin.main.outLogin)
14 | router.post('/admin/addBBD',adminauth,controller.admin.main.addBBD)
15 | router.get('/admin/getListBBD',adminauth,controller.admin.main.getListBBD)
16 | router.get('/admin/delBBDbyId/:id',adminauth,controller.admin.main.delBBDbyId)
17 | router.post('/admin/updateIsTop',adminauth,controller.admin.main.updateIsTop)
18 | }
--------------------------------------------------------------------------------
/service/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "service",
3 | "version": "1.0.0",
4 | "description": "",
5 | "private": true,
6 | "egg": {
7 | "declarations": true
8 | },
9 | "dependencies": {
10 | "egg": "^2.15.1",
11 | "egg-cors": "^2.2.0",
12 | "egg-mysql": "^3.0.0",
13 | "egg-origin": "^1.0.3",
14 | "egg-scripts": "^2.11.0"
15 | },
16 | "devDependencies": {
17 | "autod": "^3.0.1",
18 | "autod-egg": "^1.1.0",
19 | "egg-bin": "^4.11.0",
20 | "egg-ci": "^1.11.0",
21 | "egg-mock": "^3.21.0",
22 | "eslint": "^5.13.0",
23 | "eslint-config-egg": "^7.1.0"
24 | },
25 | "engines": {
26 | "node": ">=10.0.0"
27 | },
28 | "scripts": {
29 | "start": "egg-scripts start --daemon --title=egg-server-service",
30 | "stop": "egg-scripts stop --title=egg-server-service",
31 | "dev": "egg-bin dev",
32 | "debug": "egg-bin debug",
33 | "test": "npm run lint -- --fix && npm run test-local",
34 | "test-local": "egg-bin test",
35 | "cov": "egg-bin cov",
36 | "lint": "eslint .",
37 | "ci": "npm run lint && npm run cov",
38 | "autod": "autod"
39 | },
40 | "ci": {
41 | "version": "10"
42 | },
43 | "repository": {
44 | "type": "git",
45 | "url": ""
46 | },
47 | "author": "",
48 | "license": "MIT"
49 | }
50 |
--------------------------------------------------------------------------------
/blog/static/style/pages/comm.css:
--------------------------------------------------------------------------------
1 | body{
2 | background-color: #f4f5f5;
3 | font-family: -apple-system,system-ui,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Arial,sans-serif;
4 |
5 | height: auto;
6 | }
7 |
8 | .comm-left{
9 | background-color: #FFF;
10 | padding:.3rem;
11 | border-radius: .3rem;
12 | border:1px solid #eee;
13 | margin-bottom: 16px;
14 | }
15 | .comm-box{
16 | background-color: #FFF;
17 | margin-left: .5rem;
18 | padding:.3rem;
19 | border-radius: .3rem;
20 | border:1px solid #eee;
21 |
22 |
23 | }
24 | .comm-main{
25 | margin-top: .5rem !important;
26 | max-width: 1100px;
27 | margin:0 auto;
28 |
29 | }
30 |
31 | .list-title{
32 | font-size:1.4rem;
33 | color: #1e90ff;
34 | padding: 0 0.5rem;
35 | margin-top: 1.3rem;
36 | }
37 | .list-context{
38 | color:#777;
39 | padding:.5rem;
40 | }
41 | .list-icon{
42 | padding:.5rem 0;
43 | color:#ccc;
44 | }
45 | .list-icon span{
46 | display: inline-block;
47 | padding: 0 10px;
48 | }
49 | .list-header{
50 | font-size: 1.1rem;
51 | padding-left:1rem;
52 | }
53 | .list-go{
54 | text-align: right;
55 | margin-right: 20px;
56 | color:#bbb;
57 | }
58 | .bbd-img{
59 | border-radius: 4px;
60 | width: 100%;
61 | }
62 | .bbd-title{
63 | margin-top:10px;
64 | overflow: hidden;
65 | text-overflow:ellipsis;
66 | white-space: nowrap;
67 | }
68 | .bbd-zi{
69 | color: #888;
70 | }
71 | .ant-card-body{
72 | padding:10px;
73 | }
74 |
--------------------------------------------------------------------------------
/blog/components/StudyLine.js:
--------------------------------------------------------------------------------
1 | import '../static/style/components/studyLine.css'
2 | import {Row,Col,Icon} from 'antd'
3 |
4 | const StudyLine = ()=>{
5 | return (
6 |
33 | )
34 | }
35 |
36 | export default StudyLine
--------------------------------------------------------------------------------
/admin_app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/service/config/config.default.js:
--------------------------------------------------------------------------------
1 | /* eslint valid-jsdoc: "off" */
2 |
3 | 'use strict';
4 |
5 | /**
6 | * @param {Egg.EggAppInfo} appInfo app info
7 | */
8 | module.exports = appInfo => {
9 | /**
10 | * built-in config
11 | * @type {Egg.EggAppConfig}
12 | **/
13 | const config = exports = {};
14 |
15 | // use for cookie sign key, should change to your own and keep security
16 | config.keys = appInfo.name + '_1570612395695_7299';
17 |
18 | // add your middleware config here
19 | config.middleware = [];
20 |
21 | // add your user config here
22 | const userConfig = {
23 | // myAppName: 'egg',
24 | };
25 | config.mysql = {
26 | // database configuration
27 | client: {
28 | // host
29 | host: 'localhost',
30 | // port
31 | port: '3306',
32 | // username
33 | user: 'root',
34 | // password
35 | password: '12345678',
36 | // database
37 | database: 'react_blog',
38 | },
39 | // load into app, default is open
40 | app: true,
41 | // load into agent, default is close
42 | agent: false,
43 | };
44 |
45 | config.security = {
46 | csrf: {enable: false},
47 | domainWhiteList: [ '*' ]
48 | };
49 |
50 | config.cors = {
51 | origin: 'http://127.0.0.1:3000',
52 | credentials: true, //允许Cook可以跨域
53 | allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS'
54 | };
55 |
56 | //长文本设置
57 | config.bodyParser = {
58 | enable: true,
59 | encoding: 'utf8',
60 | formLimit: '5024kb',
61 | jsonLimit: '5024kb',
62 | strict: true,
63 | // @see https://github.com/hapijs/qs/blob/master/lib/parse.js#L8 for more options
64 | queryString: {
65 | arrayLimit: 100,
66 | depth: 5,
67 | parameterLimit: 1000,
68 | },
69 |
70 | }
71 |
72 | return {
73 | ...config,
74 | ...userConfig,
75 | };
76 | };
77 |
78 |
79 |
--------------------------------------------------------------------------------
/blog/components/tocify.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Anchor } from 'antd';
3 | import { last } from 'lodash';
4 |
5 | const { Link } = Anchor;
6 |
7 | export interface TocItem {
8 | anchor: string;
9 | level: number;
10 | text: string;
11 | children?: TocItem[];
12 | }
13 |
14 | export type TocItems = TocItem[]; // TOC目录树结构
15 |
16 | export default class Tocify {
17 | tocItems: TocItems = [];
18 |
19 | index: number = 0;
20 |
21 | constructor() {
22 | this.tocItems = [];
23 | this.index = 0;
24 | }
25 |
26 | add(text: string, level: number) {
27 | const anchor = `toc${level}${++this.index}`;
28 | const item = { anchor, level, text };
29 | const items = this.tocItems;
30 |
31 | if (items.length === 0) { // 第一个 item 直接 push
32 | items.push(item);
33 | } else {
34 | let lastItem = last(items) as TocItem; // 最后一个 item
35 |
36 | if (item.level > lastItem.level) { // item 是 lastItem 的 children
37 | for (let i = lastItem.level + 1; i <= 2; i++) {
38 | const { children } = lastItem;
39 | if (!children) { // 如果 children 不存在
40 | lastItem.children = [item];
41 | break;
42 | }
43 |
44 | lastItem = last(children) as TocItem; // 重置 lastItem 为 children 的最后一个 item
45 |
46 | if (item.level <= lastItem.level) { // item level 小于或等于 lastItem level 都视为与 children 同级
47 | children.push(item);
48 | break;
49 | }
50 | }
51 | } else { // 置于最顶级
52 | items.push(item);
53 | }
54 | }
55 |
56 | return anchor;
57 | }
58 |
59 | reset = () => {
60 | this.tocItems = [];
61 | this.index = 0;
62 | };
63 |
64 | renderToc(items: TocItem[]) { // 递归 render
65 | return items.map(item => (
66 |
67 | {item.children && this.renderToc(item.children)}
68 |
69 | ));
70 | }
71 |
72 | render() {
73 | return (
74 |
75 | {this.renderToc(this.tocItems)}
76 |
77 | );
78 | }
79 | }
--------------------------------------------------------------------------------
/admin_app/src/components/tocify.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Anchor } from 'antd';
3 | import { last } from 'lodash';
4 |
5 | const { Link } = Anchor;
6 |
7 | export interface TocItem {
8 | anchor: string;
9 | level: number;
10 | text: string;
11 | children?: TocItem[];
12 | }
13 |
14 | export type TocItems = TocItem[]; // TOC目录树结构
15 |
16 | export default class Tocify {
17 | tocItems: TocItems = [];
18 |
19 | index: number = 0;
20 |
21 | constructor() {
22 | this.tocItems = [];
23 | this.index = 0;
24 | }
25 |
26 | add(text: string, level: number) {
27 | const anchor = `toc${level}${++this.index}`;
28 | const item = { anchor, level, text };
29 | const items = this.tocItems;
30 |
31 | if (items.length === 0) { // 第一个 item 直接 push
32 | items.push(item);
33 | } else {
34 | let lastItem = last(items) as TocItem; // 最后一个 item
35 |
36 | if (item.level > lastItem.level) { // item 是 lastItem 的 children
37 | for (let i = lastItem.level + 1; i <= 2; i++) {
38 | const { children } = lastItem;
39 | if (!children) { // 如果 children 不存在
40 | lastItem.children = [item];
41 | break;
42 | }
43 |
44 | lastItem = last(children) as TocItem; // 重置 lastItem 为 children 的最后一个 item
45 |
46 | if (item.level <= lastItem.level) { // item level 小于或等于 lastItem level 都视为与 children 同级
47 | children.push(item);
48 | break;
49 | }
50 | }
51 | } else { // 置于最顶级
52 | items.push(item);
53 | }
54 | }
55 |
56 | return anchor;
57 | }
58 |
59 | reset = () => {
60 | this.tocItems = [];
61 | this.index = 0;
62 | };
63 |
64 | renderToc(items: TocItem[]) { // 递归 render
65 | return items.map(item => (
66 |
67 | {item.children && this.renderToc(item.children)}
68 |
69 | ));
70 | }
71 |
72 | render() {
73 | return (
74 |
75 | {this.renderToc(this.tocItems)}
76 |
77 | );
78 | }
79 | }
--------------------------------------------------------------------------------
/blog/components/LearningRoute.js:
--------------------------------------------------------------------------------
1 | import React,{useState ,useEffect} from 'react'
2 | import {Avatar,Divider,Tooltip ,Tag} from 'antd'
3 | import '../static/style/components/author.css'
4 | import servicePath from '../config/apiUrl'
5 | import axios from 'axios'
6 | import CountUp from 'react-countup'
7 |
8 | const Author =()=>{
9 |
10 |
11 |
12 | const [ all_part_count , setAll_part_count ] = useState(0);
13 | const [ all_view_count , setAll_view_count ] = useState( 0);
14 |
15 | useEffect(()=>{
16 |
17 | fetchData()
18 |
19 | },[])
20 |
21 |
22 | const fetchData = async ()=>{
23 | const result = await axios(servicePath.getAllPartCount).then(
24 | (res)=>{ return res.data.data }
25 | )
26 | setAll_part_count(result[0].all_part_count)
27 | setAll_view_count(result[0].all_view_count)
28 | }
29 |
30 | return (
31 |
32 |
33 |
34 |
技术胖
35 |
专注于WEB和移动前端开发
36 |
37 | 光头Coder
38 | 12年经验
39 | 业余讲师
40 | 免费视频 集
41 | 被访问次
42 |
43 |
44 |
社交账号
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | )
58 |
59 | }
60 |
61 |
62 |
63 | export default Author
--------------------------------------------------------------------------------
/admin_app/src/static/css/AddArticle.css:
--------------------------------------------------------------------------------
1 | .markdown-content{
2 | font-size:16px !important;
3 | max-height: 745px;
4 | }
5 | .show-html{
6 | padding:10px;
7 | border:1px solid #ddd;
8 | border-radius: 5px;
9 | font-size:16px;
10 | height: 745px;
11 | background-color: #f0f0f0;
12 | overflow: auto;
13 | }
14 | .show-html img{
15 | width: 100%;
16 | }
17 |
18 | .show-html h1{
19 | font-size:30px;
20 | }
21 |
22 | .show-html h2{
23 | font-size:28px;
24 | border-bottom: 1px solid #cbcbcb;
25 | }
26 | .show-html h3{
27 | font-size:24px;
28 | }
29 |
30 | .show-html pre{
31 | display: block;
32 | background-color: #ccc;
33 | padding: 5px;
34 | border-radius: 5px;
35 | }
36 | .show-html pre>code{
37 | color: #000;
38 | background-color: #ccc;
39 | }
40 | .show-html code {
41 | background-color: #fff5f5;
42 | padding: 5px 10px;
43 | border-radius: 5px;
44 | margin: 0px 3px;
45 | color: #ff502c;
46 | }
47 | .show-html blockquote{
48 | border-left:4px solid #cbcbcb ;
49 | padding: 10px 10px 10px 30px;
50 | background-color: #f8f8f8;
51 | }
52 | .introduce-html{
53 | padding:10px;
54 | border:1px solid #ddd;
55 | border-radius: 5px;
56 | font-size:16px;
57 |
58 | background-color: #f0f0f0;
59 | }
60 |
61 |
62 | .introduce-html h1{
63 | font-size:30px;
64 | }
65 |
66 | .introduce-html h2{
67 | font-size:28px;
68 | border-bottom: 1px solid #cbcbcb;
69 | }
70 | .introduce-html h3{
71 | font-size:24px;
72 | }
73 |
74 | .introduce-html pre{
75 | display: block;
76 | background-color: #f0f0f0;
77 | padding: 5px;
78 | border-radius: 5px;
79 | }
80 | .introduce-html pre>code{
81 | color: #000;
82 | background-color: #ccc;
83 | }
84 | .introduce-html code {
85 | background-color: #fff5f5;
86 | padding: 5px 10px;
87 | border-radius: 5px;
88 | margin: 0px 3px;
89 | color: #ff502c;
90 | }
91 | .introduce-html img{
92 | width: 100%;
93 | }
94 | .introduce-html blockquote{
95 | border-left:4px solid #cbcbcb ;
96 | padding: 10px 10px 10px 30px;
97 | background-color: #f8f8f8;
98 | }
99 | .date-select{
100 | margin-top:10px;
101 | }
--------------------------------------------------------------------------------
/blog/components/Header.js:
--------------------------------------------------------------------------------
1 | import React ,{useState,useEffect} from 'react'
2 | import Router from 'next/router'
3 | import Link from 'next/link'
4 | import '../static/style/components/header.css'
5 | import {Row,Col, Menu, Icon} from 'antd'
6 | import axios from 'axios'
7 | import servicePath from '../config/apiUrl'
8 |
9 | const Header = () => {
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 技术胖
20 |
21 |
22 |
23 | 专注前端开发,每年100集免费视频。
24 |
25 |
26 |
27 |
28 |
29 |
30 | 博客首页
31 |
32 |
33 |
34 |
35 | 视频教程
36 |
37 |
38 |
39 |
40 | 逼逼叨
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | export default Header
53 |
--------------------------------------------------------------------------------
/blog/components/Author.js:
--------------------------------------------------------------------------------
1 | import React,{useState ,useEffect} from 'react'
2 | import {Avatar,Divider,Tooltip ,Tag} from 'antd'
3 | import '../static/style/components/author.css'
4 | import servicePath from '../config/apiUrl'
5 | import axios from 'axios'
6 | import CountUp from 'react-countup'
7 |
8 | const Author =()=>{
9 |
10 |
11 |
12 | const [ all_part_count , setAll_part_count ] = useState(0);
13 | const [ all_view_count , setAll_view_count ] = useState( 0);
14 |
15 | useEffect(()=>{
16 |
17 | fetchData()
18 |
19 | },[])
20 |
21 |
22 | const fetchData = async ()=>{
23 | const result = await axios(servicePath.getAllPartCount).then(
24 | (res)=>{ return res.data.data }
25 | )
26 | setAll_part_count(result[0].all_part_count)
27 | setAll_view_count(result[0].all_view_count)
28 | }
29 |
30 | return (
31 |
32 |
33 |
34 |
技术胖
35 |
专注于WEB和移动前端开发
36 |
37 | 光头Coder
38 | 12年经验
39 | 业余讲师
40 | 免费视频 集
41 | 被访问次
42 |
43 |
44 |
社交账号
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | )
65 |
66 | }
67 |
68 |
69 |
70 | export default Author
--------------------------------------------------------------------------------
/blog/static/style/pages/detailed.css:
--------------------------------------------------------------------------------
1 | .bread-div{
2 | padding: .6rem;
3 | border-bottom:1px solid #DDD;
4 | background-color: #FFF;
5 |
6 | }
7 | .detailed-title{
8 | font-size: 1.6rem;
9 | text-align: center;
10 | padding: 1rem;
11 | }
12 | .center{
13 | text-align: center;
14 | }
15 | .detailed-content{
16 | padding: 1.3rem;
17 | font-size: 1.05rem;
18 | color: #777;
19 | line-height: 1.9rem;
20 | }
21 | .detailed-content h2{
22 | border-bottom: 1px solid #d9dada;
23 | padding-bottom: 1rem;
24 | margin-bottom: 2rem;
25 | margin-top: 2rem;
26 | -webkit-margin-bottom-collapse: 2rem;
27 | color: #333 ;
28 | font-size: 1.3rem;
29 | font-weight: bold;
30 | }
31 | .detailed-content h3{
32 | border-left: 4px solid rgb(156, 203, 250);
33 | padding-left:1rem ;
34 | margin-bottom: 1rem;
35 | margin-top: 1rem;
36 | -webkit-margin-bottom-collapse: 2rem;
37 | color: #666 ;
38 | font-size: 1.3rem;
39 | font-weight: bold;
40 |
41 | }
42 | .detailed-content img{
43 | border-radius: 5px;
44 | border:1px solid #f0f0f0;
45 | }
46 | pre{
47 | display: block;
48 | background-color: #283646 !important;
49 | padding: .5rem !important;
50 | overflow-y: auto;
51 | font-weight: 300;
52 | font-family: Menlo, monospace;
53 | border-radius: .3rem;
54 | }
55 |
56 | pre > code{
57 | border:0px !important;
58 | background-color: #283646 !important;
59 | color:#ccc !important;
60 | font-size: .87rem;
61 |
62 | }
63 | code {
64 | display: inline-block ;
65 | background-color:#fff5f5;
66 | border-radius:3px;
67 | padding-left: 5px;
68 | padding-right: 5px;
69 | color:#ff502c;
70 | margin: 0px 3px;
71 | line-height: 1.1rem;
72 | font-size: .87rem;
73 |
74 | }
75 |
76 | .title-anchor{
77 | color:#888 !important;
78 | padding:4px !important;
79 | margin: 0rem !important;
80 | height: auto !important;
81 | line-height: 1.2rem !important;
82 | font-size: .7rem !important;
83 | border-bottom: 1px dashed #eee;
84 | overflow: hidden;
85 | text-overflow:ellipsis;
86 | white-space: nowrap;
87 | }
88 | .active{
89 | color:rgb(30, 144, 255) !important;
90 | }
91 | .nav-title{
92 | text-align: center;
93 | color: #888;
94 | border-bottom: 1px solid rgb(30, 144, 255);
95 | font-size: 1rem;
96 |
97 | }
98 | .article-menu{
99 | font-size:12px;
100 | }
101 | iframe{
102 | height: 34rem;
103 | border:1px solid #ccc;
104 | border-radius: 8px;
105 | }
106 | @media screen and (min-width: 320px)and (max-width: 750px){
107 | iframe{
108 | height: 12rem;
109 | }
110 | }
111 |
112 | .detailed-content img{
113 | width: 100%;
114 | border:1px solid #f3f3f3;
115 | }
116 | .title-level3{
117 | display: none !important;
118 | }
119 | .ant-anchor-link-title{
120 | font-size: 12px !important;
121 | }
122 | .ant-anchor-wrapper{
123 | padding: 5px !important;
124 | }
125 |
126 |
--------------------------------------------------------------------------------
/admin_app/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:1ddss
6 |
7 | ### `yarn 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.d
14 |
15 | ### `yarn 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 | ### `yarn 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 | ### `yarn 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 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `yarn build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/blog/pages/bibidao.js:
--------------------------------------------------------------------------------
1 | import React,{useState,useEffect} from 'react'
2 | import Head from 'next/head'
3 | import {Row, Col , List , Card,Breadcrumb } from 'antd'
4 | import Header from '../components/Header'
5 | import Author from '../components/Author'
6 | import Advert from '../components/Advert'
7 | import Footer from '../components/Footer'
8 | import '../static/style/pages/bibidao.css'
9 |
10 | import axios from 'axios'
11 | import servicePath from '../config/apiUrl'
12 |
13 |
14 | const Bibidao = (data) =>{
15 |
16 | const [mylist, setList ] = useState(data.list)
17 |
18 |
19 | useEffect(()=>{
20 |
21 | })
22 |
23 | return (
24 | <>
25 |
26 | 大胖逼逼叨
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 首页
36 | 大胖逼逼叨
37 |
38 |
39 |
40 |
41 |
(
52 |
53 |
57 |
62 |
67 |
68 |
69 |
70 |
71 | )}
72 | />
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | >
88 | )
89 |
90 | }
91 |
92 | Bibidao.getInitialProps = async (context)=>{
93 |
94 |
95 | const promise = new Promise((resolve)=>{
96 |
97 | axios(servicePath.getListBBD).then(
98 |
99 | (res)=>{
100 |
101 | return resolve(res.data)
102 | }
103 | )
104 |
105 | })
106 |
107 |
108 | return await promise
109 | }
110 |
111 | export default Bibidao
112 |
--------------------------------------------------------------------------------
/admin_app/src/pages/Login.js:
--------------------------------------------------------------------------------
1 | import React , {useState,useEffect,createContext} from 'react';
2 | import 'antd/dist/antd.css';
3 | import '../static/css/Login.css';
4 | import { Card, Input, Icon,Button ,Spin,message } from 'antd';
5 | import axios from 'axios'
6 | import servicePath from '../config/apiUrl'
7 |
8 | const openIdContext = createContext()
9 |
10 | function Login(props){
11 |
12 | const [userName , setUserName] = useState('')
13 | const [password , setPassword] = useState('')
14 | const [isLoading, setIsLoading] = useState(false)
15 |
16 | useEffect(()=>{
17 | //检查是否已经登录
18 | let openId = localStorage.getItem('openId')
19 | let dataProps = {'openId':openId}
20 | console.log(openId)
21 | if(openId){
22 | axios({
23 | method:'post',
24 | url:servicePath.checkOpenId,
25 | data:dataProps,
26 | withCredentials: true,
27 | header:{ 'Access-Control-Allow-Origin':'*' }
28 | }).then(
29 | res=>{
30 | console.log(res)
31 | if(res.data.data){
32 | message.success('已经登录')
33 | props.history.push('/index')
34 | }
35 | }
36 | )
37 |
38 | }
39 | },[])
40 |
41 | const checkLogin = ()=>{
42 | setIsLoading(true)
43 |
44 | if(!userName){
45 | message.error('用户名不能为空')
46 | return false
47 | }else if(!password){
48 | message.error('密码不能为空')
49 | return false
50 | }
51 | let dataProps = {
52 | 'userName':userName,
53 | 'password':password
54 | }
55 | axios({
56 | method:'post',
57 | url:servicePath.checkLogin,
58 | data:dataProps,
59 | header:{ 'Access-Control-Allow-Origin':'*' },
60 | withCredentials: true
61 | }).then(
62 | res=>{
63 | console.log(res.data)
64 | setIsLoading(false)
65 | if(res.data.data=='登录成功'){
66 | localStorage.setItem('openId',res.data.openId)
67 | props.history.push('/index')
68 | }else{
69 | message.error('用户名密码错误')
70 | }
71 |
72 |
73 | }
74 | )
75 |
76 | setTimeout(()=>{
77 | setIsLoading(false)
78 | },1000)
79 | }
80 |
81 | return (
82 |
83 |
84 |
85 |
86 | }
91 | onChange={(e)=>{setUserName(e.target.value)}}
92 | />
93 |
94 | }
99 | onChange={(e)=>{setPassword(e.target.value)}}
100 | />
101 |
102 |
103 |
104 |
105 |
106 | )
107 | }
108 |
109 |
110 |
111 | export default Login
112 |
113 |
114 |
--------------------------------------------------------------------------------
/blog/components/Rightmi.js:
--------------------------------------------------------------------------------
1 | import {Icon, Tabs,Alert,Button} from 'antd'
2 | import '../static/style/components/rightmi.css'
3 | const { TabPane } = Tabs;
4 |
5 | const Rightmi =(props)=>{
6 |
7 | const goArticle=()=>{
8 | window.location.href="https://jspang.com/detailed?id=54"
9 | }
10 |
11 | return (
12 |
13 |
14 |
15 |
16 |

17 |
18 |
19 | 只要50元/年 得4项福利
20 |
21 |
22 |
23 |
视频离线高清版下载-400集
24 |
每周至少两篇文章分享
25 |
技术胖收费视频半价购买
26 |
每天回答所提问题(选择性回答)
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |

38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | 加入QQ群 一起学习
47 |
48 |
76 |
77 |
78 |
79 |
80 |
81 | )
82 |
83 | }
84 |
85 | export default Rightmi
--------------------------------------------------------------------------------
/admin_app/src/pages/AdminIndex.js:
--------------------------------------------------------------------------------
1 | import React,{useState} from 'react';
2 | import { Layout, Menu, Breadcrumb, Icon ,message} from 'antd';
3 | import { Route } from "react-router-dom";
4 | import ArticleList from './ArticleList'
5 | import AddArticle from './AddArticle'
6 | import BBDList from './BBDList'
7 | import '../static/css/AdminIndex.css'
8 |
9 | import axios from 'axios'
10 | import servicePath from '../config/apiUrl'
11 |
12 | const { Header, Content, Footer, Sider } = Layout;
13 | const { SubMenu } = Menu;
14 |
15 |
16 | function AdminIndex(props){
17 |
18 | const [collapsed,setCollapsed] = useState(false)
19 |
20 | const onCollapse = collapsed => {
21 | setCollapsed(collapsed)
22 | };
23 |
24 | const handleClickArticle = e=>{
25 | console.log(e.item.props)
26 | if(e.key=='addArticle'){
27 | props.history.push('/index/add')
28 | }else{
29 | props.history.push('/index/list')
30 | }
31 |
32 | }
33 |
34 | // 退出登录的方法
35 | const handleExit= e=>{
36 |
37 | localStorage.removeItem('openId')
38 | axios({
39 | method:'get',
40 | url:servicePath.outLogin,
41 | header:{ 'Access-Control-Allow-Origin':'*' },
42 | withCredentials:true
43 | }).then(
44 | res=>{
45 | if(res.data.data=='退出成功')
46 | {
47 | message.success('已退出')
48 | setTimeout(()=>{
49 | props.history.push('/')
50 | },1000)
51 | }
52 | }
53 | )
54 |
55 | }
56 |
57 | // 跳转到大胖逼逼叨的页面
58 | const handleBBD = e=>{
59 | props.history.push('/index/bbd')
60 |
61 | }
62 |
63 |
64 | return (
65 |
66 |
67 | JSPang System
>
68 |
103 |
104 |
105 |
106 |
107 |
108 | 后台管理
109 | 工作台
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | )
132 |
133 | }
134 |
135 | export default AdminIndex
--------------------------------------------------------------------------------
/blog/pages/list.js:
--------------------------------------------------------------------------------
1 | import React,{useState,useEffect} from 'react'
2 | import Head from 'next/head'
3 | import {Row, Col , List ,Icon ,Breadcrumb ,Spin } from 'antd'
4 | import Header from '../components/Header'
5 | import Author from '../components/Author'
6 | import Advert from '../components/Advert'
7 | import Footer from '../components/Footer'
8 | import '../static/style/pages/list.css'
9 |
10 | import axios from 'axios'
11 | import servicePath from '../config/apiUrl'
12 | import Link from 'next/link'
13 |
14 | import marked from 'marked'
15 | import hljs from "highlight.js";
16 | import 'highlight.js/styles/monokai-sublime.css';
17 |
18 | const ArticleList = (list) =>{
19 |
20 | const [ mylist , setMylist ] = useState(list.data);
21 | const [ loading,setLoading] =useState(false)
22 |
23 | const goLoading= ()=>{
24 |
25 | setLoading(true)
26 | }
27 |
28 |
29 | const renderer = new marked.Renderer();
30 | marked.setOptions({
31 | renderer: renderer,
32 | gfm: true,
33 | pedantic: false,
34 | sanitize: false,
35 | tables: true,
36 | breaks: false,
37 | smartLists: true,
38 | smartypants: false,
39 | sanitize:false,
40 | xhtml: false,
41 | highlight: function (code) {
42 | return hljs.highlightAuto(code).value;
43 | }
44 |
45 | });
46 |
47 | useEffect(()=>{
48 | setMylist(list.data)
49 | })
50 |
51 | return (
52 | <>
53 |
54 | 列表| 技术胖-胜洪宇关注web前端技术-前端免费视频第一博客
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 首页
64 | 视频列表
65 |
66 |
67 |
68 |
(
72 |
73 |
74 |
79 |
80 | {item.addTime}
81 | {item.typeName}
82 | {item.view_count}人
83 |
84 |
87 |
88 |
89 |
90 |
91 | 查看全文
92 |
93 |
94 |
95 |
96 |
97 | )}
98 | />
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | >
111 | )
112 |
113 | }
114 |
115 | ArticleList.getInitialProps = async (context)=>{
116 |
117 | let id =parseInt(context.query.id)
118 | const promise = new Promise((resolve)=>{
119 | if(id){
120 | axios(servicePath.getListById+id).then(
121 | (res)=>resolve(res.data)
122 | )
123 |
124 | } else {
125 | console.log('error.....')
126 | resolve({article_content:'Id Error'})
127 | }
128 | })
129 | return await promise
130 | }
131 |
132 | export default ArticleList
133 |
--------------------------------------------------------------------------------
/admin_app/src/pages/ArticleList.js:
--------------------------------------------------------------------------------
1 | import React,{useState,useEffect} from 'react';
2 | import '../static/css/ArticleList.css'
3 | import { List ,Row ,Col , Modal ,message ,Button,Switch} from 'antd';
4 | import axios from 'axios'
5 | import servicePath from '../config/apiUrl'
6 | const { confirm } = Modal;
7 |
8 |
9 |
10 |
11 |
12 | function ArticleList(props){
13 |
14 | const [list,setList]=useState([])
15 |
16 | useEffect(()=>{
17 | getList()
18 | },[])
19 |
20 | //得到文章列表
21 | const getList = ()=>{
22 | axios({
23 | method:'get',
24 | url: servicePath.getArticleList,
25 | withCredentials: true,
26 | header:{ 'Access-Control-Allow-Origin':'*' }
27 | }).then(
28 | res=>{
29 | setList(res.data.list)
30 | }
31 | )
32 | }
33 | //删除文章的方法
34 | const delArticle = (id)=>{
35 | confirm({
36 | title: '确定要删除这篇博客文章吗?',
37 | content: '如果你点击OK按钮,文章将会永远被删除,无法恢复。',
38 | onOk() {
39 | axios(servicePath.delArticle+id,{ withCredentials: true}).then(
40 | res=>{
41 |
42 | message.success('文章删除成功')
43 | getList()
44 | }
45 | )
46 | },
47 | onCancel() {
48 | message.success('没有任何改变')
49 | },
50 | });
51 |
52 | }
53 |
54 | //修改文章
55 | const updateArticle = (id,checked)=>{
56 |
57 | props.history.push('/index/add/'+id)
58 |
59 | }
60 |
61 | //设置置顶
62 | const setTop = (id,checked)=>{
63 |
64 | let dataProps={}
65 | dataProps.id=id
66 | dataProps.isTop = checked ? 1:0
67 | axios({
68 | method:'post',
69 | url:servicePath.updateIsTop,
70 | header:{ 'Access-Control-Allow-Origin':'*' },
71 | data:dataProps,
72 | withCredentials: true
73 | }).then(
74 | res=>{
75 | if(res.data.data==='success'){
76 | message.success('置顶设置成功')
77 | getList()
78 | }else{
79 | message.error('设置失败');
80 | }
81 | }
82 | )
83 |
84 | }
85 |
86 |
87 | return (
88 |
89 |
92 |
93 | 标题
94 |
95 |
96 | 类别
97 |
98 |
99 | 发布时间
100 |
101 |
102 | 集数
103 |
104 |
105 | 浏览量
106 |
107 |
108 | 置顶
109 |
110 |
111 | 操作
112 |
113 |
114 |
115 | }
116 | bordered
117 | dataSource={list}
118 | renderItem={item => (
119 |
120 |
121 |
122 | {item.title}
123 |
124 |
125 | {item.typeName}
126 |
127 |
128 | {item.addTime}
129 |
130 |
131 | 共{item.part_count}集
132 |
133 |
134 | {item.view_count}
135 |
136 |
137 | {setTop(item.id,checked)}} />
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | )}
148 | />
149 |
150 |
151 | )
152 |
153 | }
154 |
155 | export default ArticleList
--------------------------------------------------------------------------------
/service/app/controller/default/home.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Controller = require('egg').Controller
4 |
5 | class HomeController extends Controller{
6 |
7 | async index(){
8 | //首页的文章列表数据
9 |
10 | this.ctx.body='hi api'
11 | }
12 |
13 | //首页文章列表接口
14 | async getArticleList(){
15 |
16 | let sql = 'SELECT article.id as id,'+
17 | 'article.title as title,'+
18 | 'article.introduce as introduce,'+
19 | "FROM_UNIXTIME(article.addTime,'%Y-%m-%d' ) as addTime,"+
20 | 'article.view_count as view_count ,'+
21 | 'article.introduce_html as introduce_html ,'+
22 | 'type.typeName as typeName '+
23 | 'FROM article LEFT JOIN type ON article.type_id = type.Id '+
24 | 'WHERE article.isTop = 0 AND article.type_id <> 99 '+
25 | 'ORDER BY article.id DESC'
26 | //console.log(sql)
27 | const resList = await this.app.mysql.query(sql)
28 | const resType = await this.app.mysql.select('type')
29 | //大胖逼逼叨列表
30 | const bibidaoList = await this.app.mysql.select('bibidao',{
31 | orders:[['id','desc']],
32 | limit:4
33 | })
34 | //置顶文章
35 | let sql2 = 'SELECT article.id as id,'+
36 | 'article.title as title,'+
37 | 'article.introduce as introduce,'+
38 | "FROM_UNIXTIME(article.addTime,'%Y-%m-%d' ) as addTime,"+
39 | 'article.view_count as view_count ,'+
40 | 'article.introduce_html as introduce_html ,'+
41 | 'type.typeName as typeName '+
42 | 'FROM article LEFT JOIN type ON article.type_id = type.Id '+
43 | 'WHERE article.isTop = 1 '+
44 | 'ORDER BY article.id DESC'
45 | const resTopList = await this.app.mysql.query(sql2)
46 | this.ctx.body={
47 | list:resList,
48 | type:resType,
49 | bibidaoList:bibidaoList,
50 | topList:resTopList
51 |
52 | }
53 |
54 | }
55 | //得到详细页文章接口
56 | async getArticleById(){
57 | //先配置路由的动态传值,然后再接收值
58 | let id = this.ctx.params.id
59 |
60 | if(id){
61 | let sql1 = "UPDATE article SET view_count = (view_Count+1) WHERE id ="+id
62 |
63 | let updateResult=await this.app.mysql.query(sql1)
64 | const updateSuccess = updateResult.affectedRows === 1
65 | if(updateSuccess){
66 | let sql2 = 'SELECT Id,type_id,title,article_content,'+
67 | 'introduce,view_count,part_count,article_content_html ,introduce_html,'+
68 | "FROM_UNIXTIME(addTime,'%Y-%m-%d' ) as addTime"+
69 | ' FROM article WHERE id='+id
70 |
71 | let result2 = await this.app.mysql.query(sql2)
72 | result2=JSON.stringify(result2)
73 | result2=JSON.parse(result2)
74 |
75 | let typeid = result2[0].type_id
76 |
77 |
78 | let sql3 = 'SELECT typeName FROM type WHERE id='+typeid
79 | let result3 = await this.app.mysql.query(sql3)
80 |
81 | //console.log(result3)
82 | result3=JSON.stringify(result3)
83 | result3=JSON.parse(result3)
84 |
85 | result2[0].typeName=result3[0].typeName
86 |
87 |
88 | this.ctx.body={data:result2}
89 |
90 | }else{
91 | console.log('id错误1')
92 | this.ctx.body={data:'id错误'}
93 | }
94 |
95 |
96 | }else{
97 | console.log('id错误2')
98 | this.ctx.body={data:'id错误'}
99 | }
100 |
101 |
102 |
103 | }
104 |
105 | //得到类别名称和编号
106 | async getTypeInfo(){
107 |
108 | const result = await this.app.mysql.select('type')
109 | this.ctx.body = {data:result}
110 |
111 | }
112 |
113 | //根据类别ID获得文章列表
114 | async getListById(){
115 | let id = parseInt(this.ctx.params.id)
116 | if(id){
117 | let sql = 'SELECT article.id as id,'+
118 | 'article.title as title,'+
119 | 'article.introduce as introduce,'+
120 | "FROM_UNIXTIME(article.addTime,'%Y-%m-%d %H:%i:%s' ) as addTime,"+
121 | 'article.view_count as view_count ,'+
122 | 'type.typeName as typeName '+
123 | 'FROM article LEFT JOIN type ON article.type_id = type.Id '+
124 | 'WHERE type_id='+id
125 | const result = await this.app.mysql.query(sql)
126 | this.ctx.body={data:result}
127 | }else{
128 | this.ctx.body={data:'错误的Id'}
129 | }
130 |
131 |
132 | }
133 |
134 | //获取总集数和总浏览数
135 | async getAllPartCount(){
136 |
137 | let sql = 'SELECT SUM(part_count) as all_part_count ,'+
138 | 'SUM(view_count) as all_view_count '+
139 | 'FROM article'
140 |
141 | const result = await this.app.mysql.query(sql)
142 | this.ctx.body={data:result}
143 | }
144 | //读取大胖逼逼叨的列表
145 | async getListBBD(){
146 | const resList = await this.app.mysql.select('bibidao',{
147 | orders:[['id','desc']]
148 | })
149 |
150 | this.ctx.body={list:resList}
151 | }
152 |
153 |
154 |
155 |
156 |
157 | }
158 |
159 | module.exports = HomeController
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## React hooks + Next.js + Egg.js 博客系统
2 |
3 |
4 | ### 系统介绍
5 |
6 | 这是一个适合写IT技术文章的博客系统,主要使用的技术是React Hooks + Next.js + Egg.js + MySql + Ant Design 。从技术上你不难看出,这个博客主要用的技术都是前端程序员熟悉的技术,所以适合前端程序员使用。
7 |
8 | 对Markdown的解析使用marked+highlight的方式,这也是目前最成熟的R解决方案。
9 |
10 | 博客预览地址:[https://jspang.com](https://jspang.com)
11 |
12 |
13 | 博客预览图片:
14 |
15 | 
16 |
17 | 
18 |
19 | 
20 |
21 | 
22 |
23 | 
24 |
25 | 
26 |
27 |
28 | 为了更好的使用这个博客,我还出了44集视频,你可以进行观看学习,学习完成你一定可以轻松使用这套系统。
29 |
30 |
31 |
32 | - 01、博客实战的课程介绍:[https://jspang.com/detailed?id=52#toc21](https://jspang.com/detailed?id=52#toc21)
33 | - 02、项目前端基础开发环境搭建:[https://jspang.com/detailed?id=52#toc25](https://jspang.com/detailed?id=52#toc25)
34 | - 03、制作博客公用头部并形成组件:[https://jspang.com/detailed?id=52#toc210](https://jspang.com/detailed?id=52#toc210)
35 | - 04、完成首页主体的两栏布局:[https://jspang.com/detailed?id=52#toc213](https://jspang.com/detailed?id=52#toc213)
36 | - 05、利用List组件制作博客列表:[https://jspang.com/detailed?id=52#toc217](https://jspang.com/detailed?id=52#toc217)
37 | - 06、编写“博主介绍”组件:[https://jspang.com/detailed?id=52#toc220](https://jspang.com/detailed?id=52#toc220)
38 | - 07、编写“通用广告”组件:[https://jspang.com/detailed?id=52#toc224](https://jspang.com/detailed?id=52#toc224)
39 | - 08、博客列表页面快速制作:[https://jspang.com/detailed?id=52#toc228](https://jspang.com/detailed?id=52#toc228)
40 | - 09、博客详细页面制作1-编写基本页面结构:[https://jspang.com/detailed?id=52#toc231](https://jspang.com/detailed?id=52#toc231)
41 | - 10、博客详细页面制作2-解析Markdown语法:[https://jspang.com/detailed?id=52#toc233](https://jspang.com/detailed?id=52#toc233)
42 | - 11、博客详细页面制作3-Markdown导航制作:[https://jspang.com/detailed?id=52#toc238](https://jspang.com/detailed?id=52#toc238)
43 | - 12、中台搭建1-安装egg.js开发环境:[https://jspang.com/detailed?id=52#toc241](https://jspang.com/detailed?id=52#toc241)
44 | - 13、中台搭建2-egg.js目录结构和约定规范:[https://jspang.com/detailed?id=52#toc244](https://jspang.com/detailed?id=52#toc244)
45 | - 14、中台搭建3-RESTful API设计简介和路由配置:[https://jspang.com/detailed?id=52#toc247](https://jspang.com/detailed?id=52#toc247)
46 | - 15、中台搭建4-Egg.js中连接mysql数据库:[https://jspang.com/detailed?id=52#toc250](https://jspang.com/detailed?id=52#toc250)
47 | - 16、中台搭建5-数据库设计和首页接口制作:[https://jspang.com/detailed?id=52#toc256](https://jspang.com/detailed?id=52#toc256)
48 | - 17、前中台结合1-前台读取首页文章列表接口:[https://jspang.com/detailed?id=52#toc259](https://jspang.com/detailed?id=52#toc259)
49 | - 18、前中台结合2-文章详细页面接口制作展示:[https://jspang.com/detailed?id=52#toc264](https://jspang.com/detailed?id=52#toc264)
50 | - 19、解决egg.js的跨域问题:[https://jspang.com/detailed?id=52#toc268](https://jspang.com/detailed?id=52#toc268)
51 | - 20、重构前台博客详细页面1-marked+highlight:[https://jspang.com/detailed?id=52#toc272](https://jspang.com/detailed?id=52#toc272)
52 | - 21、重构前台博客详细页面2-实现文章导航:[https://jspang.com/detailed?id=52#toc276](https://jspang.com/detailed?id=52#toc276)
53 | - 22、前台文章列表页的制作1-接口模块化和读取文章分类:[https://jspang.com/detailed?id=52#toc279](https://jspang.com/detailed?id=52#toc279)
54 | - 23、前台文章列表页的制作2-根据类别读取文章列表:[https://jspang.com/detailed?id=52#toc284](https://jspang.com/detailed?id=52#toc284)
55 | - 24、让前台所有页面支持Markdown解析:[https://jspang.com/detailed?id=52#toc288](https://jspang.com/detailed?id=52#toc288)
56 | - 25、后台开发01-开发环境搭建:[https://jspang.com/detailed?id=52#toc290](https://jspang.com/detailed?id=52#toc290)
57 | - 26、后台开发02-页面路由配置:[https://jspang.com/detailed?id=52#toc293](https://jspang.com/detailed?id=52#toc293)
58 | - 27、后台开发03-编写登录界面:[https://jspang.com/detailed?id=52#toc296](https://jspang.com/detailed?id=52#toc296)
59 | - 28、后台开发04-UI框架搭建:[https://jspang.com/detailed?id=52#toc2100](https://jspang.com/detailed?id=52#toc2100)
60 | - 29、后台开发05-添加文章页面制作1:[https://jspang.com/detailed?id=52#toc2103](https://jspang.com/detailed?id=52#toc2103)
61 | - 30、后台开发06-添加文章页面制作2:[https://jspang.com/detailed?id=52#toc2106](https://jspang.com/detailed?id=52#toc2106)
62 | - 31、后台开发07-Markdown编辑器制作:[https://jspang.com/detailed?id=52#toc2110](https://jspang.com/detailed?id=52#toc2110)
63 | - 32、后台开发08-编写service登录接口:[https://jspang.com/detailed?id=52#toc2115](https://jspang.com/detailed?id=52#toc2115)
64 | - 33、后台开发09-实现前台登录操作:[https://jspang.com/detailed?id=52#toc2119](https://jspang.com/detailed?id=52#toc2119)
65 | - 34、后台开发10-中台路由守卫制作:[https://jspang.com/detailed?id=52#toc2123](https://jspang.com/detailed?id=52#toc2123)
66 | - 35、后台开发11-读取添加文章页面的类别信息:[https://jspang.com/detailed?id=52#toc2127](https://jspang.com/detailed?id=52#toc2127)
67 | - 36、后台开发12-添加文章的方法(上):[https://jspang.com/detailed?id=52#toc2132](https://jspang.com/detailed?id=52#toc2132)
68 | - 37、后台开发13-添加文章的方法(中):[https://jspang.com/detailed?id=52#toc2137](https://jspang.com/detailed?id=52#toc2137)
69 | - 38、后台开发14-添加文章的方法(下):[https://jspang.com/detailed?id=52#toc2137](https://jspang.com/detailed?id=52#toc2137)
70 | - 39、后台开发15-文章列表制作(上):[https://jspang.com/detailed?id=52#toc2146](https://jspang.com/detailed?id=52#toc2146)
71 | - 40、后台开发16-文章列表制作(中):[https://jspang.com/detailed?id=52#toc2149](https://jspang.com/detailed?id=52#toc2149)
72 | - 41、后台开发17-删除文章:[https://jspang.com/detailed?id=52#toc2153](https://jspang.com/detailed?id=52#toc2153)
73 | - 42、后台开发18-修改文章(上):[https://jspang.com/detailed?id=52#toc2157](https://jspang.com/detailed?id=52#toc2157)
74 | - 43、后台开发19-修改文章(下):[https://jspang.com/detailed?id=52#toc2161](https://jspang.com/detailed?id=52#toc2161)
75 | - 44、博客部署介绍和演示:[https://jspang.com/detailed?id=52#toc2164](https://jspang.com/detailed?id=52#toc2164)
76 |
77 |
78 | ### 后期更新计划
79 |
80 | 1. 修改完善目前的博客前台Bug。
81 | 2. 增加好课推荐功能。
82 | 3. 优化后台对应的功能和安全性。
83 | 4. 增加留言功能。
84 |
85 |
--------------------------------------------------------------------------------
/service/app/controller/admin/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Controller = require('egg').Controller
4 |
5 | class MainController extends Controller{
6 |
7 | async index(){
8 | //首页的文章列表数据
9 | this.ctx.body='hi api'
10 | }
11 |
12 | //判断用户名密码是否正确
13 | async checkLogin(){
14 | let userName = this.ctx.request.body.userName
15 | let password = this.ctx.request.body.password
16 | const sql = " SELECT userName FROM admin_user WHERE userName = '"+userName +
17 | "' AND password = '"+password+"'"
18 |
19 | const res = await this.app.mysql.query(sql)
20 | if(res.length>0){
21 | //登录成功,进行session缓存
22 | let openId=new Date().getTime()
23 | this.ctx.session.openId={ 'openId':openId }
24 | this.ctx.body={'data':'登录成功','openId':openId}
25 |
26 | }else{
27 | this.ctx.body={data:'登录失败'}
28 | }
29 | }
30 | //退出登录
31 | async outLogin(){
32 | this.ctx.session.openId=null
33 | this.ctx.body={'data':'退出成功'}
34 |
35 | }
36 |
37 | async checkOpenId(){
38 | let cOpenId = this.ctx.request.body.openId
39 | let sOpenId = this.ctx.session.openId.openId
40 | if(sOpenId & cOpenId==sOpenId){
41 | this.ctx.body={data:'已经登录'}
42 | }else{
43 | this.ctx.body={data:'没有登录'}
44 | }
45 |
46 | }
47 |
48 | //后台文章分类信息
49 | async getTypeInfo(){
50 | const resType = await this.app.mysql.select('type')
51 | this.ctx.body={data:resType}
52 | }
53 | //添加文章
54 | async addArticle(){
55 |
56 | let tmpArticle= this.ctx.request.body
57 | // tmpArticle.
58 | const result = await this.app.mysql.insert('article',tmpArticle)
59 | const insertSuccess = result.affectedRows === 1
60 | const insertId = result.insertId
61 |
62 | this.ctx.body={
63 | isScuccess:insertSuccess,
64 | insertId:insertId
65 | }
66 | }
67 | //修改文章
68 | async updateArticle(){
69 | let tmpArticle= this.ctx.request.body
70 |
71 | const result = await this.app.mysql.update('article', tmpArticle);
72 | const updateSuccess = result.affectedRows === 1;
73 | console.log(updateSuccess)
74 | this.ctx.body={
75 | isScuccess:updateSuccess
76 | }
77 | }
78 |
79 | //修改文章置顶信息
80 | async updateIsTop(){
81 | let tmpArticle= this.ctx.request.body
82 |
83 |
84 | let sql = 'update article set isTop = '+tmpArticle.isTop+' where id = '+tmpArticle.id
85 | let updateResult=await this.app.mysql.query(sql)
86 | const updateSuccess = updateResult.affectedRows === 1
87 | if(updateSuccess){
88 | this.ctx.body={data:'success'}
89 | }else{
90 | this.ctx.body={data:'error'}
91 | }
92 | }
93 |
94 |
95 | //获得文章列表
96 | async getArticleList(){
97 |
98 | let sql = 'SELECT article.id as id,'+
99 | 'article.title as title,'+
100 | 'article.introduce as introduce,'+
101 | "FROM_UNIXTIME(article.addTime,'%Y-%m-%d' ) as addTime,"+
102 | 'article.view_count as view_count ,'+
103 | 'article.part_count as part_count ,'+
104 | 'article.isTop as isTop ,'+
105 | 'type.typeName as typeName '+
106 | 'FROM article LEFT JOIN type ON article.type_id = type.Id '+
107 | 'ORDER BY article.id DESC '
108 |
109 | const resList = await this.app.mysql.query(sql)
110 | this.ctx.body={list:resList}
111 |
112 | }
113 |
114 | //删除文章
115 | async delArticle(){
116 | let id = this.ctx.params.id
117 | const res = await this.app.mysql.delete('article',{'id':id})
118 | this.ctx.body={data:res}
119 | }
120 |
121 | //根据文章ID得到文章详情,用于修改文章
122 | async getArticleById(){
123 | let id = this.ctx.params.id
124 |
125 | let sql = 'SELECT article.id as id,'+
126 | 'article.title as title,'+
127 | 'article.introduce as introduce,'+
128 | 'article.article_content as article_content,'+
129 | "FROM_UNIXTIME(article.addTime,'%Y-%m-%d' ) as addTime,"+
130 | 'article.view_count as view_count ,'+
131 | 'article.part_count as part_count ,'+
132 | 'type.typeName as typeName ,'+
133 | 'type.id as typeId '+
134 | 'FROM article LEFT JOIN type ON article.type_id = type.Id '+
135 | 'WHERE article.id='+id
136 | const result = await this.app.mysql.query(sql)
137 | this.ctx.body={data:result}
138 | }
139 |
140 |
141 | //增加大胖逼逼叨的方法
142 | async addBBD(){
143 | let tmpBBD = this.ctx.request.body
144 | const result = await this.app.mysql.insert('bibidao',tmpBBD)
145 | const insertSuccess = result.affectedRows === 1
146 | this.ctx.body={ isScuccess:insertSuccess }
147 | }
148 | //读取大胖逼逼叨的列表
149 | async getListBBD(){
150 | const resList = await this.app.mysql.select('bibidao',{
151 | orders:[['id','desc']]
152 | })
153 | console.log(resList)
154 | this.ctx.body={list:resList}
155 | }
156 |
157 | //根据ID删除逼逼叨列表的方法
158 | async delBBDbyId(){
159 | let id = this.ctx.params.id
160 | const res = await this.app.mysql.delete('bibidao',{'id':id})
161 | this.ctx.body={data:res}
162 | }
163 |
164 |
165 |
166 |
167 | }
168 |
169 | module.exports = MainController
--------------------------------------------------------------------------------
/blog/pages/detailed.js:
--------------------------------------------------------------------------------
1 | import React,{useState,useEffect} from 'react'
2 | import Head from 'next/head'
3 | import {Row, Col , Icon ,Breadcrumb ,BackTop ,Skeleton } from 'antd'
4 |
5 | import Header from '../components/Header'
6 | import Author from '../components/Author'
7 | import Advert from '../components/Advert'
8 | import Footer from '../components/Footer'
9 | import Rightmi from '../components/Rightmi'
10 | import '../static/style/pages/detailed.css'
11 |
12 | import 'markdown-navbar/dist/navbar.css';
13 | import axios from 'axios'
14 | import marked from 'marked'
15 | import hljs from "highlight.js";
16 | import 'highlight.js/styles/monokai-sublime.css';
17 | import Tocify from '../components/tocify.tsx'
18 | import servicePath from '../config/apiUrl'
19 |
20 |
21 |
22 |
23 |
24 | const Detailed = (props) =>{
25 |
26 | let articleContent=props.article_content
27 | if(articleContent=='id错误'){
28 | console.log('渲染完成,但什么都没有')
29 |
30 | return false
31 | }
32 |
33 | useEffect( ()=>{
34 |
35 | setTimeout(()=>{
36 | myFuction()
37 | },100)
38 |
39 |
40 |
41 |
42 |
43 |
44 | },[])
45 |
46 | const [html,setHtml] = useState(props.article_content_html)
47 | const [tocify,setTocify] = useState(new Tocify())
48 | const [loading,setLoading] = useState(true)
49 |
50 |
51 | const myFuction = async ()=>{
52 |
53 | let newhtml =await marked(props.article_content)
54 | //setHtml(newhtml)
55 | setLoading(false)
56 | //console.log(tocify.render())
57 |
58 | }
59 |
60 |
61 | const renderer = new marked.Renderer();
62 | renderer.heading = function(text, level, raw) {
63 | const anchor = tocify.add(text, level);
64 | return `${text}\n`;
65 | };
66 |
67 | marked.setOptions({
68 |
69 | renderer: renderer,
70 |
71 | gfm: true,
72 | pedantic: false,
73 | sanitize: false,
74 | tables: true,
75 | breaks: false,
76 | smartLists: true,
77 | smartypants: false,
78 |
79 | highlight: function (code) {
80 | return hljs.highlightAuto(code).value;
81 | }
82 |
83 | });
84 |
85 |
86 |
87 |
88 | return (
89 | <>
90 |
91 | 技术胖-{props.title}
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | 首页
102 | {props.typeName}
103 | {props.title}
104 |
105 |
106 |
107 |
108 |
109 | {props.title}
110 |
111 |
112 |
113 | {props.addTime}
114 | {props.typeName}
115 | {props.view_count}
116 |
117 |
118 |
119 |
120 |
121 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
文章目录
142 |
143 |
144 | {tocify && tocify.render()}
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | >
160 | )
161 | //{tocify && tocify.render()}
162 |
163 | }
164 |
165 | Detailed.getInitialProps = async(context)=>{
166 | let date=new Date();
167 |
168 |
169 | let month=date.getMonth();
170 | let day=date.getDate();
171 |
172 | let hour=date.getHours();
173 | let minute=date.getMinutes();
174 | let second=date.getSeconds();
175 | let time=month+'/'+day+'/'+hour+':'+minute+':'+second
176 |
177 |
178 | console.log('----->'+time+':Visit the details page,parameter='+context.query.id)
179 | //把ID强制转换成数字
180 |
181 | let id =parseInt(context.query.id)
182 |
183 |
184 | const promise = new Promise((resolve)=>{
185 | if(id){
186 | axios(servicePath.getArticleById+id).then(
187 | (res)=>{
188 | // console.log(title)
189 | if(res.data.data=='id错误'){
190 | console.log('ERROR.......')
191 | resolve({article_content:'id ERROR'})
192 | }else{
193 | resolve(res.data.data[0])
194 | }
195 |
196 | }
197 | )
198 | }else{
199 | console.log('error......')
200 | resolve({article_content:'Id Error'})
201 |
202 | }
203 |
204 | })
205 | return await promise
206 |
207 |
208 |
209 |
210 | }
211 |
212 | export default Detailed
213 |
--------------------------------------------------------------------------------
/admin_app/src/pages/BBDList.js:
--------------------------------------------------------------------------------
1 | import React,{useState,useEffect} from 'react';
2 | import '../static/css/BBDList.css'
3 | import {List,Icon, Card,Input, Modal ,message ,Button, Drawer,} from 'antd';
4 |
5 | import axios from 'axios'
6 | import servicePath from '../config/apiUrl'
7 | import ClearableLabeledInput from 'antd/lib/input/ClearableLabeledInput';
8 | const { confirm } = Modal;
9 | const { TextArea } = Input;
10 |
11 |
12 |
13 |
14 |
15 | function ArticleList(props){
16 |
17 | const [list,setList]=useState([])
18 | const [visible,setVisible] = useState(false)
19 | const [title,setTtitle] = useState('')
20 | const [videoUrl,setVideoUrl] = useState('')
21 | const [imageUrl,setImageUrl] = useState('')
22 |
23 | //修改标题
24 | const changeTitle = (e)=>{
25 | setTtitle(e.target.value)
26 | }
27 |
28 | //修改视频地址
29 | const changeVideoUrl = (e)=>{
30 | setVideoUrl(e.target.value)
31 | }
32 | //修改图片地址
33 | const changeImageUrl = (e)=>{
34 | setImageUrl(e.target.value)
35 | }
36 |
37 | //清除文本框值的方法
38 | const clearAll = ()=>{
39 | setTtitle('')
40 | setVideoUrl('')
41 | setImageUrl('')
42 | }
43 |
44 | //增加视频方法
45 | const saveBBD = ()=>{
46 |
47 | if(!title){
48 | message.error('视频标题不能为空')
49 | return false
50 | }else if(!videoUrl){
51 | message.error('视频地址不能为空')
52 | return false
53 | }else if(!imageUrl){
54 | message.error('图片地址不能为空')
55 | return false
56 | }
57 |
58 | let dataProps = {}
59 | dataProps.title = title
60 | dataProps.url = videoUrl
61 | dataProps.image = imageUrl
62 | dataProps.order_id = 0
63 | axios({
64 | method:'post',
65 | url:servicePath.addBBD,
66 | data:dataProps,
67 | withCredentials: true
68 | }).then(
69 | res=>{
70 |
71 | if(res.data.isScuccess){
72 | message.success('视频添加成功')
73 | clearAll()
74 | getList()
75 | }else{
76 | message.error('文章添加失败');
77 | }
78 |
79 | }
80 | )
81 | }
82 |
83 | const delBBDAction = (id)=>{
84 | console.log(id)
85 | confirm({
86 | title: '确定要删除这个视频吗?',
87 | content: '如果你点击OK按钮,视频将永远被删除,无法恢复。',
88 | onOk() {
89 | axios(servicePath.delBBDbyId+id,{ withCredentials: true}).then(
90 | res=>{
91 |
92 | message.success('文章删除成功')
93 | getList()
94 | }
95 | )
96 | },
97 | onCancel() {
98 | message.success('没有任何改变')
99 | },
100 | });
101 |
102 |
103 | }
104 |
105 |
106 |
107 | //读取视频列表
108 | const getList = ()=>{
109 |
110 | axios({
111 | method:'get',
112 | url:servicePath.getListBBD,
113 | withCredentials: true
114 | }).then(
115 | res=>{
116 | console.log(res.data.list)
117 | setList(res.data.list)
118 | }
119 | )
120 |
121 | }
122 |
123 |
124 | //打开抽屉
125 | const showDrawer = ()=>{
126 | setVisible(true)
127 | }
128 |
129 | //关闭抽屉
130 | const onClose = ()=>{
131 | setVisible(false)
132 | }
133 |
134 | useEffect(()=>{
135 | getList()
136 | },[])
137 |
138 |
139 |
140 |
141 |
142 | return (
143 |
144 |
145 |
146 |
154 |
155 |
156 |
161 |
162 |
163 |
168 |
169 |
170 |
175 |
176 |
177 |

178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
(
205 |
206 | ,
211 | {
212 | delBBDAction(item.id)
213 | }} />,
214 |
215 | ]}
216 | cover={
217 |
221 | }
222 | >
223 |
226 |
227 |
228 |
229 | )}
230 | />
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 | )
239 |
240 | }
241 |
242 | export default ArticleList
--------------------------------------------------------------------------------
/blog/pages/index.js:
--------------------------------------------------------------------------------
1 | import React,{useState } from 'react'
2 | import Link from 'next/link'
3 | import Head from 'next/head'
4 | import {Row, Col ,Tag, List ,Icon ,BackTop ,Spin ,Affix ,Card} from 'antd'
5 | import axios from 'axios'
6 | import Header from '../components/Header'
7 | import Author from '../components/Author'
8 | import Advert from '../components/Advert'
9 | import Footer from '../components/Footer'
10 | import Rightmi from '../components/Rightmi'
11 | import StudyLine from '../components/StudyLine'
12 | import '../static/style/pages/index.css'
13 | import marked from 'marked'
14 | import hljs from "highlight.js";
15 | import 'highlight.js/styles/monokai-sublime.css'
16 | import servicePath from '../config/apiUrl'
17 | import CountUp from 'react-countup'
18 |
19 |
20 | const Home = (res) =>{
21 | const [ mylist , setMylist ] = useState( res.list);
22 | const [ topList , setTopList ] = useState( res.topList);
23 | const [ type , setType ] = useState( res.type);
24 | const [ bibidaoList , setBibidaoList ] = useState( res.bibidaoList);
25 | const [ loading,setLoading] =useState(false)
26 |
27 |
28 |
29 |
30 |
31 | const renderer = new marked.Renderer();
32 | marked.setOptions({
33 | renderer: renderer,
34 | gfm: true,
35 | pedantic: false,
36 | sanitize: false,
37 | tables: true,
38 | breaks: false,
39 | smartLists: true,
40 | smartypants: false,
41 | sanitize:false,
42 | xhtml: false,
43 | highlight: function (code) {
44 | return hljs.highlightAuto(code).value;
45 | }
46 |
47 | });
48 |
49 | const goLoading= ()=>{
50 |
51 | setLoading(true)
52 | }
53 |
54 | return (
55 | <>
56 |
57 |
58 | 首页 | 技术胖-胜洪宇关注web前端技术-前端免费视频第一博客
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
(
79 |
80 |
81 |
86 |
87 | 置顶
88 | {item.addTime}
89 | {item.typeName}
90 | 人
91 |
92 |
93 |
96 |
97 |
98 |
99 |
100 |
101 | 查看全文
102 |
103 |
104 |
105 |
106 |
107 | )}
108 | />
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
119 | 大胖逼逼叨
120 |
121 |
122 |
更多
123 |
124 |
125 |
126 | }
127 | dataSource={bibidaoList}
128 | grid={{
129 | gutter: 10,
130 | sm: 0,
131 | md: 4,
132 | }}
133 | renderItem={item => (
134 |
135 |
136 |
137 |
138 |
144 |
145 | )}
146 | />
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 | 最新日志
}
158 | itemLayout="vertical"
159 | dataSource={mylist}
160 | renderItem={item => (
161 |
162 |
163 |
168 |
169 | {item.addTime}
170 | {item.typeName}
171 | 人
172 |
173 |
176 |
177 |
178 |
179 |
180 |
181 | 查看全文
182 |
183 |
184 |
185 |
186 |
187 | )}
188 | />
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 | >
208 | )
209 |
210 | }
211 |
212 | Home.getInitialProps = async (context)=>{
213 |
214 | let date=new Date();
215 |
216 |
217 | let month=date.getMonth();
218 | let day=date.getDate();
219 |
220 | let hour=date.getHours();
221 | let minute=date.getMinutes();
222 | let second=date.getSeconds();
223 | let time=month+'/'+day+'/'+hour+':'+minute+':'+second
224 |
225 |
226 |
227 | console.log('----->'+time+':Visit the Index page')
228 |
229 |
230 | const promise = new Promise((resolve)=>{
231 | axios(servicePath.getArticleList).then(
232 | (res)=>{
233 |
234 | resolve(res.data)
235 | }
236 | )
237 | })
238 |
239 | return await promise
240 | }
241 |
242 | export default Home
243 |
--------------------------------------------------------------------------------
/admin_app/src/pages/AddArticle.js:
--------------------------------------------------------------------------------
1 | import React,{useState , useEffect} from 'react';
2 | import marked from 'marked'
3 | import '../static/css/AddArticle.css'
4 | import { Row, Col ,Input, Select ,Button ,DatePicker ,message,Spin } from 'antd'
5 | import axios from 'axios'
6 | import servicePath from '../config/apiUrl'
7 | import Tocify from '../components/tocify.tsx'
8 | import hljs from "highlight.js";
9 | import 'highlight.js/styles/monokai-sublime.css';
10 | const { Option } = Select;
11 | const { TextArea } = Input
12 |
13 | function AddArticle(props){
14 |
15 | const [articleId,setArticleId] = useState(0) // 文章的ID,如果是0说明是新增加,如果不是0,说明是修改
16 | const [articleTitle,setArticleTitle] = useState('') //文章标题
17 | const [articleContent , setArticleContent] = useState('') //markdown的编辑内容
18 | const [markdownContent, setMarkdownContent] = useState('预览内容') //html内容
19 | const [introducemd,setIntroducemd] = useState() //简介的markdown内容
20 | const [introducehtml,setIntroducehtml] = useState('等待编辑') //简介的html内容
21 | const [showDate,setShowDate] = useState() //发布日期
22 | const [updateDate,setUpdateDate] = useState() //修改日志的日期
23 | const [typeInfo ,setTypeInfo] = useState([]) // 文章类别信息
24 | const [selectedType,setSelectType] = useState(1) //选择的文章类别
25 | const [partCount,setPartCount] = useState(0) //文章的集数
26 | const [isLoading,setIsLoadding] = useState(false) //是否显示加载
27 |
28 |
29 |
30 |
31 | useEffect(()=>{
32 |
33 | getTypeInfo()
34 | //获得文章ID
35 | let tmpId = props.match.params.id
36 | console.log(tmpId)
37 | if(tmpId){
38 | setIsLoadding(true)
39 | setArticleId(tmpId)
40 | getArticleById(tmpId)
41 |
42 | }
43 |
44 |
45 | },[])
46 |
47 | const getArticleById = (id)=>{
48 | axios(servicePath.getArticleById+id,{
49 | withCredentials: true,
50 | header:{ 'Access-Control-Allow-Origin':'*' }
51 | }).then(
52 | res=>{
53 | console.log(res)
54 | setIsLoadding(false)
55 | setArticleTitle(res.data.data[0].title)
56 | setArticleContent(res.data.data[0].article_content)
57 | let html=marked(res.data.data[0].article_content)
58 | setMarkdownContent(html)
59 | setIntroducemd(res.data.data[0].introduce)
60 | let tmpInt = marked(res.data.data[0].introduce)
61 | setIntroducehtml(tmpInt)
62 | setShowDate(res.data.data[0].addTime)
63 | setSelectType(res.data.data[0].typeId)
64 | setPartCount(res.data.data[0].part_count)
65 |
66 |
67 | }
68 | )
69 | }
70 |
71 |
72 |
73 |
74 | const tocify =new Tocify()
75 | const renderer = new marked.Renderer();
76 | renderer.heading = function(text, level, raw) {
77 | const anchor = tocify.add(text, level);
78 | return `${text}\n`;
79 | };
80 | marked.setOptions({
81 |
82 | renderer: renderer,
83 | gfm: true,
84 | pedantic: false,
85 | sanitize: false,
86 | tables: true,
87 | breaks: false,
88 | smartLists: true,
89 | smartypants: false,
90 | highlight: function (code) {
91 | return hljs.highlightAuto(code).value;
92 | }
93 |
94 | });
95 | //从中台得到文章类别信息
96 | const getTypeInfo =()=>{
97 |
98 | axios({
99 | method:'get',
100 | url:servicePath.getTypeInfo,
101 | header:{ 'Access-Control-Allow-Origin':'*' },
102 | withCredentials: true
103 | }).then(
104 | res=>{
105 | console.log(res.data.data)
106 | if(res.data.data=="没有登录"){
107 | localStorage.removeItem('openId')
108 | props.history.push('/')
109 | }else{
110 | setTypeInfo(res.data.data)
111 | }
112 |
113 | }
114 | )
115 | }
116 |
117 |
118 | const changeContent = (e)=>{
119 | setArticleContent(e.target.value)
120 |
121 |
122 | }
123 | const markedContent = ()=>{
124 | setIsLoadding(true)
125 |
126 | setTimeout(()=>{
127 | let html=marked(articleContent)
128 | setMarkdownContent(html)
129 | setIsLoadding(false)
130 | message.success('转换完成')
131 | },300)
132 |
133 |
134 | }
135 |
136 | const changeIntroduce = (e)=>{
137 | setIntroducemd(e.target.value)
138 | let html=marked(e.target.value)
139 | setIntroducehtml(html)
140 | }
141 | //选择类别后的便哈
142 | const selectTypeHandler =(value)=>{
143 | console.log(value)
144 | setSelectType(value)
145 | }
146 |
147 | //保存文章的方法
148 | const saveArticle = ()=>{
149 |
150 | // markedContent() //先进行转换
151 |
152 |
153 | if(!selectedType){
154 | message.error('必须选择文章类别')
155 | return false
156 | }else if(!articleTitle){
157 | message.error('文章名称不能为空')
158 | return false
159 | }else if(!articleContent){
160 | message.error('文章内容不能为空')
161 | return false
162 | }else if(!introducemd){
163 | message.error('简介不能为空')
164 | return false
165 | }else if(!showDate){
166 | message.error('发布日期不能为空')
167 | return false
168 | }
169 |
170 |
171 |
172 |
173 | setIsLoadding(true)
174 |
175 |
176 | let dataProps={}
177 | console.log(selectedType)
178 | dataProps.type_id = selectedType
179 | dataProps.title = articleTitle
180 | dataProps.article_content =articleContent
181 | dataProps.introduce =introducemd
182 | let datetext= showDate.replace('-','/') //把字符串转换成时间戳
183 | dataProps.addTime =(new Date(datetext).getTime())/1000
184 | dataProps.part_count = partCount
185 | dataProps.article_content_html = markdownContent
186 | dataProps.introduce_html = introducehtml
187 |
188 | if(articleId==0){
189 | console.log('articleId=:'+articleId)
190 | dataProps.view_count =Math.ceil(Math.random()*100)+1000
191 | axios({
192 | method:'post',
193 | url:servicePath.addArticle,
194 | header:{ 'Access-Control-Allow-Origin':'*' },
195 | data:dataProps,
196 | withCredentials: true
197 | }).then(
198 | res=>{
199 | setIsLoadding(false)
200 | setArticleId(res.data.insertId)
201 | if(res.data.isScuccess){
202 | message.success('文章发布成功')
203 | }else{
204 | message.error('文章发布失败');
205 | }
206 |
207 | }
208 | )
209 | }else{
210 | console.log('articleId:'+articleId)
211 | setIsLoadding(false)
212 | dataProps.id = articleId
213 | axios({
214 | method:'post',
215 | url:servicePath.updateArticle,
216 | header:{ 'Access-Control-Allow-Origin':'*' },
217 | data:dataProps,
218 | withCredentials: true
219 | }).then(
220 | res=>{
221 |
222 | if(res.data.isScuccess){
223 | message.success('文章保存成功')
224 | }else{
225 | message.error('保存失败');
226 | }
227 |
228 | }
229 | )
230 | }
231 |
232 |
233 | }
234 |
235 |
236 | return (
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 | {
250 |
251 | setArticleTitle(e.target.value)
252 | }}
253 | size="large" />
254 |
255 |
256 |
257 |
266 |
267 |
268 | {
272 |
273 | setPartCount(e.target.value)
274 | }}
275 | size="large" />
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
290 |
291 |
292 |
293 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
320 |
321 |
324 |
325 |
326 |
327 |
328 | setShowDate(dateString)}
330 | placeholder="发布日期"
331 | size="large"
332 |
333 | />
334 |
335 |
336 |
337 |
338 | setUpdateDate(dateString)}
341 | placeholder="修改日期"
342 | />
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 | )
355 | }
356 |
357 | export default AddArticle
--------------------------------------------------------------------------------
/blog/static/style/pages/han.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | :root {
4 | --active-file-bg-color: #dadada;
5 | --active-file-bg-color: rgba(32, 43, 51, 0.63);
6 | --active-file-text-color: white;
7 | --bg-color: #fff;
8 | --text-color: #333;
9 | --side-bar-bg-color: #f5f5f5;
10 | --control-text-color: #666;
11 | }
12 |
13 | /* 防止用户自定义背景颜色对网页的影响,添加让用户可以自定义字体 */
14 | html {
15 | color: #333;
16 | background: #fff;
17 | -webkit-text-size-adjust: 100%;
18 | -ms-text-size-adjust: 100%;
19 | text-rendering: optimizelegibility;
20 | font-size: 14px;
21 | -webkit-font-smoothing: initial;
22 | }
23 |
24 | #write {
25 | max-width: 960px;
26 | padding-top: 2em;
27 | padding-left: 60px;
28 | padding-right: 60px;
29 | min-height: calc(100vh - 6em);
30 | -webkit-font-smoothing: antialiased;
31 | font-size: 16px;
32 | }
33 |
34 | .typora-node #write {
35 | min-height: calc(100% - 6em);
36 | }
37 |
38 | pre.md-meta-block {
39 | background: #f5f5f5;
40 | padding: 1em;
41 | border-radius: 3px;
42 | font-size: 14px;
43 | }
44 |
45 | @media screen and (max-width: 800px) {
46 | html {
47 | font-size: 14px;
48 | }
49 |
50 | #write {
51 | padding-left: 30px;
52 | padding-right: 30px;
53 | font-size: 14px;
54 | }
55 | }
56 |
57 | @media screen and (min-width: 1100px) {
58 | body, #footer-word-count-info {
59 | background: #f5f5f5;
60 | }
61 |
62 | body.pin-outline,
63 | .pin-outline #footer-word-count-info,
64 | .pin-outline footer {
65 | background: #fff;
66 | }
67 |
68 | #write {
69 | max-width: 1000px;
70 | padding: 40px 60px;
71 | background: #fff;
72 | margin: 3em auto 3em;
73 | border: 1px solid #ddd;
74 | border-width: 0 1px;
75 | }
76 |
77 | .pin-outline #write {
78 | max-width: 1000px;
79 | background: #fff;
80 | margin: 0 0 0;
81 | border: 0;
82 | padding-left: 60px;
83 | padding-right: 60px;
84 | }
85 |
86 | footer {
87 | background-color: transparent;
88 | }
89 | }
90 |
91 | @media screen and (min-width: 1300px) {
92 | body.pin-outline,
93 | .pin-outline #footer-word-count-info,
94 | .pin-outline footer {
95 | background: #f5f5f5;
96 | }
97 |
98 | .pin-outline #write {
99 | max-width: 1000px;
100 | padding: 40px 60px;
101 | background: #fff;
102 | margin: 3em auto 3em;
103 | border: 1px solid #ddd;
104 | border-width: 0 1px;
105 | }
106 |
107 | .pin-outline footer {
108 | background-color: transparent;
109 | }
110 |
111 | #footer-word-count-info {
112 | background: #f5f5f5;
113 | }
114 | }
115 |
116 |
117 | /* 如果你的项目仅支持 IE9+ | Chrome | Firefox 等,推荐在 中添加 .borderbox 这个 class */
118 | html.borderbox *, html.borderbox *:before, html.borderbox *:after {
119 | -moz-box-sizing: border-box;
120 | -webkit-box-sizing: border-box;
121 | box-sizing: border-box;
122 | }
123 |
124 | /* 内外边距通常让各个浏览器样式的表现位置不同 */
125 | body, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, menu, nav, section {
126 | margin: 0;
127 | padding: 0;
128 | }
129 |
130 | /* 重设 HTML5 标签, IE 需要在 js 中 createElement(TAG) */
131 | article, aside, details, figcaption, figure, footer, header, menu, nav, section {
132 | display: block;
133 | }
134 |
135 | /* HTML5 媒体文件跟 img 保持一致 */
136 | audio, canvas, video {
137 | display: inline-block;
138 | }
139 |
140 | /* 要注意表单元素并不继承父级 font 的问题 */
141 | body, button, input, select, textarea {
142 | font: 300 1em/1.8 "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans;
143 | }
144 |
145 | body {
146 | font-family: "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans;
147 | }
148 |
149 | h1, h2, h3, h4, h5, h6 {
150 | font-family: "TimesNewRomanPS-ItalicMT", "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans;
151 | /*font-family: "PingFang SC", "Lantinghei SC", "Microsoft Yahei", "Hiragino Sans GB", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans;*/
152 | -webkit-font-smoothing: initial;
153 | font-weight: 100;
154 | color: var(--text-color);
155 | line-height: 1.35;
156 | font-variant-numeric: lining-nums;
157 | margin-bottom: 1em;
158 | }
159 |
160 | em {
161 | font-family: Georgia-Italic, STSongti-SC-Light, serif;
162 | }
163 |
164 | strong em,
165 | em strong {
166 | font-family: Georgia-BoldItalic, STSongti-SC-Regular, serif;
167 | }
168 |
169 | button::-moz-focus-inner,
170 | input::-moz-focus-inner {
171 | padding: 0;
172 | border: 0;
173 | }
174 |
175 | /* 去掉各Table cell 的边距并让其边重合 */
176 | table {
177 | border-collapse: collapse;
178 | border-spacing: 0;
179 | }
180 |
181 | /* 去除默认边框 */
182 | fieldset, img {
183 | border: 0;
184 | }
185 |
186 | /* 块/段落引用 */
187 | blockquote {
188 | position: relative;
189 | color: #999;
190 | font-weight: 400;
191 | border-left: 1px solid #1abc9c;
192 | padding-left: 1em;
193 | margin: 1em 3em 1em 2em;
194 | }
195 |
196 | @media only screen and ( max-width: 640px ) {
197 | blockquote {
198 | margin: 1em 0;
199 | }
200 | }
201 |
202 | /* Firefox 以外,元素没有下划线,需添加 */
203 | acronym, abbr {
204 | border-bottom: 1px dotted;
205 | font-variant: normal;
206 | }
207 |
208 | /* 添加鼠标问号,进一步确保应用的语义是正确的(要知道,交互他们也有洁癖,如果你不去掉,那得多花点口舌) */
209 | abbr {
210 | cursor: help;
211 | }
212 |
213 | address, caption, cite, code, dfn, th, var {
214 | font-style: normal;
215 | font-weight: 400;
216 | }
217 |
218 | /* 去掉列表前的标识, li 会继承,大部分网站通常用列表来很多内容,所以应该当去 */
219 | ul, ol {
220 | list-style: none;
221 | }
222 |
223 | /* 对齐是排版最重要的因素, 别让什么都居中 */
224 | caption, th {
225 | text-align: left;
226 | }
227 |
228 | q:before, q:after {
229 | content: '';
230 | }
231 |
232 | /* 统一上标和下标 */
233 | sub, sup {
234 | font-size: 75%;
235 | line-height: 0;
236 | position: relative;
237 | }
238 |
239 | :root sub, :root sup {
240 | vertical-align: baseline; /* for ie9 and other modern browsers */
241 | }
242 |
243 | sup {
244 | top: -0.5em;
245 | }
246 |
247 | sub {
248 | bottom: -0.25em;
249 | }
250 |
251 | /* 让链接在 hover 状态下显示下划线 */
252 | a {
253 | color: #1abc9c;
254 | }
255 |
256 | a:hover {
257 | text-decoration: underline;
258 | }
259 |
260 | #write a {
261 | border-bottom: 1px solid #1abc9c;
262 | }
263 |
264 | #write a:hover {
265 | border-bottom-color: #555;
266 | color: #555;
267 | text-decoration: none;
268 | }
269 |
270 | /* 默认不显示下划线,保持页面简洁 */
271 | ins, a {
272 | text-decoration: none;
273 | }
274 |
275 | /* 标记,类似于手写的荧光笔的作用 */
276 | mark {
277 | background: #fffdd1;
278 | border-bottom: 1px solid #ffedce;
279 | padding: 2px;
280 | margin: 0 5px;
281 | }
282 |
283 | /* 代码片断 */
284 | pre, code, pre tt {
285 | font-family: Courier, 'Courier New', monospace;
286 | }
287 |
288 | #write .md-fences {
289 | border: 1px solid #ddd;
290 | padding: 1em 0.5em;
291 | display: block;
292 | -webkit-overflow-scrolling: touch;
293 | }
294 |
295 | /* 一致化 horizontal rule */
296 | hr {
297 | border: none;
298 | border-bottom: 1px solid #cfcfcf;
299 | margin-bottom: 0.8em;
300 | height: 10px;
301 | }
302 |
303 | #write strong {
304 | font-weight: bolder;
305 | color: #000;
306 | }
307 |
308 | .code-tooltip.md-hover-tip strong {
309 | color: white;
310 | }
311 |
312 | /* 保证块/段落之间的空白隔行 */
313 | #write p, #write .md-fences, #write ul, #write ol, #write dl, #write form, #write hr, #write figure,
314 | #write-p, #write-pre, #write-ul, #write-ol, #write-dl, #write-form, #write-hr, #write-table, blockquote {
315 | margin-bottom: 1.2em
316 | }
317 |
318 | html {
319 | font-family: PingFang SC, Verdana, Helvetica Neue, Microsoft Yahei, Hiragino Sans GB, Microsoft Sans Serif, WenQuanYi Micro Hei, sans-serif;
320 | }
321 |
322 | /* 标题应该更贴紧内容,并与其他块区分,margin 值要相应做优化 */
323 | #write h1, #write h2, #write h3, #write h4, #write h5, #write h6,
324 | #write-h1, #write-h2, #write-h3, #write-h4, #write-h5, #write-h6 {
325 | margin-top: 1.2em;
326 | margin-bottom: 0.6em;
327 | line-height: 1.35;
328 | color: #000;
329 | }
330 |
331 | #write h1, #write-h1 {
332 | font-size: 2.4em;
333 | padding-bottom: 1em;
334 | border-bottom: 3px double #eee;
335 | }
336 |
337 | #write h2, #write-h2 {
338 | font-size: 1.8em;
339 | }
340 |
341 | #write h3, #write-h3 {
342 | font-size: 1.6em;
343 | }
344 |
345 | #write h4, #write-h4 {
346 | font-size: 1.4em;
347 | }
348 |
349 | #write h5, #write h6, #write-h5, #write-h6 {
350 | font-size: 1.2em;
351 | }
352 |
353 | /* 在文章中,应该还原 ul 和 ol 的样式 */
354 | #write ul, #write-ul {
355 | margin-left: 1.3em;
356 | list-style: disc;
357 | }
358 |
359 | #write ol, #write-ol {
360 | list-style: decimal;
361 | margin-left: 1.9em;
362 | }
363 |
364 | #write li ul, #write li ol, #write-ul ul, #write-ul ol, #write-ol ul, #write-ol ol {
365 | margin-bottom: 0.8em;
366 | margin-left: 2em;
367 | }
368 |
369 | #write li ul, #write-ul ul, #write-ol ul {
370 | list-style: circle;
371 | }
372 |
373 |
374 | #write table th, #write table td {
375 | border: 1px solid #ddd;
376 | padding: 0.5em 1em;
377 | color: #666;
378 | }
379 |
380 | #write table .md-table-edit th {
381 | border: none;
382 | padding: 0;
383 | color: inherit;
384 | }
385 |
386 | #write table th, #write-table th {
387 | background: #fbfbfb;
388 | }
389 |
390 | #write table thead th, #write-table thead th {
391 | background: #f1f1f1;
392 | }
393 |
394 | #write table caption {
395 | border-bottom: none;
396 | }
397 |
398 | #write em {
399 | font-weight: inherit;
400 | font-style: inherit;
401 | }
402 |
403 | li>p {
404 | margin-bottom: 0 !important;
405 | }
406 |
407 | /* Responsive images */
408 | #write img {
409 | max-width: 100%;
410 | }
411 |
412 | a.md-toc-inner {
413 | border-bottom: 0 !important;
414 | }
415 |
416 | .md-toc-h1:first-of-type:last-of-type{
417 | display: none;
418 | }
419 |
420 | .md-toc {
421 | font-size: inherit;
422 | }
423 |
424 | .md-toc-h1 .md-toc-inner {
425 | font-weight: normal;
426 | }
427 |
428 | .md-table-edit th {
429 | padding: 0 !important;
430 | border: 0 !important;
431 | }
432 |
433 | .mac-seamless-mode #write {
434 | min-height: calc(100vh - 6em - 20px);
435 | }
436 |
437 | .typora-quick-open-item.active {
438 | color: var(--active-file-text-color);
439 | }
440 |
441 | *.in-text-selection, ::selection {
442 | background: var(--active-file-bg-color);
443 | text-shadow: none;
444 | color: white;
445 | }
446 |
447 | .btn-primary {
448 | background-color: #2d2d2d;
449 | border-color: #020202;
450 | }
451 |
452 | .btn-primary:hover, .btn-primary:focus, .btn-primary.focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary {
453 | background-color: #4e4c4e;
454 | border: #4e4c4e;
455 | }
456 |
457 | #preference-dialog .modal-content{
458 | background: #6e757a;
459 | --bg-color: #6e757a;
460 | --text-color: #f1f1f1;
461 | color: #f1f1f1;
462 | }
463 |
464 | #typora-source,
465 | .typora-sourceview-on {
466 | --bg-color: #eee;
467 | background: #eee;
468 | }
469 |
470 | .cm-s-typora-default .cm-header, .cm-s-typora-default .cm-property {
471 | color: #116098;
472 | }
473 |
474 | .cm-s-typora-default .cm-link {
475 | color: #11987d;
476 | }
477 |
478 | .cm-s-typora-default .cm-em {
479 | font-family: Georgia-Italic, STSongti-SC-Light, serif;
480 | color: #6f6400;
481 | }
482 |
483 | .cm-s-typora-default .cm-em{
484 | color: rgb(0, 22, 45);
485 | }
486 |
487 | .CodeMirror.cm-s-typora-default div.CodeMirror-cursor{
488 | border-left: 3px solid #6e757a;
489 | }
490 |
491 | .cm-s-typora-default .CodeMirror-selectedtext,
492 | .typora-sourceview-on .CodeMirror-focused .CodeMirror-selected {
493 | background: #6e757a;
494 | color: white;
495 | }
496 |
497 | .file-node-icon.fa.fa-folder:before {
498 | color: rgba(32, 43, 51, 0.49);
499 | }
500 |
501 | #preference-dialog .megamenu-menu-panel h1 {
502 | margin-bottom: 1em;
503 | }
504 |
505 | ::-webkit-scrollbar-corner {
506 | display: none;
507 | background: transparent;
508 | }
509 |
510 | /*.file-node-icon.fa.fa-folder:before {
511 | content: "\f114";
512 | }
513 |
514 | #typora-sidebar {
515 |
516 | }*/
517 |
518 | /*.cm-s-typora-default .cm-header, .cm-s-typora-default .cm-property {
519 | color: #fffff1;
520 | }
521 |
522 | .cm-s-typora-default .cm-link {
523 | color: #86f9e2;
524 | color: #e5f7eb;
525 | }
526 |
527 | .cm-s-typora-default .cm-comment, .cm-s-typora-default .cm-code {
528 | color: rgb(255, 199, 199);
529 | }
530 |
531 | .cm-s-typora-default .cm-atom, .cm-s-typora-default .cm-number {
532 | color: #dec4c7;
533 | }
534 |
535 | .cm-s-typora-default .cm-em {
536 | font-family: Georgia-Italic, STSongti-SC-Light, serif;
537 | color: #f3ff7e;
538 | }
539 |
540 | .typora-sourceview-on .CodeMirror-cursor {
541 | border-left: 3px solid #ffffd6;
542 | }
543 |
544 | .typora-sourceview-on #toggle-sourceview-btn {
545 | background: #505050;
546 | }
547 |
548 | .typora-sourceview-on .cm-s-inner .cm-variable,
549 | .typora-sourceview-on .cm-s-inner .cm-operator,
550 | .typora-sourceview-on .cm-s-inner .cm-property {
551 | color: #b8bfc6;
552 | }
553 |
554 | .typora-sourceview-on .cm-s-inner .cm-keyword {
555 | color: #C88FD0;
556 | }
557 |
558 | .typora-sourceview-on .cm-s-inner .cm-tag {
559 | color: #7DF46A;
560 | }
561 |
562 | .typora-sourceview-on .cm-s-inner .cm-attribute {
563 | color: #7575E4;
564 | }
565 |
566 | .typora-sourceview-on .cm-s-inner .cm-string {
567 | color: #D26B6B;
568 | }
569 |
570 | .typora-sourceview-on .cm-s-inner .cm-comment,
571 | .typora-sourceview-on .cm-s-inner.cm-comment {
572 | color: #DA924A;
573 | }
574 |
575 | .typora-sourceview-on .cm-s-inner .cm-header,
576 | .typora-sourceview-on .cm-s-inner .cm-def,
577 | .typora-sourceview-on .cm-s-inner.cm-header,
578 | .typora-sourceview-on .cm-s-inner.cm-def {
579 | color: #8d8df0;
580 | }
581 |
582 | .typora-sourceview-on .cm-s-inner .cm-quote,
583 | .typora-sourceview-on .cm-s-inner.cm-quote {
584 | color: #57ac57;
585 | }
586 |
587 | .typora-sourceview-on .cm-s-inner .cm-hr {
588 | color: #d8d5d5;
589 | }
590 |
591 | .typora-sourceview-on .cm-s-inner .cm-link {
592 | color: #d3d3ef;
593 | }
594 |
595 | .typora-sourceview-on .cm-s-inner .cm-negative {
596 | color: #d95050;
597 | }
598 |
599 | .typora-sourceview-on .cm-s-inner .cm-positive {
600 | color: #50e650;
601 | }
602 |
603 | .typora-sourceview-on .cm-s-inner .cm-string-2 {
604 | color: #f50;
605 | }
606 |
607 | .typora-sourceview-on .cm-s-inner .cm-meta,
608 | .typora-sourceview-on .cm-s-inner .cm-qualifier {
609 | color: #b7b3b3;
610 | }
611 |
612 | .typora-sourceview-on .cm-s-inner .cm-builtin {
613 | color: #f3b3f8;
614 | }
615 |
616 | .typora-sourceview-on .cm-s-inner .cm-bracket {
617 | color: #997;
618 | }
619 |
620 | .typora-sourceview-on .cm-s-inner .cm-atom,
621 | .typora-sourceview-on .cm-s-inner.cm-atom {
622 | color: #84B6CB;
623 | }
624 |
625 | .typora-sourceview-on .cm-s-inner .cm-number {
626 | color: #64AB8F;
627 | }
628 |
629 | .typora-sourceview-on .cm-s-inner .cm-variable {
630 | color: #b8bfc6;
631 | }
632 |
633 | .typora-sourceview-on .cm-s-inner .cm-variable-2 {
634 | color: #9FBAD5;
635 | }
636 |
637 | .typora-sourceview-on .cm-s-inner .cm-variable-3 {
638 | color: #1cc685;
639 | }
640 |
641 | .typora-sourceview-on .CodeMirror div.CodeMirror-cursor {
642 | border-left: 1px solid #b8bfc6;
643 | z-index: 3;
644 | }
645 |
646 | .cm-s-typora-default .CodeMirror-selectedtext,
647 | .typora-sourceview-on .CodeMirror-focused .CodeMirror-selected {
648 | background: #212324;
649 | }
650 |
651 | .typora-sourceview-on .CodeMirror-linenumber {
652 | color: rgb(255, 255, 255);
653 | }*/
--------------------------------------------------------------------------------