├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── Dockerfile ├── README.md ├── conf.d ├── admin.clicli.cc.conf ├── admin.clicli.cc.key ├── admin.clicli.cc.pem ├── api.clicli.cc.conf ├── api.clicli.cc.key ├── api.clicli.cc.pem ├── www.clicli.cc.conf ├── www.clicli.cc.key └── www.clicli.cc.pem ├── package.json ├── packages ├── fre │ ├── .babelrc │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── dist │ │ ├── index.html │ │ ├── main.css │ │ ├── main.js │ │ └── vendors~main.js │ ├── docs │ │ ├── 404.html │ │ └── index.html │ ├── package.json │ ├── src │ │ ├── api │ │ │ └── get.js │ │ ├── component │ │ │ ├── comment-list │ │ │ │ └── index.js │ │ │ ├── footer │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ │ ├── header │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ │ ├── home │ │ │ │ ├── index.js │ │ │ │ ├── index.styl │ │ │ │ └── m.styl │ │ │ ├── page │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ │ ├── post-detail │ │ │ │ ├── index.js │ │ │ │ ├── index.styl │ │ │ │ └── snarkdown.js │ │ │ ├── post-list │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ │ ├── rank │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ │ ├── recommend │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ │ ├── search │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ │ ├── ugc-list │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ │ ├── video-list │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ │ └── week-list │ │ │ │ ├── index.js │ │ │ │ └── index.styl │ │ ├── index.html │ │ ├── index.js │ │ ├── public │ │ │ ├── css │ │ │ │ └── var.styl │ │ │ ├── img │ │ │ │ └── sliders │ │ │ │ │ ├── 0.jpg │ │ │ │ │ ├── 1.jpg │ │ │ │ │ ├── 2.jpg │ │ │ │ │ └── 3.jpg │ │ │ └── js │ │ │ │ ├── const.js │ │ │ │ ├── fetch.js │ │ │ │ └── util.js │ │ ├── use-location.js │ │ └── widget │ │ │ ├── ad │ │ │ └── index.js │ │ │ ├── eplayer │ │ │ ├── index.js │ │ │ └── index.styl │ │ │ ├── search │ │ │ ├── index.js │ │ │ └── index.styl │ │ │ ├── tab │ │ │ ├── index.js │ │ │ └── index.styl │ │ │ └── tag │ │ │ ├── index.js │ │ │ └── index.styl │ ├── webpack.config.js │ └── yarn.lock └── react │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── dist │ ├── index.html │ ├── main.css │ ├── main.js │ ├── runtime~main.js │ └── vendors~main.js │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── api │ │ ├── comment.js │ │ ├── jx.js │ │ ├── post.js │ │ ├── user.js │ │ └── video.js │ ├── app.js │ ├── base │ │ ├── confirm │ │ │ ├── confirm.css │ │ │ └── confirm.js │ │ ├── list-view │ │ │ ├── list-view.js │ │ │ └── listview.css │ │ ├── loading │ │ │ ├── loading.css │ │ │ ├── loading.gif │ │ │ └── loading.js │ │ ├── markdown │ │ │ ├── markdown.css │ │ │ └── markdown.js │ │ ├── reach-box │ │ │ ├── reach-box.css │ │ │ └── reach-box.js │ │ └── top-tip │ │ │ ├── top-tip.css │ │ │ └── top-tip.js │ ├── common │ │ ├── js │ │ │ ├── axios.js │ │ │ └── util.js │ │ └── style │ │ │ ├── iconfont.css │ │ │ ├── index.css │ │ │ └── reset.css │ ├── component │ │ ├── editor-user │ │ │ ├── editor-user.css │ │ │ └── editor-user.js │ │ ├── editor-video │ │ │ └── editor-video.js │ │ ├── header │ │ │ ├── header.css │ │ │ └── header.js │ │ ├── login │ │ │ ├── login.css │ │ │ └── login.js │ │ ├── post-list │ │ │ └── post-list.js │ │ ├── register │ │ │ └── register.js │ │ ├── user-info │ │ │ ├── user-info.css │ │ │ └── user-info.js │ │ ├── user-list │ │ │ ├── user-list.css │ │ │ └── user-list.js │ │ └── write-article │ │ │ ├── wirte-article.js │ │ │ └── write-article.css │ ├── hoc │ │ ├── auth │ │ │ └── auth.js │ │ └── handle-form │ │ │ └── handle-form.js │ ├── index.html │ ├── index.js │ └── store │ │ ├── actions.js │ │ ├── index.js │ │ ├── mutations.js │ │ └── state.js │ └── webpack.config.js ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── yarn.lock /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | jobs: 6 | publish: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Build Dist 11 | run: | 12 | yarn install 13 | yarn build:fre 14 | cd ./packages/react 15 | yarn install 16 | yarn build 17 | - name: Build Image 18 | env: 19 | DockerUsername: ${{ secrets.DOCKER_USERNAME }} 20 | DockerPassword: ${{ secrets.DOCKER_PASSWORD }} 21 | run: | 22 | docker login -u $DockerUsername -p $DockerPassword 23 | docker build -t yisar/clicli_fe . 24 | docker push yisar/clicli_fe 25 | deploy: 26 | runs-on: ubuntu-latest 27 | needs: [publish] 28 | steps: 29 | - uses: appleboy/ssh-action@master 30 | with: 31 | host: ${{ secrets.DEPLOY_HOST }} 32 | username: ${{ secrets.DEPLOY_USERNAME }} 33 | password: ${{ secrets.DEPLOY_PASSWORD }} 34 | port: 22 35 | script: | 36 | docker pull yisar/clicli_fe 37 | docker-compose up -d 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/**/node_modules/ -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | 3 | USER root 4 | 5 | COPY /packages/react/dist/ /usr/share/nginx/html/react/ 6 | COPY /packages/fre/dist/ /usr/share/nginx/html/fre/ 7 | 8 | RUN chmod -R 775 /usr/share/nginx/html -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fre-clicli 2 | 3 | c 站前端 monorepo 4 | 5 | ### run 6 | 7 | 本仓库是使用 pnpm 构建的 monorepo,可以单独打开 packages 下面的文件夹单独进行开发 -------------------------------------------------------------------------------- /conf.d/admin.clicli.cc.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name admin.clicli.cc; 4 | return 301 https://admin.clicli.cc; 5 | } 6 | 7 | server { 8 | listen 443 ssl; 9 | server_name admin.clicli.cc; 10 | ssl_certificate conf.d/admin.clicli.cc.pem; 11 | ssl_certificate_key conf.d/admin.clicli.cc.key; 12 | ssl_session_timeout 5m; 13 | ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; 14 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 15 | ssl_prefer_server_ciphers on; 16 | 17 | location / { 18 | root /usr/share/nginx/html/react; 19 | index index.html; 20 | try_files $uri $uri/ /index.html; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /conf.d/admin.clicli.cc.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEoQIBAAKCAQEAoys1Pe6aTEIeciEi0nFmg3y0k+khq+qYzGZL2ADPaaezNJWC 3 | v0TZU2FuXx0WtVr5ZbQuGrI81KeDY6o52cmDUJQnbkq+d0f7mNlBGwJYoIrMZ/eZ 4 | 768P0jt9WCrPkTONCokVyAe6H5uUCStaKgQ8gAhXLitOawC+gCILvr21MA66/y9m 5 | djYFE1H8xS7qAD7Wu96UTKwDdOomtayfLRcS5LSwlvzsOScrsw448VU8vMYnZlzW 6 | pi+jVjpn332y8yUgRpo30xneMRpLRehVUdxUW8L+iYhCGeYYi0iRUwxfMS0QnceL 7 | epHhL1BSmAdEbqxe6ae2NkiI03y/AeauUzAkOwIDAQABAoIBAAHI3ZkDAxNsDBxm 8 | psemdd76Sv9TJwsguPGDYB3JfhTc8+WfVpuNvtLEPgpl4KaBi2v3v7yLA1KDT1L6 9 | 8A1xwGRUbbOT6oFje1z9abjh50lz1MiBhmCyVkbDLJvKQnrtrwOhr2gaRyIRXlnu 10 | hdq8l4ZokPHwVB5xFteDIWqOQjnd6uCIWBk6wVMAXkrFlmNfCRyfND2bPeph8cHL 11 | CP9LOyGaDujv5gAGDAuEeW6kQQLtyeegabKNWfMgkOWsA77Y64BDU918AaKRQYXM 12 | GBt/KmfE/3JxM84lGODsiELZ8fRmtzshqVc4p/vM5JFqGDbzDBnWGyx5LKs/9tQv 13 | nYeX0AECgYEA5bI75cetZu1jDMVfIPkzRmDx6XtRfBkdfDuHXiW4T/Tfny38M0Ph 14 | /gxGXgiQ9uObPpfDW3hXq8G9Vn0tjW4HhZ7riLkgIS7/SQSYTVc2vKXiC14N9RFC 15 | YatBOvU7zV2XJs+w+feFkdOQDnPm4aqzluPFnW/zHFDqeT6U+u18Ch8CgYEAtdqo 16 | a4xfImDB53L/XviJZDEY7j4cIMdHL//c1DJud38fK8TbY0WKAs6YX4K8ZZGYnJZX 17 | 3Yx4EtZyfN7YNjZOes3Qo2IFeztb1XNOfmkxMvKpzRWIEdaaWUvPfE0G2mrGVJLp 18 | iukxKlaUtL7jzdI8Q2vzvGzH34BYYyNNqNKnGmUCgYACrmLumrcWdXzrgD5NBzum 19 | /ai1PgBEf5KNibBZ0/zjygr2M7Y27vrrWT9VB6qRhzSftWvNXIlBcwB/yvRGK1uh 20 | HqrHLMoIOSE3u/r/JB56c0FBOrbU/n+U3kfjpUsuaYJDLLd3I4GGoz6SPJ4cUj7I 21 | ax46aT0gPn2OLsHUcAM7pwJ/VIhPwa5+inxvpibOjlb5hplL/XLhoFsBvE4zWHAY 22 | 6XMuCip30K2CecuPCNa8vHnxdthr/5BMQHVuwpzT5F9aG7uQQGJQKfhtt8rhseEz 23 | /XrPc4WOurwHA3s5943WobEAgE/KP8gmEogsl9gkDIiteqmzouLuBfajH3JJpzBE 24 | fQKBgQCJjj0DhBGXemklPSGpy4d4lI+mgVNidyy9pgRcvUDP6qxvVNMk7Dc6KZLB 25 | ZEasgeqD9D5is/dLYk4avJPfTM0epIponI3fOyJ9AxHQfda/F/GewEow4pnDE9XQ 26 | gjQTIGAvNnRmwVjXQdXjgo7uX0cH1mR6g4BCyu4Ge/h/xl0zPA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /conf.d/admin.clicli.cc.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIF8jCCBNqgAwIBAgIQBkrPlVpflfuCW+jY8kAW+jANBgkqhkiG9w0BAQsFADBu 3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 4 | d3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg 5 | RFYgVExTIENBIC0gRzEwHhcNMjExMjE5MDAwMDAwWhcNMjIxMjIwMjM1OTU5WjAa 6 | MRgwFgYDVQQDEw9hZG1pbi5jbGljbGkuY2MwggEiMA0GCSqGSIb3DQEBAQUAA4IB 7 | DwAwggEKAoIBAQCjKzU97ppMQh5yISLScWaDfLST6SGr6pjMZkvYAM9pp7M0lYK/ 8 | RNlTYW5fHRa1WvlltC4asjzUp4NjqjnZyYNQlCduSr53R/uY2UEbAligisxn95nv 9 | rw/SO31YKs+RM40KiRXIB7ofm5QJK1oqBDyACFcuK05rAL6AIgu+vbUwDrr/L2Z2 10 | NgUTUfzFLuoAPta73pRMrAN06ia1rJ8tFxLktLCW/Ow5JyuzDjjxVTy8xidmXNam 11 | L6NWOmfffbLzJSBGmjfTGd4xGktF6FVR3FRbwv6JiEIZ5hiLSJFTDF8xLRCdx4t6 12 | keEvUFKYB0RurF7pp7Y2SIjTfL8B5q5TMCQ7AgMBAAGjggLeMIIC2jAfBgNVHSME 13 | GDAWgBRVdE+yck/1YLpQ0dfmUVyaAYca1zAdBgNVHQ4EFgQUUnDHfTKBYgvEX0VB 14 | XUVt/rzUv9UwGgYDVR0RBBMwEYIPYWRtaW4uY2xpY2xpLmNjMA4GA1UdDwEB/wQE 15 | AwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwPgYDVR0gBDcwNTAz 16 | BgZngQwBAgEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20v 17 | Q1BTMIGABggrBgEFBQcBAQR0MHIwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp 18 | Z2ljZXJ0LmNvbTBKBggrBgEFBQcwAoY+aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu 19 | Y29tL0VuY3J5cHRpb25FdmVyeXdoZXJlRFZUTFNDQS1HMS5jcnQwCQYDVR0TBAIw 20 | ADCCAX0GCisGAQQB1nkCBAIEggFtBIIBaQFnAHUARqVV63X6kSAwtaKJafTzfREs 21 | QXS+/Um4havy/HD+bUcAAAF90P8s2gAABAMARjBEAiAEoBOrHJRiAHzuIdlWuN1E 22 | 6/Cqe+rmqynuqFfFAKVYjwIgCmcKu5odDk0KzJINT3NTCAnXvzRQV8ewgd/Ph9j/ 23 | bPoAdQBRo7D1/QF5nFZtuDd4jwykeswbJ8v3nohCmg3+1IsF5QAAAX3Q/y0PAAAE 24 | AwBGMEQCIEPEkdhcK2Y69VirtdNiW0azE124ENdxd8G3QHa2f1vYAiAkiq7hexzS 25 | qWPokT6vMlnGqz2Jn73tMDGvao2nySHvVgB3AEHIyrHfIkZKEMahOglCh15OMYsb 26 | A+vrS8do8JBilgb2AAABfdD/LOwAAAQDAEgwRgIhAMsttMITHy6uER2GTv4gjRE1 27 | RrjVspc/LQlNpHZvicv6AiEA7ZhwWaAt+djjYJ+NEL0NjmrvuJ6ql89YkwaZx15m 28 | PZgwDQYJKoZIhvcNAQELBQADggEBAFe0R6WbmGtKm3CJY2LMRbKwMKVu2zMJGHxL 29 | y7H+4U/CrFuvnqX3A9NWNqRW0LbEmytMJbP1AWItQgxFnNGHuSFwtf1S2s4b14nI 30 | +Dq4xMHT+LtoSXy5PIA6E7OSA2xkPvztxSoZjcjNKkTvmZRMT9f9aTAyDZPV7mz8 31 | KjuhyyeQlMu6EyONXhZdptOuh2coW+ktKgUbtGkOskjt68+2plXo+FutrHTRR3Hq 32 | EDRKd8CALHjctdBHAmaxu9NSd96NKYEN3nhVeVtWOauhgSbNDA/VZx9FbHhwhF6Z 33 | Mr7KRZ8XM+tY6gMvDZuAwicG4Rkazr9GOz4ll3ZWa6RkLL7skLg= 34 | -----END CERTIFICATE----- 35 | -----BEGIN CERTIFICATE----- 36 | MIIEqjCCA5KgAwIBAgIQAnmsRYvBskWr+YBTzSybsTANBgkqhkiG9w0BAQsFADBh 37 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 38 | d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD 39 | QTAeFw0xNzExMjcxMjQ2MTBaFw0yNzExMjcxMjQ2MTBaMG4xCzAJBgNVBAYTAlVT 40 | MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j 41 | b20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH 42 | MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPeP6wkab41dyQh6mKc 43 | oHqt3jRIxW5MDvf9QyiOR7VfFwK656es0UFiIb74N9pRntzF1UgYzDGu3ppZVMdo 44 | lbxhm6dWS9OK/lFehKNT0OYI9aqk6F+U7cA6jxSC+iDBPXwdF4rs3KRyp3aQn6pj 45 | pp1yr7IB6Y4zv72Ee/PlZ/6rK6InC6WpK0nPVOYR7n9iDuPe1E4IxUMBH/T33+3h 46 | yuH3dvfgiWUOUkjdpMbyxX+XNle5uEIiyBsi4IvbcTCh8ruifCIi5mDXkZrnMT8n 47 | wfYCV6v6kDdXkbgGRLKsR4pucbJtbKqIkUGxuZI2t7pfewKRc5nWecvDBZf3+p1M 48 | pA8CAwEAAaOCAU8wggFLMB0GA1UdDgQWBBRVdE+yck/1YLpQ0dfmUVyaAYca1zAf 49 | BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTAOBgNVHQ8BAf8EBAMCAYYw 50 | HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C 51 | AQAwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp 52 | Y2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQu 53 | Y29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG 54 | /WwBAjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT 55 | MAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAQEAK3Gp6/aGq7aBZsxf/oQ+TD/B 56 | SwW3AU4ETK+GQf2kFzYZkby5SFrHdPomunx2HBzViUchGoofGgg7gHW0W3MlQAXW 57 | M0r5LUvStcr82QDWYNPaUy4taCQmyaJ+VB+6wxHstSigOlSNF2a6vg4rgexixeiV 58 | 4YSB03Yqp2t3TeZHM9ESfkus74nQyW7pRGezj+TC44xCagCQQOzzNmzEAP2SnCrJ 59 | sNE2DpRVMnL8J6xBRdjmOsC3N6cQuKuRXbzByVBjCqAA8t1L0I+9wXJerLPyErjy 60 | rMKWaBFLmfK/AHNF4ZihwPGOc7w6UHczBZXH5RFzJNnww+WnKuTPI0HfnVH8lg== 61 | -----END CERTIFICATE----- 62 | -------------------------------------------------------------------------------- /conf.d/api.clicli.cc.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name api.clicli.cc; 4 | return 301 https://api.clicli.cc; 5 | } 6 | server { 7 | listen 443 ssl; 8 | server_name api.clicli.cc; 9 | ssl_certificate conf.d/api.clicli.cc.pem; 10 | ssl_certificate_key conf.d/api.clicli.cc.key; 11 | ssl_session_timeout 5m; 12 | ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; 13 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 14 | ssl_prefer_server_ciphers on; 15 | 16 | location / { 17 | proxy_pass http://45.207.47.90:4000; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /conf.d/api.clicli.cc.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAnM+bapkzTf3choaoOipw0fB3LX3v3eOy9ATuHdektdpoUN/l 3 | trZW7JOfVKWYLkDlLfuF05mJI8oWpfL9nssCgTo/VezxFKmkJ2DTowVNNA5MA3De 4 | U29bci6DbUJRi5xu6KKNWzGgk82w97tcjmGj8/hYZziAGmykn5TAFIyKl97yhSLU 5 | 4EmOeI6u0N3n/hjXWvVUhotavYZgvUtvOlVw35hCn/mCAMxx79TvlfFQO//APJHH 6 | +xTyKkgn75YJV+72oY1iets9g3ClHbnYi7cPC79yR6CumpBNcTdEasRas+O/3NXb 7 | 3ACEvKpHBKEC0eUQtFiifsDdxwsCSzftwmc41wIDAQABAoIBADI4xYha/5T7eKz3 8 | JJrYa+C5GJpPyspweihwCckx/vUlOaKpSrt9Y5KKZw2nqXkq3JuhAkf2VPdK6n0E 9 | uVipoSg9PPqqk72v5TydZLrB4GuLJqo1dGcZc5q6reoIwvApTlYgdZBe8RRVXNfC 10 | wI5zVBy3jaylalXMNN9NN0V9i4e+5XSe43YnH0QbeTbFEDVl+Cg/fA7xaeN97W/o 11 | UsgIC1OvU4YjQ7pS6GeTLAvMxur8kZa2QrIehes15AJSpkuK0h9rjI4OgqD9xS5G 12 | 3bzSY3LuyiwQFYD4G4h7Qzq7Nk3wG85gk9bSVOnK8MPfLfO8pRmlSZxjD13vvEiz 13 | 6AQPARECgYEA2iQGbRftOglxGarh061PGK1rhsD7viClz07+VBYATxT8UPqXWR+I 14 | l/7D1d525klGzxZveVZVdeV7gm21RogUNKCjWp2/ES6CwxWUe6QFpJ315pXEcEVK 15 | ffDpBSKv7i1h9xOGAIU513sHAVpGwijNNmhqg0qv3sGojNPyGvo3SRECgYEAuAa0 16 | rCsY1cBj/uCkzpm1jw9Pdr5zYoWMI175F/2qWeezxHO6SBVz2SF8e9UX92/4Howa 17 | 9btjZQy6XHWeVLYLj3bQ/c/8WZyMep9iBbvc9EbXOQ11prRi5jwJrnvhHMz5RvwI 18 | ZoiaZO3fnS2bTh7WdOi2X9Mme8L0aA54qxBzo2cCgYAGaEp8B9ji/IYC7/8EHbIW 19 | Enn3ElISqpjUNwjHZYIK0J38ru+lIYT8mfIcpRUu/HYE/S63DK8j8GKpWRmbAcI+ 20 | LX2+2qVTFt+mZUrgOtgjlrQzWrDTb2WZv2rIyNUMFO8st7kW6NLjHmz9QC6V42br 21 | szN1ucriqyONR2h0HGdlUQKBgApXfiHKzjt882GoeDqQEs72Wk3fCBb6UFTQiokZ 22 | nVAuIEEjC83vPrJuGlPPNuiV50i4MO+sVzqfi32UspAqJt4qHDaXUKQskwx1Fooh 23 | 85navU+cfQcgTgqIRzcCCTWYI04k7cftV1fislVf8cFjJEQvq8gY0qnT2+5ZGdjr 24 | NMRzAoGBAJErAslN14bqkwUsahcyjWttglt8ouEOgIbfvTWcB4wP4U54z+l39ynX 25 | sajoooI53xNegYO+Fk0WWa6TmQhOwRAvzHYUKET/DbcsP27Je/ps9/NMTxw8tDmM 26 | F5az2WhuwzJ9NvVKldPexXsYQwHVAg4SoDMkLJsmFqYSNuL1qTEo 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /conf.d/api.clicli.cc.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIF8TCCBNmgAwIBAgIQDArv9/L5ibBdSxuy0I6B6TANBgkqhkiG9w0BAQsFADBu 3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 4 | d3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg 5 | RFYgVExTIENBIC0gRzEwHhcNMjExMjE5MDAwMDAwWhcNMjIxMjIwMjM1OTU5WjAY 6 | MRYwFAYDVQQDEw1hcGkuY2xpY2xpLmNjMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 7 | MIIBCgKCAQEAnM+bapkzTf3choaoOipw0fB3LX3v3eOy9ATuHdektdpoUN/ltrZW 8 | 7JOfVKWYLkDlLfuF05mJI8oWpfL9nssCgTo/VezxFKmkJ2DTowVNNA5MA3DeU29b 9 | ci6DbUJRi5xu6KKNWzGgk82w97tcjmGj8/hYZziAGmykn5TAFIyKl97yhSLU4EmO 10 | eI6u0N3n/hjXWvVUhotavYZgvUtvOlVw35hCn/mCAMxx79TvlfFQO//APJHH+xTy 11 | Kkgn75YJV+72oY1iets9g3ClHbnYi7cPC79yR6CumpBNcTdEasRas+O/3NXb3ACE 12 | vKpHBKEC0eUQtFiifsDdxwsCSzftwmc41wIDAQABo4IC3zCCAtswHwYDVR0jBBgw 13 | FoAUVXRPsnJP9WC6UNHX5lFcmgGHGtcwHQYDVR0OBBYEFN8TKiGYvwTlHjd1wDEu 14 | Z2Y0HBuKMBgGA1UdEQQRMA+CDWFwaS5jbGljbGkuY2MwDgYDVR0PAQH/BAQDAgWg 15 | MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA+BgNVHSAENzA1MDMGBmeB 16 | DAECATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMw 17 | gYAGCCsGAQUFBwEBBHQwcjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl 18 | cnQuY29tMEoGCCsGAQUFBzAChj5odHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v 19 | RW5jcnlwdGlvbkV2ZXJ5d2hlcmVEVlRMU0NBLUcxLmNydDAJBgNVHRMEAjAAMIIB 20 | gAYKKwYBBAHWeQIEAgSCAXAEggFsAWoAdwApeb7wnjk5IfBWc59jpXflvld9nGAK 21 | +PlNXSZcJV3HhAAAAX3Q/kKIAAAEAwBIMEYCIQCuqad+IuQkI2LC0ni2nPXKg1ty 22 | ilnW7b0oC4N3cz9G3wIhALkopvo4G0eR+Cl3j/rXt7Uzr69kpHTL1NYh23FsvI2Y 23 | AHcAUaOw9f0BeZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeUAAAF90P5DBQAABAMA 24 | SDBGAiEA6z3pA9Z4nVJMenrlDXAzXrCrY8+YvaG/LY/a8//KyPsCIQCwqg5Z8I5I 25 | lR8A6ZCkGN3ptaNMLyZOSuYFX0XczPEpgQB2AN+lXqtogk8fbK3uuF9OPlrqzaIS 26 | pGpejjsSwCBEXCpzAAABfdD+QrcAAAQDAEcwRQIhAO1qXc3zda5R/yt+qmco9Znw 27 | e3QD9YWMuLJsK8TC0IxkAiBBZnoqd/rKeFvc6RnLJ4/lXvz/NjLUnvhdFIjGbIfZ 28 | uTANBgkqhkiG9w0BAQsFAAOCAQEAWRbjVhN3n7th60nb5cQVXnCuTKAnUJwmbFeS 29 | LZp6glVZfzR0dmNG/0wYSsvFullD/WDArikyEJXhivQAyglY+9ZqbdputZME7i4q 30 | 4rTHOyPkmGdSw6ix38IWcsNiG+n+r2z98wRWhWQ6oZEVBcIjSyg2rS2K3f4m82rf 31 | fzZw/wovA/Vp2UcOc76HfhPKqVeY7sxK8mQsxPoxXHLZlwC4MKaZmLbERplaDXKQ 32 | j6Krf0IwoZsp4Ufno1r/VtG/il7XXVli3CrJplyxRh9kTuRNYaGyPWB15KYaPZ1S 33 | T78h+aW5ZIoF1Ea04Ub4IY17BajyHUF+vMAK1IKuWkj/QtmVmA== 34 | -----END CERTIFICATE----- 35 | -----BEGIN CERTIFICATE----- 36 | MIIEqjCCA5KgAwIBAgIQAnmsRYvBskWr+YBTzSybsTANBgkqhkiG9w0BAQsFADBh 37 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 38 | d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD 39 | QTAeFw0xNzExMjcxMjQ2MTBaFw0yNzExMjcxMjQ2MTBaMG4xCzAJBgNVBAYTAlVT 40 | MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j 41 | b20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH 42 | MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPeP6wkab41dyQh6mKc 43 | oHqt3jRIxW5MDvf9QyiOR7VfFwK656es0UFiIb74N9pRntzF1UgYzDGu3ppZVMdo 44 | lbxhm6dWS9OK/lFehKNT0OYI9aqk6F+U7cA6jxSC+iDBPXwdF4rs3KRyp3aQn6pj 45 | pp1yr7IB6Y4zv72Ee/PlZ/6rK6InC6WpK0nPVOYR7n9iDuPe1E4IxUMBH/T33+3h 46 | yuH3dvfgiWUOUkjdpMbyxX+XNle5uEIiyBsi4IvbcTCh8ruifCIi5mDXkZrnMT8n 47 | wfYCV6v6kDdXkbgGRLKsR4pucbJtbKqIkUGxuZI2t7pfewKRc5nWecvDBZf3+p1M 48 | pA8CAwEAAaOCAU8wggFLMB0GA1UdDgQWBBRVdE+yck/1YLpQ0dfmUVyaAYca1zAf 49 | BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTAOBgNVHQ8BAf8EBAMCAYYw 50 | HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C 51 | AQAwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp 52 | Y2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQu 53 | Y29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG 54 | /WwBAjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT 55 | MAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAQEAK3Gp6/aGq7aBZsxf/oQ+TD/B 56 | SwW3AU4ETK+GQf2kFzYZkby5SFrHdPomunx2HBzViUchGoofGgg7gHW0W3MlQAXW 57 | M0r5LUvStcr82QDWYNPaUy4taCQmyaJ+VB+6wxHstSigOlSNF2a6vg4rgexixeiV 58 | 4YSB03Yqp2t3TeZHM9ESfkus74nQyW7pRGezj+TC44xCagCQQOzzNmzEAP2SnCrJ 59 | sNE2DpRVMnL8J6xBRdjmOsC3N6cQuKuRXbzByVBjCqAA8t1L0I+9wXJerLPyErjy 60 | rMKWaBFLmfK/AHNF4ZihwPGOc7w6UHczBZXH5RFzJNnww+WnKuTPI0HfnVH8lg== 61 | -----END CERTIFICATE----- 62 | -------------------------------------------------------------------------------- /conf.d/www.clicli.cc.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name www.clicli.cc clicli.cc; 4 | return 301 https://www.clicli.cc; 5 | } 6 | server { 7 | listen 443 ssl; 8 | server_name www.clicli.cc clicli.cc; 9 | ssl_certificate conf.d/www.clicli.cc.pem; 10 | ssl_certificate_key conf.d/www.clicli.cc.key; 11 | ssl_session_timeout 5m; 12 | ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; 13 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 14 | ssl_prefer_server_ciphers on; 15 | 16 | location / { 17 | root /usr/share/nginx/html/fre; 18 | index index.html; 19 | try_files $uri $uri/ /index.html; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /conf.d/www.clicli.cc.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAxlWHj88LJXbmclOR+t5spRQ/E7UDHKrDK2cR0wLvW2mu9RIn 3 | B3Vn7L2ic25QcAEAuFT/0S0CWuupAIlHfK4I6een6Ci+eYvzsdk0DhV/LPv9Kwiu 4 | LypxF4jbw6JinE4CGGUGiqUxJbjfdkfFJhafYLkprbh3peZUEQvdNP2ZaJTGkoNW 5 | 09C3qYmlZ3jUzakbZEOj6dtmrnvCwXrEvOjl7tulsjFFRPj78DPVdDjtTk4BPsYD 6 | O1LpmnTxGqqv+s2bBhbN7bXKi6kmhHUUCA4RAmv+JtYpfq90Pi4rhhKNLq4UAU+o 7 | f7KSztaiBpw0T+wx6fD4mxzG1/a0TctGe4+ugwIDAQABAoIBABukZAZpBtar4aZh 8 | e2EJiv7e+YOXoN1n91efP9629AE7k4TYiNQDSv6112RbJZ7WpPEL97OBoGhnm+BV 9 | Qb9gyiE/hilXNGTgWpCZAyZLXLQ6UqiixxQPyD9NdcOoaDH8tVINEoJUT6tAPvTP 10 | P+z1DjV+2Q47PYHDvke57ZSrG6x1gOc4VIENfT7VFqeAqpOHv1LkW8R07zXMJm5P 11 | oTYgBmT9BE0PD12Vfvo8jzVdLqAfyUAUMZCguBlR4CGrzPfoRls7GdcrT2SoBD/7 12 | Uwp2MUxfmkD120jTaxtO8Vb5w1911yqXNzbXscxbVG8VAhBWAWqtooHRY1r7l3y9 13 | uMNkiGECgYEA7jfWa1JIYSogSpyk5AJfFyw/s+RiNeCP1hCTBaL7+6iGJ87zQrkS 14 | 0tK50Buc44kYQ7+V8Pq6tKXGMV8PuLG4riSGRiz/xzsGv3CYV+uel2zvvuBinfCG 15 | s9+RTy3iUXKUgWukzMywCSeYLTgnWXepwYSQ6WWHx4+EEV/TrOoaKCsCgYEA1SOK 16 | HjJFn/nTJL1II5LjsYVSu0Bd5srM646ZK/cxdHnroAXKIkFSnCVkrgQDPAOy7VCf 17 | SwMmrTDxE5pPnG38lyHDD61IsPjesRG+fi1US5qBRkQmaFWGTtbZC30KjhmtM7qK 18 | Wif4QwYAC7FvnBUTlacnTJqmnxVbFJkt39Q9TwkCgYBOgI4jjdkWVAiSLe7UyMdH 19 | Xqibgq6urJMZ6xpYKC677vxO0A8wNMc0PIdJQmBAR2R8pv0ljarLbznR+U64AbEn 20 | ihfJtniP0i7WFafOiXIY2qhjEZTOEX6CdjFvk2JajtTLlYqYhveX/LlUZYXqINoL 21 | UMFkeLX3GJ9gDmE2QWtfQwKBgG75eTGtxTi9cCWB9GypT6qfm2rp4bqIP1gqwC/3 22 | HiXiUT/Zo9xgHK42fgxpBHIcDu6nerko49trTyqAvxDY55qdNp4UAHQ8sF6LuZ6i 23 | 5XAT+1llpIXQegTUst/bdEJ2tgroBp9Yjlf/aMit00n2Y2MZTEoDf9v215yLASq4 24 | rE9ZAoGAPkRxMpCj3LS2JKbl7dL6ZkuxWdvscQ4xI9M03fGyIVoxCfOvy7j7SvWV 25 | HhxYn/7cIuaYAQf6mQswIhs9J3+hPz5t5X1U/6ztBqGpwLBpwBVt+4QUr+GrIXWH 26 | wHTxiMhO0LSj7ILZ3RMoJe9azB2BppZ+3rx+0BI11OKIeo1m6hE= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /conf.d/www.clicli.cc.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIF+TCCBOGgAwIBAgIQDys4pdU9iijh2SIGR1O9RjANBgkqhkiG9w0BAQsFADBu 3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 4 | d3cuZGlnaWNlcnQuY29tMS0wKwYDVQQDEyRFbmNyeXB0aW9uIEV2ZXJ5d2hlcmUg 5 | RFYgVExTIENBIC0gRzEwHhcNMjExMjE5MDAwMDAwWhcNMjIxMjIwMjM1OTU5WjAY 6 | MRYwFAYDVQQDEw13d3cuY2xpY2xpLmNjMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 7 | MIIBCgKCAQEAxlWHj88LJXbmclOR+t5spRQ/E7UDHKrDK2cR0wLvW2mu9RInB3Vn 8 | 7L2ic25QcAEAuFT/0S0CWuupAIlHfK4I6een6Ci+eYvzsdk0DhV/LPv9KwiuLypx 9 | F4jbw6JinE4CGGUGiqUxJbjfdkfFJhafYLkprbh3peZUEQvdNP2ZaJTGkoNW09C3 10 | qYmlZ3jUzakbZEOj6dtmrnvCwXrEvOjl7tulsjFFRPj78DPVdDjtTk4BPsYDO1Lp 11 | mnTxGqqv+s2bBhbN7bXKi6kmhHUUCA4RAmv+JtYpfq90Pi4rhhKNLq4UAU+of7KS 12 | ztaiBpw0T+wx6fD4mxzG1/a0TctGe4+ugwIDAQABo4IC5zCCAuMwHwYDVR0jBBgw 13 | FoAUVXRPsnJP9WC6UNHX5lFcmgGHGtcwHQYDVR0OBBYEFN0GChB/nlcOVLW6B7kE 14 | rClSpb8MMCMGA1UdEQQcMBqCDXd3dy5jbGljbGkuY2OCCWNsaWNsaS5jYzAOBgNV 15 | HQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMD4GA1Ud 16 | IAQ3MDUwMwYGZ4EMAQIBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNl 17 | cnQuY29tL0NQUzCBgAYIKwYBBQUHAQEEdDByMCQGCCsGAQUFBzABhhhodHRwOi8v 18 | b2NzcC5kaWdpY2VydC5jb20wSgYIKwYBBQUHMAKGPmh0dHA6Ly9jYWNlcnRzLmRp 19 | Z2ljZXJ0LmNvbS9FbmNyeXB0aW9uRXZlcnl3aGVyZURWVExTQ0EtRzEuY3J0MAkG 20 | A1UdEwQCMAAwggF9BgorBgEEAdZ5AgQCBIIBbQSCAWkBZwB2AEalVet1+pEgMLWi 21 | iWn0830RLEF0vv1JuIWr8vxw/m1HAAABfdD/MF0AAAQDAEcwRQIhAI8K2pTQIy1O 22 | SQ3Dvm4KvHf9okiJ5ormOaleCqSb2tCxAiBoZyNRrEt0/EFS8DdW2AZnKMtgOPm1 23 | WAzU0d5rZllZPwB2AFGjsPX9AXmcVm24N3iPDKR6zBsny/eeiEKaDf7UiwXlAAAB 24 | fdD/MIIAAAQDAEcwRQIgN1/C7MtT90aWvorS8/TtTGBbUF0/dQBMVxMQkfALx+AC 25 | IQDwHlYShXAVFSSDaKh018bcRdny/gM98RtP2mempUvs2gB1AEHIyrHfIkZKEMah 26 | OglCh15OMYsbA+vrS8do8JBilgb2AAABfdD/MAQAAAQDAEYwRAIgVeq0Ze4RR1il 27 | Dzpw+InHEIE9AJFhpt1jxTWJT99Zrz8CIG+qvGOyLV8PdhwYBGwjVx0dOvctiok8 28 | uWo/sMvjMLmoMA0GCSqGSIb3DQEBCwUAA4IBAQAqveNjOCBFYSkLCtA9XvFady+u 29 | j4rKJgONILJs+Dzroo61Qm0d3XMgRkHIhfPdZjvAeF1LJhH7xCmyS2P64fjMTm4k 30 | UKy3PJB68juFKAjmyxix/5jmZ9s+CLNztLqKm5vSTaeO1fPn9V4kBogSFkcTpw79 31 | ht+sVUx8mAqExqvselV245CUw43+4c3xnIxU8oUif0Ot8pkMWQ8oUgdI8JCrJaaX 32 | cgQfjuZU8NoF9zKKu1P0Oj24LYHgCGCsyJ19f319fPuFIdb9RZCRvu5UOLdV4pn+ 33 | F49q+4bbYHrHmxDTZBuRZqldHikRT7N1PDso2NEgfXPSzRFCqQWz/hIVpQrT 34 | -----END CERTIFICATE----- 35 | -----BEGIN CERTIFICATE----- 36 | MIIEqjCCA5KgAwIBAgIQAnmsRYvBskWr+YBTzSybsTANBgkqhkiG9w0BAQsFADBh 37 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 38 | d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD 39 | QTAeFw0xNzExMjcxMjQ2MTBaFw0yNzExMjcxMjQ2MTBaMG4xCzAJBgNVBAYTAlVT 40 | MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j 41 | b20xLTArBgNVBAMTJEVuY3J5cHRpb24gRXZlcnl3aGVyZSBEViBUTFMgQ0EgLSBH 42 | MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPeP6wkab41dyQh6mKc 43 | oHqt3jRIxW5MDvf9QyiOR7VfFwK656es0UFiIb74N9pRntzF1UgYzDGu3ppZVMdo 44 | lbxhm6dWS9OK/lFehKNT0OYI9aqk6F+U7cA6jxSC+iDBPXwdF4rs3KRyp3aQn6pj 45 | pp1yr7IB6Y4zv72Ee/PlZ/6rK6InC6WpK0nPVOYR7n9iDuPe1E4IxUMBH/T33+3h 46 | yuH3dvfgiWUOUkjdpMbyxX+XNle5uEIiyBsi4IvbcTCh8ruifCIi5mDXkZrnMT8n 47 | wfYCV6v6kDdXkbgGRLKsR4pucbJtbKqIkUGxuZI2t7pfewKRc5nWecvDBZf3+p1M 48 | pA8CAwEAAaOCAU8wggFLMB0GA1UdDgQWBBRVdE+yck/1YLpQ0dfmUVyaAYca1zAf 49 | BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTAOBgNVHQ8BAf8EBAMCAYYw 50 | HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C 51 | AQAwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp 52 | Y2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQu 53 | Y29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG 54 | /WwBAjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT 55 | MAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAQEAK3Gp6/aGq7aBZsxf/oQ+TD/B 56 | SwW3AU4ETK+GQf2kFzYZkby5SFrHdPomunx2HBzViUchGoofGgg7gHW0W3MlQAXW 57 | M0r5LUvStcr82QDWYNPaUy4taCQmyaJ+VB+6wxHstSigOlSNF2a6vg4rgexixeiV 58 | 4YSB03Yqp2t3TeZHM9ESfkus74nQyW7pRGezj+TC44xCagCQQOzzNmzEAP2SnCrJ 59 | sNE2DpRVMnL8J6xBRdjmOsC3N6cQuKuRXbzByVBjCqAA8t1L0I+9wXJerLPyErjy 60 | rMKWaBFLmfK/AHNF4ZihwPGOc7w6UHczBZXH5RFzJNnww+WnKuTPI0HfnVH8lg== 61 | -----END CERTIFICATE----- 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nfes-next", 3 | "version": "1.0.0", 4 | "description": "实验性 no bundle all streams", 5 | "main": "index.js", 6 | "dependencies": { 7 | "fre": "1.13.3", 8 | "pnpm": "^6.24.1", 9 | "blueimp-md5": "^2.12.0", 10 | "devtools-detector": "^1.0.21", 11 | "snarkdown": "^1.2.2" 12 | }, 13 | "devDependencies": { 14 | "@babel/core": "^7.6.4", 15 | "@babel/plugin-transform-react-jsx": "^7.3.0", 16 | "@babel/preset-env": "^7.6.3", 17 | "babel-loader": "^8.0.6", 18 | "clean-webpack-plugin": "^3.0.0", 19 | "cross-env": "^6.0.3", 20 | "css-loader": "^3.2.0", 21 | "html-webpack-plugin": "^3.2.0", 22 | "mini-css-extract-plugin": "^0.8.0", 23 | "style-loader": "^1.0.1", 24 | "stylus": "^0.54.7", 25 | "stylus-loader": "^3.0.2", 26 | "to-string-loader": "^1.1.6", 27 | "webpack": "^4.41.1", 28 | "webpack-cli": "^3.3.9", 29 | "webpack-dev-server": "^3.8.2" 30 | }, 31 | "scripts": { 32 | "test": "echo \"Error: no test specified\" && exit 1", 33 | "build:fre": "pnpm run build --filter @clicli/fre", 34 | "dev:fre": "pnpm run start --filter @clicli/fre", 35 | "build:admin":"pnpm run build --filter @clicli/admin", 36 | "dev:admin":"pnpm run dev --filter @clicli/admin" 37 | }, 38 | "repository": { 39 | "type": "git", 40 | "url": "git@git.dev.sh.ctripcorp.com:changhaozhao/nfes-next" 41 | }, 42 | "keywords": [], 43 | "author": "", 44 | "license": "ISC" 45 | } 46 | -------------------------------------------------------------------------------- /packages/fre/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "@babel/plugin-transform-react-jsx", 5 | { 6 | "pragma": "h" 7 | } 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /packages/fre/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | .idea/ 64 | server/config.js 65 | server/hcy.js 66 | server/san.js -------------------------------------------------------------------------------- /packages/fre/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /packages/fre/README.md: -------------------------------------------------------------------------------- 1 | # fre-clicli-home 2 | > 新版c站首页,fre 强力驱动 3 | 4 | ### 技术栈 5 | * webpack4 6 | * fre 7 | * stylus 8 | 9 | ### 启动 10 | 11 | ```console 12 | yarn start 13 | ``` 14 | -------------------------------------------------------------------------------- /packages/fre/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | c站-clicli弹幕网_(⁄•⁄ω⁄•⁄) 社保~ clicli.cc 8 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/fre/dist/main.css: -------------------------------------------------------------------------------- 1 | .tab { 2 | background: #946ce6; 3 | height: auto; 4 | } 5 | .tab li { 6 | display: inline-block; 7 | padding: 20px; 8 | cursor: pointer; 9 | color: #fff; 10 | border-top: 3px solid #946ce6; 11 | } 12 | .tab li:hover { 13 | border-top-color: #fff; 14 | } 15 | .active { 16 | border-top-color: #fff !important; 17 | } 18 | .tab-right { 19 | float: right; 20 | } 21 | 22 | .post-detail { 23 | width: 100%; 24 | display: flex; 25 | } 26 | .left { 27 | width: 520px; 28 | padding: 20px 10px; 29 | } 30 | article img[alt=suo] { 31 | width: 100%; 32 | border-radius: 2px; 33 | } 34 | article img { 35 | max-width: 100%; 36 | border-radius: 2px; 37 | } 38 | article h1 { 39 | font-size: 18px; 40 | font-weight: bold; 41 | padding: 0 10px; 42 | margin: 20px 0; 43 | border-left: 5px solid #946ce6; 44 | } 45 | article h2 { 46 | font-size: 16px; 47 | font-weight: bold; 48 | padding: 0 10px; 49 | margin: 18px 0; 50 | border-left: 5px solid #946ce6; 51 | } 52 | article h3 { 53 | font-size: 14px; 54 | font-weight: bold; 55 | padding: 0 10px; 56 | margin: 16px 0; 57 | border-left: 5px solid #946ce6; 58 | } 59 | article blockquote { 60 | word-wrap: break-word; 61 | background: rgba(148,108,230,0.1); 62 | color: #946ce6; 63 | padding: 10px; 64 | margin: 10px 0; 65 | } 66 | article a { 67 | text-decoration: none; 68 | color: #946ce6; 69 | padding: 0 5px; 70 | font-weight: bold; 71 | } 72 | .right { 73 | width: 600px; 74 | padding: 10px; 75 | margin: 20px auto; 76 | } 77 | .right .info .user { 78 | display: flex; 79 | align-items: center; 80 | } 81 | .right .info .avatar, 82 | .right .info .avatar img { 83 | width: 60px; 84 | height: 60px; 85 | border-radius: 30px; 86 | } 87 | .right .info span { 88 | padding: 10px; 89 | } 90 | .right .info .uid { 91 | color: #bbb; 92 | } 93 | .right .title { 94 | display: flex; 95 | padding: 10px; 96 | align-items: center; 97 | } 98 | .right .title h1 { 99 | overflow: hidden; 100 | padding: 0; 101 | display: inline-block; 102 | } 103 | .right .title .pv { 104 | background: #946ce6; 105 | padding: 10px 15px !important; 106 | border-radius: 40px 40px 40px 0; 107 | color: #fff; 108 | font-size: 14px; 109 | text-align: center; 110 | margin-left: 20px; 111 | } 112 | .right .copyright { 113 | padding: 10px; 114 | font-size: 20px; 115 | color: #888; 116 | } 117 | .right .other { 118 | padding: 20px 0; 119 | display: none; 120 | } 121 | 122 | .ep { 123 | position: relative; 124 | } 125 | .mark { 126 | background: rgba(0,0,0,0.8); 127 | position: fixed; 128 | top: 0; 129 | bottom: 0; 130 | right: 0; 131 | left: 0; 132 | } 133 | .ep { 134 | width: 800px; 135 | height: 450px; 136 | position: fixed; 137 | top: 50%; 138 | left: 50%; 139 | transform: translate(-50%, -50%); 140 | z-index: 1; 141 | } 142 | .close { 143 | position: absolute; 144 | top: 0; 145 | right: -50px; 146 | z-index: 1; 147 | color: #fff; 148 | cursor: pointer; 149 | padding: 10px; 150 | } 151 | .close .icon-close { 152 | font-size: 24px; 153 | } 154 | video { 155 | width: 100%; 156 | height: 100%; 157 | } 158 | 159 | .video-list { 160 | width: 600px; 161 | float: left; 162 | padding: 10px 0; 163 | } 164 | .video-list .item { 165 | display: flex; 166 | align-items: center; 167 | padding: 10px; 168 | cursor: pointer; 169 | } 170 | .video-list .item img { 171 | width: 40px; 172 | height: 40px; 173 | border-radius: 50%; 174 | margin: 6px 10px; 175 | } 176 | .video-list .item span { 177 | padding: 10px; 178 | } 179 | .video-list .item:nth-of-type(even) { 180 | background: #f4f6fa; 181 | } 182 | .video-list .item2 { 183 | display: inline-block; 184 | background: #f4f6fa; 185 | padding: 10px; 186 | border-radius: 2px; 187 | margin: 10px; 188 | cursor: pointer; 189 | } 190 | 191 | .footer { 192 | background: #f4f6fa; 193 | display: flex; 194 | padding: 30px; 195 | text-align: center; 196 | color: #888; 197 | } 198 | .footer .links a { 199 | display: inline-block; 200 | padding: 20px; 201 | } 202 | .footer .mail { 203 | background: #946ce6; 204 | color: #fff; 205 | border-radius: 2px; 206 | padding: 2px 10px; 207 | margin: 10px 10px 10px 0; 208 | } 209 | 210 | .call { 211 | background: #946ce6; 212 | color: #fff; 213 | margin: 0 auto; 214 | padding: 2px 50px; 215 | display: inline-block; 216 | border-radius: 0 0 2px 2px; 217 | } 218 | * { 219 | padding: 0; 220 | margin: 0; 221 | } 222 | body, 223 | :host { 224 | font: 14px '14px -apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif'; 225 | color: #444; 226 | padding: 0; 227 | margin: 0; 228 | } 229 | a { 230 | text-decoration: none; 231 | color: #444; 232 | } 233 | li { 234 | list-style: none; 235 | } 236 | input, 237 | button { 238 | outline: none; 239 | } 240 | .wrap { 241 | width: 1160px; 242 | margin: 0 auto; 243 | position: relative; 244 | } 245 | h1 { 246 | font-size: 28px; 247 | font-weight: normal; 248 | padding: 30px 0; 249 | } 250 | img { 251 | background: #f4f6fa; 252 | } 253 | 254 | header { 255 | height: 180px; 256 | box-shadow: 2px 2px 0px rgba(5,20,50,0.1); 257 | background: url("https://ae01.alicdn.com/kf/U251ceb70165240ef865c7bed275579feN.jpg") center top; 258 | background-size: auto 100%; 259 | } 260 | .header { 261 | position: absolute; 262 | color: #fff; 263 | top: 0; 264 | left: 0; 265 | right: 0; 266 | text-shadow: 0 1px 1px rgba(0,0,0,0.24); 267 | } 268 | .header .nav { 269 | padding: 20px 0; 270 | } 271 | .header .nav li { 272 | display: inline-block; 273 | cursor: pointer; 274 | padding: 2px 10px; 275 | border-radius: 10px; 276 | transition: 0.3s; 277 | } 278 | .header .nav li:hover { 279 | background: #946ce6; 280 | } 281 | .header .wrap { 282 | display: flex; 283 | align-items: center; 284 | } 285 | .header .logo { 286 | margin: 0 10px; 287 | width: 150px; 288 | height: 86px; 289 | background: url("https://p.pstatp.com/origin/ffe900005cf65f21f262"); 290 | background-size: contain; 291 | } 292 | .header .logo h1 { 293 | font-size: 14px; 294 | display: inline-block; 295 | padding-bottom: 5px; 296 | } 297 | .header a { 298 | color: #fff; 299 | margin: 0 10px; 300 | } 301 | .header li.active { 302 | background: linear-gradient(90deg, #946ce6, #7e5fd9); 303 | } 304 | .header .biu { 305 | position: absolute; 306 | right: 0; 307 | } 308 | .header .biu .login { 309 | padding: 2px 5px; 310 | border-radius: 10px; 311 | transition: 0.3s; 312 | background: transparent; 313 | } 314 | .header .biu .login:hover { 315 | background: #946ce6; 316 | } 317 | .header .biu .user-center { 318 | background: linear-gradient(90deg, #946ce6, #7e5fd9); 319 | border-radius: 2px; 320 | padding: 5px 30px; 321 | margin-left: 10px; 322 | } 323 | .header .call { 324 | background: #fff; 325 | margin: 0 auto; 326 | padding: 2px 10px; 327 | display: inline-block; 328 | border-radius: 10px; 329 | color: #946ce6; 330 | border: 1px solid #946ce6; 331 | margin-left: 10px; 332 | } 333 | 334 | .search { 335 | position: absolute; 336 | top: 50%; 337 | left: 50%; 338 | transform: translate(-50%, -70%); 339 | } 340 | .search input { 341 | padding: 10px 15px; 342 | border-radius: 22px; 343 | border: 3px solid #946ce6; 344 | background: #fff; 345 | width: 300px; 346 | } 347 | .search span { 348 | position: absolute; 349 | right: 10px; 350 | top: 8px; 351 | } 352 | .search .icon-search { 353 | font-size: 24px; 354 | font-weight: bold; 355 | color: #946ce6; 356 | } 357 | 358 | .recommend { 359 | width: 850px; 360 | } 361 | .recommend li { 362 | width: 20%; 363 | display: inline-block; 364 | cursor: pointer; 365 | } 366 | .recommend .title { 367 | width: 150px; 368 | height: 40px; 369 | overflow: hidden; 370 | margin: 10px 0; 371 | } 372 | .recommend .cover, 373 | .recommend .cover img { 374 | width: 150px; 375 | height: 210px; 376 | box-shadow: 2px 2px 0px rgba(5,20,50,0.1); 377 | border-radius: 2px; 378 | object-fit: cover; 379 | } 380 | 381 | .rank { 382 | width: 310px; 383 | } 384 | .rank .title { 385 | padding: 0 10px; 386 | height: 21px; 387 | overflow: hidden; 388 | } 389 | .rank .cover, 390 | .rank .cover img { 391 | width: 150px; 392 | height: 210px; 393 | box-shadow: 2px 2px 0px rgba(5,20,50,0.1); 394 | border-radius: 2px; 395 | object-fit: cover; 396 | } 397 | .rank .current { 398 | display: flex; 399 | position: relative; 400 | padding: 0 7px 14px 7px; 401 | } 402 | .rank .current .title { 403 | font-size: 16px; 404 | transition: 0.3s; 405 | } 406 | .rank .current .bom { 407 | position: absolute; 408 | display: flex; 409 | align-items: baseline; 410 | bottom: 12px; 411 | } 412 | .rank .current .bom .tag { 413 | font-size: 12px; 414 | padding: 0 10px; 415 | color: #888; 416 | } 417 | .rank .current .bom .idx { 418 | font-size: 60px; 419 | color: #946ce6; 420 | font-weight: bold; 421 | position: relative; 422 | bottom: -18px; 423 | } 424 | .rank .current:hover .title { 425 | color: #946ce6; 426 | } 427 | .rank li { 428 | display: flex; 429 | padding: 7px; 430 | cursor: pointer; 431 | } 432 | .rank li span { 433 | font-size: 16px; 434 | font-weight: bold; 435 | color: #888; 436 | } 437 | .rank li .active { 438 | color: #946ce6; 439 | } 440 | .rank li a, 441 | .rank li .info { 442 | width: 0; 443 | flex-grow: 1; 444 | } 445 | .rank li .title { 446 | transition: 0.3s; 447 | overflow: hidden; 448 | text-overflow: ellipsis; 449 | white-space: nowrap; 450 | } 451 | .rank li:hover .title { 452 | color: #946ce6; 453 | } 454 | 455 | .week-list .headline { 456 | display: flex; 457 | align-items: center; 458 | } 459 | .week-list .headline ul, 460 | .week-list .headline button { 461 | background: none; 462 | border: 0; 463 | margin: 10px; 464 | font-size: 16px; 465 | padding: 5px; 466 | } 467 | .week-list .headline ul .active, 468 | .week-list .headline button .active { 469 | color: #946ce6; 470 | position: relative; 471 | } 472 | .week-list .headline ul .active:after, 473 | .week-list .headline button .active:after { 474 | content: ""; 475 | position: absolute; 476 | height: 3px; 477 | width: 10px; 478 | background: #946ce6; 479 | bottom: 0; 480 | left: 50%; 481 | transform: translateX(-50%); 482 | } 483 | .week-list .headline button:active { 484 | transform: scale(0.97); 485 | } 486 | .week-list .posts { 487 | height: 407px; 488 | overflow: auto; 489 | } 490 | .week-list .posts li { 491 | display: inline-block; 492 | width: 20%; 493 | text-align: center; 494 | cursor: pointer; 495 | } 496 | .week-list .posts li .post { 497 | padding-right: 20px; 498 | } 499 | .week-list .posts li .cover, 500 | .week-list .posts li img { 501 | height: 140px; 502 | width: 100%; 503 | object-fit: cover; 504 | box-shadow: 2px 2px 0px rgba(5,20,50,0.1); 505 | border-radius: 2px; 506 | } 507 | .week-list .posts li .title { 508 | height: 40px; 509 | overflow: hidden; 510 | padding: 10px 0; 511 | } 512 | 513 | .ugc-list { 514 | background: #f4f6fa; 515 | padding-bottom: 50px; 516 | } 517 | .ugc-list .posts li { 518 | display: inline-block; 519 | width: 275px; 520 | text-align: center; 521 | margin-right: 20px; 522 | margin-bottom: 20px; 523 | } 524 | .ugc-list .posts li .post { 525 | background: #fff; 526 | border-radius: 2px; 527 | box-shadow: 2px 2px 0px rgba(5,20,50,0.1); 528 | } 529 | .ugc-list .posts li .cover, 530 | .ugc-list .posts li .cover img { 531 | height: 160px; 532 | width: 100%; 533 | object-fit: cover; 534 | } 535 | .ugc-list .posts li .title { 536 | overflow: hidden; 537 | padding-left: 10px; 538 | height: 18px; 539 | } 540 | .ugc-list .posts li .info { 541 | display: flex; 542 | padding: 10px; 543 | align-items: center; 544 | } 545 | .ugc-list .posts li .uqq, 546 | .ugc-list .posts li .uqq img { 547 | height: 40px; 548 | width: 40px; 549 | border-radius: 50%; 550 | } 551 | .ugc-list .posts li:nth-child(4n) { 552 | margin-right: 0 !important; 553 | } 554 | 555 | .post-list { 556 | margin-bottom: 30px !important; 557 | } 558 | .post-list li { 559 | width: 176.6px; 560 | display: inline-block; 561 | margin-right: 20px; 562 | box-sizing: border-box; 563 | cursor: pointer; 564 | } 565 | .post-list .title { 566 | width: 170px; 567 | height: 40px; 568 | overflow: hidden; 569 | padding: 10px 0; 570 | } 571 | .post-list .cover, 572 | .post-list .cover img { 573 | width: 100%; 574 | height: 230px; 575 | box-shadow: 2px 2px 0px rgba(5,20,50,0.1); 576 | border-radius: 2px; 577 | object-fit: cover; 578 | } 579 | .post-list li:nth-child(6n) { 580 | margin-right: 0; 581 | } 582 | 583 | * { 584 | padding: 0; 585 | margin: 0; 586 | } 587 | body { 588 | font: 14px '14px -apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif'; 589 | color: #444; 590 | padding: 0; 591 | margin: 0; 592 | min-width: 1200px; 593 | } 594 | a { 595 | text-decoration: none; 596 | color: #444; 597 | } 598 | li { 599 | list-style: none; 600 | } 601 | input, 602 | button { 603 | outline: none; 604 | } 605 | .wrap { 606 | width: 1160px; 607 | margin: 0 auto; 608 | position: relative; 609 | } 610 | h1 { 611 | font-size: 28px; 612 | font-weight: normal; 613 | padding: 30px 0; 614 | } 615 | img { 616 | background: #f4f6fa; 617 | } 618 | ::-webkit-scrollbar { 619 | background: #fff; 620 | width: 5px; 621 | } 622 | ::-webkit-scrollbar-thumb { 623 | background: #946ce6; 624 | border-radius: 2px; 625 | } 626 | ::-webkit-scrollbar-track { 627 | background: #f4f4fa; 628 | } 629 | 630 | @media screen and (max-width: 480px) { 631 | h1 { 632 | padding: 20px 10px; 633 | font-size: 24px; 634 | } 635 | .rank, 636 | .footer, 637 | .biu, 638 | .logo, 639 | .tab-right { 640 | display: none; 641 | } 642 | body { 643 | min-width: 100%; 644 | } 645 | .wrap { 646 | width: 100%; 647 | } 648 | nav a { 649 | margin: 0 !important; 650 | } 651 | nav .active { 652 | margin-left: 10px; 653 | } 654 | header { 655 | height: 120px; 656 | } 657 | .search { 658 | transform: translate(-50%, 0); 659 | } 660 | .cover, 661 | .cover img { 662 | width: 100% !important; 663 | height: 180px !important; 664 | box-shadow: none !important; 665 | } 666 | .recommend { 667 | box-sizing: border-box; 668 | padding: 1px; 669 | height: 800px; 670 | overflow: hidden; 671 | } 672 | .recommend li { 673 | width: 33.3%; 674 | padding: 1px; 675 | box-sizing: border-box; 676 | } 677 | .recommend .title { 678 | width: 100%; 679 | } 680 | .week-list { 681 | padding-bottom: 20px; 682 | } 683 | .week-list .posts { 684 | height: auto; 685 | padding: 0 10px; 686 | box-sizing: border-box; 687 | } 688 | .week-list .posts li { 689 | width: 25%; 690 | } 691 | .week-list h1 { 692 | display: none; 693 | } 694 | .week-list .headline ul { 695 | display: flex; 696 | } 697 | .week-list .headline button { 698 | margin: 3px !important; 699 | } 700 | .week-list .cover, 701 | .week-list img { 702 | height: 80px !important; 703 | width: 80px !important; 704 | border-radius: 40px !important; 705 | } 706 | .week-list .title { 707 | height: 30px !important; 708 | } 709 | .post-list { 710 | box-sizing: border-box; 711 | padding: 1px; 712 | } 713 | .post-list li { 714 | width: 33.3%; 715 | margin: 0; 716 | box-sizing: border-box; 717 | padding: 1px; 718 | } 719 | .post-list li .title { 720 | width: 100%; 721 | } 722 | .right { 723 | box-sizing: border-box; 724 | width: 100%; 725 | } 726 | .right .info { 727 | width: 100%; 728 | } 729 | .video-list { 730 | width: 100%; 731 | box-sizing: border-box; 732 | } 733 | .ep { 734 | width: 100%; 735 | height: auto; 736 | } 737 | .close { 738 | right: 50%; 739 | transform: translate(50%, 0); 740 | top: -50px; 741 | } 742 | .ugc-list { 743 | padding: 5px 5px 20px 5px; 744 | } 745 | .ugc-list li { 746 | width: 50% !important; 747 | margin: 0 !important; 748 | padding: 5px !important; 749 | box-sizing: border-box; 750 | } 751 | .ugc-list li .post { 752 | box-shadow: none !important; 753 | } 754 | .ugc-list li .post .cover { 755 | height: 120px !important; 756 | } 757 | .ugc-list li .post .cover img { 758 | height: 120px !important; 759 | } 760 | } 761 | 762 | -------------------------------------------------------------------------------- /packages/fre/docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | c站-clicli弹幕网_(⁄•⁄ω⁄•⁄) 社保~ clicli.us 8 | 10 | 11 | 12 | 13 |

c站已更换域名为 clicli.cc 4s

14 | 21 | 22 | -------------------------------------------------------------------------------- /packages/fre/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | c站-clicli弹幕网_(⁄•⁄ω⁄•⁄) 社保~ clicli.us 8 | 10 | 11 | 12 | 13 |

c站已更换域名为 clicli.cc 4s

14 | 21 | 22 | -------------------------------------------------------------------------------- /packages/fre/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@clicli/fre", 3 | "version": "1.0.0", 4 | "description": "clicli new home page with fre", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "cross-env NODE_ENV=production webpack --mode production", 8 | "start": "cross-env NODE_ENV=development webpack-dev-server --mode development" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/cliclitv/fre-clicli-home.git" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/cliclitv/fre-clicli-home/issues" 19 | }, 20 | "homepage": "https://github.com/cliclitv/fre-clicli-home#readme", 21 | "dependencies": {}, 22 | "devDependencies": {} 23 | } -------------------------------------------------------------------------------- /packages/fre/src/api/get.js: -------------------------------------------------------------------------------- 1 | import { get } from '../public/js/fetch' 2 | 3 | export function getPost(type, tag, page, pageSize, status, uid) { 4 | return get(`//api.clicli.cc/posts?status=${status || 'public'}&sort=${type}&tag=${tag}&uid=${uid || ''}&page=${page}&pageSize=${pageSize}`) 5 | } 6 | 7 | export function getRank() { 8 | return get('//api.clicli.cc/rank') 9 | } 10 | 11 | export function getPostDetail(pid) { 12 | return get(`//api.clicli.cc/post/${pid}`) 13 | } 14 | 15 | export function getVideoList(pid) { 16 | return get(`//api.clicli.cc/videos?pid=${pid}&page=1&pageSize=150`) 17 | } 18 | 19 | export function getPlayUrl(url) { 20 | return get(`//api.clicli.cc/play?url=${url}`) 21 | } 22 | 23 | export function getPv(pid) { 24 | return get(`//api.clicli.cc/pv/${pid}`) 25 | } 26 | 27 | export function getSearch(key) { 28 | return get(`//api.clicli.cc/search/posts?key=${key}`) 29 | } 30 | -------------------------------------------------------------------------------- /packages/fre/src/component/comment-list/index.js: -------------------------------------------------------------------------------- 1 | //todo -------------------------------------------------------------------------------- /packages/fre/src/component/footer/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'fre' 2 | import './index.styl' 3 | 4 | export default function Footer() { 5 | const concat = { 6 | 风车动漫: 'https://www.92wuc.com', 7 | APP下载: 'https://app.clicli.cc', 8 | 使用说明: 'https://www.clicli.cc/play/gv31', 9 | github: 'https://github.com/cliclitv', 10 | 微博: 'https://weibo.com/u/6759984025', 11 | qq群: 'https://jq.qq.com/?_wv=1027&k=5e55m5L', 12 | } 13 | return ( 14 |
15 |
16 | 21 |

22 | admin@clicli.us © 2020 clicli Proudly use Fre.js, UGC Sponsored by{' '} 23 | 24 | 31 | 32 | . 33 |

34 |
35 |
36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /packages/fre/src/component/footer/index.styl: -------------------------------------------------------------------------------- 1 | .footer 2 | background $bbb 3 | display flex 4 | padding 30px 5 | text-align center 6 | color #888 7 | .links 8 | a 9 | display inline-block 10 | padding 20px 11 | .mail 12 | background $theme 13 | color #fff 14 | border-radius 2px 15 | padding 2px 10px 16 | margin 10px 10px 10px 0 -------------------------------------------------------------------------------- /packages/fre/src/component/header/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'fre' 2 | import { clink, alink } from 'public/js/const' 3 | import './index.styl' 4 | import Search from 'widget/search' 5 | 6 | export default function Header({ push }) { 7 | const obj = { 8 | 投稿教程: 905, 9 | 使用说明: 31, 10 | 补档: 99 11 | } 12 | return ( 13 |
14 |
15 |
16 | 27 |
28 | 29 | 登录 30 | 31 | 32 | 注册 33 | 34 | 35 | 投稿 36 | 37 |
38 |
39 |
40 |
41 | 42 |
43 |
44 |
45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /packages/fre/src/component/header/index.styl: -------------------------------------------------------------------------------- 1 | header 2 | height 180px 3 | box-shadow $shadow 4 | background url('https://ae01.alicdn.com/kf/U251ceb70165240ef865c7bed275579feN.jpg') center top 5 | background-size auto 100% 6 | .header 7 | position absolute 8 | color #fff 9 | top 0 10 | left 0 11 | right 0 12 | text-shadow 0 1px 1px rgba(0, 0, 0, 0.24) 13 | .nav 14 | padding 20px 0 15 | li 16 | display inline-block 17 | cursor pointer 18 | padding 2px 10px 19 | border-radius 10px 20 | transition 0.3s 21 | li:hover 22 | background $theme 23 | .wrap 24 | display flex 25 | align-items center 26 | .logo 27 | margin 0 10px 28 | width 150px 29 | height 86px 30 | background url('https://p.pstatp.com/origin/ffe900005cf65f21f262') 31 | background-size contain 32 | h1 33 | font-size 14px 34 | display inline-block 35 | padding-bottom 5px 36 | a 37 | color #fff 38 | margin 0 10px 39 | li.active 40 | background $linear 41 | .biu 42 | position absolute 43 | right 0 44 | .login 45 | padding 2px 5px 46 | border-radius 10px 47 | transition 0.3s 48 | background transparent 49 | .login:hover 50 | background $theme 51 | .user-center 52 | background $linear 53 | border-radius 2px 54 | padding 5px 30px 55 | margin-left 10px 56 | .call 57 | background #fff 58 | margin 0 auto 59 | padding 2px 10px 60 | display inline-block 61 | border-radius 10px 62 | color $theme 63 | border 1px solid $theme 64 | margin-left 10px -------------------------------------------------------------------------------- /packages/fre/src/component/home/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'fre' 2 | import Header from 'component/header' 3 | import Recommend from 'component/recommend' 4 | import Footer from 'component/footer' 5 | import Rank from 'component/rank' 6 | import WeekList from 'component/week-list' 7 | import UgcList from 'component/ugc-list' 8 | import PostList from 'component/post-list' 9 | import Ad from 'widget/ad' 10 | import './index.styl' 11 | import './m.styl' 12 | 13 | export default function Home({ push }) { 14 | return ( 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 |
26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /packages/fre/src/component/home/index.styl: -------------------------------------------------------------------------------- 1 | * 2 | padding 0 3 | margin 0 4 | body 5 | font 14px '14px -apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif' 6 | color #444 7 | padding 0 8 | margin 0 9 | min-width 1200px 10 | a 11 | text-decoration none 12 | color #444 13 | li 14 | list-style none 15 | input, button 16 | outline none 17 | .wrap 18 | width 1160px 19 | margin 0 auto 20 | position relative 21 | h1 22 | font-size 28px 23 | font-weight normal 24 | padding 30px 0 25 | img 26 | background #f4f6fa 27 | ::-webkit-scrollbar 28 | background #fff 29 | width 5px 30 | ::-webkit-scrollbar-thumb 31 | background $theme 32 | border-radius 2px 33 | ::-webkit-scrollbar-track 34 | background #f4f4fa 35 | -------------------------------------------------------------------------------- /packages/fre/src/component/home/m.styl: -------------------------------------------------------------------------------- 1 | @media screen and (max-width: 480px) 2 | h1 3 | padding 20px 10px 4 | font-size 24px 5 | .rank, .footer, .biu, .logo, .tab-right 6 | display none 7 | body 8 | min-width 100% 9 | .wrap 10 | width 100% 11 | nav 12 | a 13 | margin 0 !important 14 | .active 15 | margin-left 10px 16 | header 17 | height 120px 18 | .search 19 | transform translate(-50%, 0) 20 | .cover, .cover img 21 | width 100% !important 22 | height 180px !important 23 | box-shadow none !important 24 | .recommend 25 | box-sizing border-box 26 | padding 1px 27 | height 800px 28 | overflow hidden 29 | li 30 | width 33.3% 31 | padding 1px 32 | box-sizing border-box 33 | .title 34 | width 100% 35 | .week-list 36 | padding-bottom 20px 37 | .posts 38 | height auto 39 | padding 0 10px 40 | box-sizing border-box 41 | li 42 | width 25% 43 | h1 44 | display none 45 | .headline 46 | ul 47 | display flex 48 | button 49 | margin 3px !important 50 | .cover, img 51 | height 80px !important 52 | width 80px !important 53 | border-radius 40px !important 54 | .title 55 | height 30px !important 56 | .post-list 57 | box-sizing border-box 58 | padding 1px 59 | li 60 | width 33.3% 61 | margin 0 62 | box-sizing border-box 63 | padding 1px 64 | .title 65 | width 100% 66 | .right 67 | box-sizing border-box 68 | width 100% 69 | .info 70 | width 100% 71 | .video-list 72 | width 100% 73 | box-sizing border-box 74 | .ep 75 | width 100% 76 | height auto 77 | .close 78 | right 50% 79 | transform translate(50%, 0) 80 | top -50px 81 | .ugc-list 82 | padding 5px 5px 20px 5px 83 | li 84 | width 50% !important 85 | margin 0 !important 86 | padding 5px !important 87 | box-sizing border-box 88 | .post 89 | box-shadow none !important 90 | .cover 91 | height 120px !important 92 | img 93 | height 120px !important -------------------------------------------------------------------------------- /packages/fre/src/component/page/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'fre' 2 | import Tab from 'widget/tab' 3 | import PostDetal from 'component/post-detail' 4 | import Footer from '../../component/footer' 5 | import {getAv} from '../../public/js/util' 6 | import './index.styl' 7 | 8 | export default function Page(props) { 9 | const gv = props.gv 10 | return
11 | 12 |
13 | 14 |
15 |
17 | } -------------------------------------------------------------------------------- /packages/fre/src/component/page/index.styl: -------------------------------------------------------------------------------- 1 | .call 2 | background $theme 3 | color #fff 4 | margin: 0 auto 5 | padding: 2px 50px 6 | display inline-block 7 | border-radius 0 0 2px 2px 8 | 9 | * 10 | padding 0 11 | margin 0 12 | 13 | body, :host 14 | font: 14px '14px -apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif' 15 | color: #444 16 | padding 0 17 | margin 0 18 | 19 | a 20 | text-decoration: none 21 | color: #444444 22 | 23 | li 24 | list-style: none 25 | 26 | input, button 27 | outline: none 28 | 29 | .wrap 30 | width: 1160px 31 | margin: 0 auto 32 | position: relative 33 | 34 | 35 | h1 36 | font-size: 28px 37 | font-weight: normal 38 | padding: 30px 0 39 | 40 | img 41 | background: #f4f6fa 42 | 43 | 44 | -------------------------------------------------------------------------------- /packages/fre/src/component/post-detail/index.js: -------------------------------------------------------------------------------- 1 | import { h, useRef, useEffect, useState, useLayout } from 'fre' 2 | import './index.styl' 3 | import VideoList from 'component/video-list' 4 | import { getPostDetail, getPv } from '../../api/get' 5 | import { getAvatar } from '../../public/js/util' 6 | import snarkdown from './snarkdown' 7 | 8 | export default function PostDetal(props) { 9 | const t = useRef(null) 10 | const u = useRef(null) 11 | const [post, setPost] = useState({}) 12 | const [pv, setPv] = useState(0) 13 | useEffect(() => { 14 | getPostDetail(props.gv).then(res => { 15 | setPost(res.result) 16 | getPv(props.gv).then(ret => { 17 | setPv(ret.result.pv) 18 | }) 19 | const w = document.body.clientWidth 20 | if (res.result.tag.indexOf('其它') > -1 || w < 480) { 21 | t.current.style.display = 'none' 22 | u.current.style.display = 'block' 23 | u.current.innerHTML = snarkdown(res.result.content) 24 | } else { 25 | t.current.innerHTML = snarkdown(res.result.content) 26 | } 27 | }) 28 | }, [props.gv]) 29 | 30 | const videos = post.videos ? post.videos.split('\n').map(v => v.split(' ')).filter(i=>i.length>0) : [] 31 | 32 | return ( 33 |
34 |
35 |
36 |
37 |
38 | 39 | {post.uqq} 40 | 41 | {post.uname} 42 | uid:{post.uid} 43 |
44 |
45 |

{post.title || '少年祈祷中……'}

46 | {pv} ℃ 47 |
48 |
49 | {post.tag} 50 | {post.time} 51 |
52 |
53 |
54 | {post.status === 'public' ? :
版权原因,该番剧未上架,请支持正版
} 55 |
56 |
57 | ) 58 | } 59 | -------------------------------------------------------------------------------- /packages/fre/src/component/post-detail/index.styl: -------------------------------------------------------------------------------- 1 | .post-detail 2 | width 100% 3 | display flex 4 | .left 5 | width 520px 6 | padding 20px 10px 7 | article 8 | img[alt=suo] 9 | width 100% 10 | border-radius 2px 11 | img 12 | max-width 100% 13 | border-radius 2px 14 | h1 15 | font-size 18px 16 | font-weight bold 17 | padding 0 10px 18 | margin 20px 0 19 | border-left 5px solid $theme 20 | h2 21 | font-size 16px 22 | font-weight bold 23 | padding 0 10px 24 | margin 18px 0 25 | border-left 5px solid $theme 26 | h3 27 | font-size 14px 28 | font-weight bold 29 | padding 0 10px 30 | margin 16px 0 31 | border-left 5px solid $theme 32 | blockquote 33 | word-wrap break-word 34 | background rgba(148, 108, 230, 0.1) 35 | color $theme 36 | padding 10px 37 | margin 10px 0 38 | a 39 | text-decoration none 40 | color $theme 41 | padding 0 5px 42 | font-weight bold 43 | .right 44 | width 600px 45 | padding 10px 46 | margin 20px auto 47 | .info 48 | .user 49 | display flex 50 | align-items center 51 | .avatar, .avatar img 52 | width 60px 53 | height 60px 54 | border-radius 30px 55 | span 56 | padding 10px 57 | .uid 58 | color #bbb 59 | .title 60 | display flex 61 | padding 10px 62 | align-items center 63 | h1 64 | overflow hidden 65 | padding 0 66 | display inline-block 67 | .pv 68 | background $theme 69 | padding 10px 15px !important 70 | border-radius 40px 40px 40px 0 71 | color #fff 72 | font-size 14px 73 | text-align center 74 | margin-left 20px 75 | .copyright 76 | padding 10px 77 | font-size 20px 78 | color #888 79 | .other 80 | padding 20px 0 81 | display none 82 | -------------------------------------------------------------------------------- /packages/fre/src/component/post-detail/snarkdown.js: -------------------------------------------------------------------------------- 1 | const TAGS = { 2 | _ : ['',''], 3 | '~' : ['',''], 4 | '\n' : ['
'], 5 | ' ' : ['
'], 6 | '-': ['
'] 7 | }; 8 | 9 | /** Outdent a string based on the first indented line's leading whitespace 10 | * @private 11 | */ 12 | function outdent(str) { 13 | return str.replace(RegExp('^'+(str.match(/^(\t| )+/) || '')[0], 'gm'), ''); 14 | } 15 | 16 | /** Encode special attribute characters to HTML entities in a String. 17 | * @private 18 | */ 19 | function encodeAttr(str) { 20 | return (str+'').replace(/"/g, '"').replace(//g, '>'); 21 | } 22 | 23 | /** Parse Markdown into an HTML String. */ 24 | export default function parse(md, prevLinks) { 25 | let tokenizer = /((?:^|\n+)(?:\n---+|\* \*(?: \*)+)\n)|(?:^``` *(\w*)\n([\s\S]*?)\n```$)|((?:(?:^|\n+)(?:\t| {2,}).+)+\n*)|((?:(?:^|\n)([>*+-]|\d+\.)\s+.*)+)|(?:\!\[([^\]]*?)\]\(([^\)]+?)\))|(\[)|(\](?:\(([^\)]+?)\))?)|(?:(?:^|\n+)([^\s].*)\n(\-{3,}|={3,})(?:\n+|$))|(?:(?:^|\n+)(#{1,6})\s*(.+)(?:\n+|$))|(?:`([^`].*?)`)|( \n\n*|\n{2,}|__|\*\*|[_*]|~~)/gm, 26 | context = [], 27 | out = '', 28 | links = prevLinks || {}, 29 | last = 0, 30 | chunk, prev, token, inner, t; 31 | 32 | function tag(token) { 33 | var desc = TAGS[token.replace(/\*/g,'_')[1] || ''], 34 | end = context[context.length-1]==token; 35 | if (!desc) return token; 36 | if (!desc[1]) return desc[0]; 37 | context[end?'pop':'push'](token); 38 | return desc[end|0]; 39 | } 40 | 41 | function flush() { 42 | let str = ''; 43 | while (context.length) str += tag(context[context.length-1]); 44 | return str; 45 | } 46 | 47 | md = md.replace(/^\[(.+?)\]:\s*(.+)$/gm, (s, name, url) => { 48 | links[name.toLowerCase()] = url; 49 | return ''; 50 | }).replace(/^\n+|\n+$/g, ''); 51 | 52 | while ( (token=tokenizer.exec(md)) ) { 53 | prev = md.substring(last, token.index); 54 | last = tokenizer.lastIndex; 55 | chunk = token[0]; 56 | if (prev.match(/[^\\](\\\\)*\\$/)) { 57 | // escaped 58 | } 59 | // Code/Indent blocks: 60 | else if (token[3] || token[4]) { 61 | chunk = '
'+outdent(encodeAttr(token[3] || token[4]).replace(/^\n+|\n+$/g, ''))+'
'; 62 | } 63 | // > Quotes, -* lists: 64 | else if (token[6]) { 65 | t = token[6]; 66 | if (t.match(/\./)) { 67 | token[5] = token[5].replace(/^\d+/gm, ''); 68 | } 69 | console.log(token[5]) 70 | inner = parse(outdent(token[5].replace(/^\s*[>*+.-]/gm, '\n'))); 71 | if (t==='>') t = 'blockquote'; 72 | else { 73 | t = t.match(/\./) ? 'ol' : 'ul'; 74 | inner = inner.replace(/^(.*)(\n|$)/gm, '
  • $1
  • '); 75 | } 76 | chunk = '<'+t+'>' + inner + ''; 77 | } 78 | // Images: 79 | else if (token[8]) { 80 | chunk = `${encodeAttr(token[7])}`; 81 | } 82 | // Links: 83 | else if (token[10]) { 84 | out = out.replace('', ``); 85 | chunk = flush() + ''; 86 | } 87 | else if (token[9]) { 88 | chunk = ''; 89 | } 90 | // Headings: 91 | else if (token[12] || token[14]) { 92 | t = 'h' + (token[14] ? token[14].length : (token[13][0]==='='?1:2)); 93 | chunk = '<'+t+'>' + parse(token[12] || token[15], links) + ''; 94 | } 95 | // `code`: 96 | else if (token[16]) { 97 | chunk = ''+encodeAttr(token[16])+''; 98 | } 99 | // Inline formatting: *em*, **strong** & friends 100 | else if (token[17] || token[1]) { 101 | chunk = tag(token[17] || '--'); 102 | } 103 | out += prev; 104 | out += chunk; 105 | } 106 | 107 | return (out + md.substring(last) + flush()).trim(); 108 | } -------------------------------------------------------------------------------- /packages/fre/src/component/post-list/index.js: -------------------------------------------------------------------------------- 1 | import { h, useEffect, useState } from 'fre' 2 | import { getPost } from 'api/get' 3 | import { getSuo } from 'public/js/util' 4 | import { clink } from 'public/js/const' 5 | import './index.styl' 6 | 7 | export default function PostList(props) { 8 | const [posts, setPosts] = useState([]) 9 | useEffect(() => { 10 | getPost('bgm', '', 1, 30).then(res => { 11 | setPosts(res.posts) 12 | }) 13 | }, []) 14 | return ( 15 |
    16 |

    最新更新

    17 | 30 |
    31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /packages/fre/src/component/post-list/index.styl: -------------------------------------------------------------------------------- 1 | .post-list 2 | margin-bottom 30px!important 3 | li 4 | width 176.6px 5 | display inline-block 6 | margin-right 20px 7 | box-sizing border-box 8 | cursor: pointer 9 | .title 10 | width 170px 11 | height 40px 12 | overflow hidden 13 | padding: 10px 0 14 | .cover,.cover img 15 | width 100% 16 | height 230px 17 | box-shadow $shadow 18 | border-radius 2px 19 | object-fit cover 20 | 21 | li:nth-child(6n) 22 | margin-right 0 -------------------------------------------------------------------------------- /packages/fre/src/component/rank/index.js: -------------------------------------------------------------------------------- 1 | import { h, useEffect, useState } from 'fre' 2 | import { getRank } from 'api/get' 3 | import { getSuo } from 'public/js/util' 4 | import './index.styl' 5 | import { clink } from 'public/js/const' 6 | export default function Rank(props) { 7 | const [posts, setPosts] = useState([]) 8 | useEffect(() => { 9 | getRank().then(res => { 10 | setPosts(res.posts) 11 | }) 12 | }, []) 13 | return ( 14 |
    15 |

    排行榜

    16 | 42 |
    43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /packages/fre/src/component/rank/index.styl: -------------------------------------------------------------------------------- 1 | .rank 2 | width 310px 3 | .title 4 | padding: 0 10px 5 | height 21px 6 | overflow hidden 7 | .cover,.cover img 8 | width 150px 9 | height 210px 10 | box-shadow $shadow 11 | border-radius 2px 12 | object-fit cover 13 | .current 14 | display flex 15 | position relative 16 | padding 0 7px 14px 7px 17 | .title 18 | font-size: 16px 19 | transition: 0.3s 20 | .bom 21 | position absolute 22 | display flex 23 | align-items baseline 24 | bottom 12px 25 | .tag 26 | font-size: 12px 27 | padding 0 10px 28 | color: $grey 29 | .idx 30 | font-size: 60px 31 | color: $theme 32 | font-weight: bold 33 | position: relative 34 | bottom: -18px 35 | .current:hover 36 | .title 37 | color $theme 38 | li 39 | display flex 40 | padding: 7px 41 | cursor pointer 42 | span 43 | font-size:16px 44 | font-weight bold 45 | color: $grey 46 | .active 47 | color: $theme 48 | a,.info 49 | width: 0 50 | flex-grow: 1 51 | .title 52 | transition 0.3s 53 | overflow hidden 54 | text-overflow ellipsis 55 | white-space nowrap 56 | li:hover 57 | .title 58 | color: $theme 59 | -------------------------------------------------------------------------------- /packages/fre/src/component/recommend/index.js: -------------------------------------------------------------------------------- 1 | import {h, useEffect, useState} from 'fre' 2 | import {getPost} from 'api/get' 3 | import {getSuo} from 'public/js/util' 4 | import {clink} from 'public/js/const' 5 | import './index.styl' 6 | 7 | export default function Recommend(props) { 8 | const [posts, setPosts] = useState([]) 9 | useEffect(() => { 10 | getPost('', '推荐', 1, 10).then(res => { 11 | setPosts(res.posts) 12 | }) 13 | }, []) 14 | return
    15 |

    编辑推荐

    16 | 26 |
    27 | } -------------------------------------------------------------------------------- /packages/fre/src/component/recommend/index.styl: -------------------------------------------------------------------------------- 1 | .recommend 2 | width 850px 3 | li 4 | width 20% 5 | display inline-block 6 | cursor: pointer 7 | .title 8 | width 150px 9 | height 40px 10 | overflow hidden 11 | margin: 10px 0 12 | .cover,.cover img 13 | width 150px 14 | height 210px 15 | box-shadow $shadow 16 | border-radius 2px 17 | object-fit cover -------------------------------------------------------------------------------- /packages/fre/src/component/search/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'fre' 2 | import Tab from 'widget/tab' 3 | import Footer from 'component/footer' 4 | import UgcList from 'component/ugc-list' 5 | 6 | export default function List(props) { 7 | return ( 8 |
    9 | 10 | 11 |
    12 |
    13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /packages/fre/src/component/search/index.styl: -------------------------------------------------------------------------------- 1 | .call 2 | background $theme 3 | color #fff 4 | margin: 0 auto 5 | padding: 2px 50px 6 | display inline-block 7 | border-radius 0 0 2px 2px 8 | 9 | * 10 | padding 0 11 | margin 0 12 | 13 | body, :host 14 | font: 14px '14px -apple-system,BlinkMacSystemFont,Helvetica Neue,Helvetica,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif' 15 | color: #444 16 | padding 0 17 | margin 0 18 | 19 | a 20 | text-decoration: none 21 | color: #444444 22 | 23 | li 24 | list-style: none 25 | 26 | input, button 27 | outline: none 28 | 29 | .wrap 30 | width: 1160px 31 | margin: 0 auto 32 | position: relative 33 | 34 | 35 | h1 36 | font-size: 28px 37 | font-weight: normal 38 | padding: 30px 0 39 | 40 | img 41 | background: #f4f6fa 42 | 43 | 44 | -------------------------------------------------------------------------------- /packages/fre/src/component/ugc-list/index.js: -------------------------------------------------------------------------------- 1 | import { h, useEffect, useState } from 'fre' 2 | import { getPost, getSearch } from 'api/get' 3 | import { getAvatar, getSuo } from 'public/js/util' 4 | import './index.styl' 5 | import { clink } from 'public/js/const' 6 | 7 | export default function UGCList(props) { 8 | const [posts, setPosts] = useState([]) 9 | useEffect(() => { 10 | if (props.word) { 11 | getSearch(props.word).then(res => setPosts(res.posts)) 12 | } else { 13 | getPost('原创', '', 2, 4).then(res => setPosts(res.posts)) 14 | } 15 | }, []) 16 | return ( 17 |
    18 |
    19 |

    {props.title}

    20 |
      21 | {posts && 22 | posts.map(item => ( 23 |
    • props.push(`/play/gv${item.id}`)} key={item.id}> 24 |
      25 |
      26 | 27 |
      28 |
      29 |
      30 | 31 |
      32 |
      {item.title}
      33 |
      34 |
      35 |
    • 36 | ))} 37 |
    38 |
    39 |
    40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /packages/fre/src/component/ugc-list/index.styl: -------------------------------------------------------------------------------- 1 | .ugc-list 2 | background $bbb 3 | padding-bottom 50px 4 | .posts 5 | li 6 | display inline-block 7 | width 275px 8 | text-align center 9 | margin-right: 20px 10 | margin-bottom: 20px 11 | .post 12 | background #fff 13 | border-radius 2px 14 | box-shadow $shadow 15 | .cover, .cover img 16 | height 160px 17 | width 100% 18 | object-fit cover 19 | .title 20 | overflow hidden 21 | padding-left 10px 22 | height 18px 23 | .info 24 | display flex 25 | padding 10px 26 | align-items center 27 | .uqq, .uqq img 28 | height 40px 29 | width 40px 30 | border-radius 50% 31 | li:nth-child(4n) 32 | margin-right 0!important -------------------------------------------------------------------------------- /packages/fre/src/component/video-list/index.js: -------------------------------------------------------------------------------- 1 | import { h, useState, useEffect, useRef } from 'fre' 2 | import Eplayer from '../../widget/eplayer' 3 | 4 | import './index.styl' 5 | 6 | export default function VideoList(props) { 7 | const [content, setContent] = useState(null) 8 | 9 | function hide() { 10 | setContent(null) 11 | document.body.style.overflow = 'auto' 12 | } 13 | 14 | function show(url) { 15 | setContent(url) 16 | document.body.style.overflow = 'hidden' 17 | } 18 | return ( 19 |
    20 | {props.videos.length < 27 21 | ? props.videos.map((item,index) => { 22 | return ( 23 |
  • show(item[1])}> 24 | P {index} 25 | {item[0]} 26 |
  • 27 | ) 28 | }) 29 | : props.videos.map((item,index)=>{ 30 | return
  • show(item[1])}>P {index}
  • 31 | })} 32 | {content && } 33 |
    34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /packages/fre/src/component/video-list/index.styl: -------------------------------------------------------------------------------- 1 | .video-list 2 | width 600px 3 | float left 4 | padding 10px 0 5 | .item 6 | display flex 7 | align-items center 8 | padding 10px 9 | cursor pointer 10 | img 11 | width 40px 12 | height 40px 13 | border-radius 50% 14 | margin 6px 10px 15 | span 16 | padding 10px 17 | .item:nth-of-type(even) 18 | background #f4f6fa 19 | .item2 20 | display inline-block 21 | background: #f4f6fa 22 | padding: 10px 23 | border-radius: 2px 24 | margin: 10px 25 | cursor pointer -------------------------------------------------------------------------------- /packages/fre/src/component/week-list/index.js: -------------------------------------------------------------------------------- 1 | import { h, useEffect, useState } from 'fre' 2 | import { getPost } from 'api/get' 3 | import { getSuo } from 'public/js/util' 4 | import './index.styl' 5 | 6 | export default function WeekList({ push }) { 7 | const [posts, setPosts] = useState([]) 8 | const [day, setDay] = useState(new Date().getDay()) 9 | useEffect(() => { 10 | getPost('新番', '', 1, 100, 'nowait').then(res => { 11 | let ret = {} 12 | res.posts.forEach(item => { 13 | let day = new Date(item.time).getDay() 14 | ret[day] = ret[day] || [] 15 | ret[day].push(item) 16 | }) 17 | setPosts(ret) 18 | }) 19 | }, []) 20 | const map = { 21 | 0: '周日', 22 | 1: '周一', 23 | 2: '周二', 24 | 3: '周三', 25 | 4: '周四', 26 | 5: '周五', 27 | 6: '周六' 28 | } 29 | return ( 30 |
    31 |
    32 |
    33 |

    更新表

    34 |
      35 | {map && 36 | Object.keys(map).map((item, index) => ( 37 | 40 | ))} 41 |
    42 |
    43 |
      44 | {posts[day] && 45 | posts[day].map(item => ( 46 |
    • push(`/play/gv${item.id}`)} key={item.id}> 47 |
      48 |
      49 | 50 |
      51 |
      {item.title}
      52 |
      53 |
    • 54 | ))} 55 |
    56 |
    57 |
    58 | ) 59 | } 60 | -------------------------------------------------------------------------------- /packages/fre/src/component/week-list/index.styl: -------------------------------------------------------------------------------- 1 | .week-list 2 | .headline 3 | display flex 4 | align-items center 5 | ul 6 | button 7 | background none 8 | border 0 9 | margin: 10px 10 | font-size 16px 11 | padding: 5px 12 | .active 13 | color: $theme 14 | position relative 15 | .active:after 16 | content "" 17 | position absolute 18 | height 3px 19 | width: 10px 20 | background $theme 21 | bottom: 0 22 | left: 50% 23 | transform: translateX(-50%) 24 | button:active 25 | transform: scale(.97) 26 | .posts 27 | height 407px 28 | overflow: auto 29 | li 30 | display inline-block 31 | width 20% 32 | text-align center 33 | cursor: pointer 34 | .post 35 | padding-right: 20px 36 | .cover,img 37 | height 140px 38 | width 100% 39 | object-fit cover 40 | box-shadow $shadow 41 | border-radius 2px 42 | .title 43 | height 40px 44 | overflow hidden 45 | padding: 10px 0 46 | -------------------------------------------------------------------------------- /packages/fre/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | c站-clicli弹幕网_(⁄•⁄ω⁄•⁄) 社保~ clicli.cc 8 | 12 | 13 | 14 | 15 | 16 | 17 |
    18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/fre/src/index.js: -------------------------------------------------------------------------------- 1 | import { h, render, useEffect } from 'fre' 2 | import { useLocation } from './use-location' 3 | import Page from 'component/page' 4 | import Home from 'component/home' 5 | import Search from 'component/search' 6 | 7 | const App = () => { 8 | const [location, setLocation] = useLocation() 9 | const p = location.match(/gv(\S*)+/) 10 | const s = location.match(/search\/(\S*)+/) 11 | if (location === '/') { 12 | return 13 | } else if (p) { 14 | return 15 | } else if (s) { 16 | return 17 | } else { 18 | return 404 19 | } 20 | } 21 | // @ts-ignore 22 | const root = document.getElementById('root') 23 | render(, root) 24 | 25 | // if (new Date().getTime() < new Date('2020-04-04').setHours(23, 59, 59, 999)) root.style = 'filter:grayscale(100%)' 26 | -------------------------------------------------------------------------------- /packages/fre/src/public/css/var.styl: -------------------------------------------------------------------------------- 1 | $theme = #946ce6 2 | $bg = #001935 3 | $grey = #888 4 | $bbb = #f4f6fa 5 | $shadow = 2px 2px 0px rgba(5,20,50,.1) 6 | $linear = linear-gradient(90deg,#946ce6,#7e5fd9) 7 | -------------------------------------------------------------------------------- /packages/fre/src/public/img/sliders/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cliclitv/fre-clicli/a60d1f6b647c5da69f29e046cee52d68adf1b072/packages/fre/src/public/img/sliders/0.jpg -------------------------------------------------------------------------------- /packages/fre/src/public/img/sliders/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cliclitv/fre-clicli/a60d1f6b647c5da69f29e046cee52d68adf1b072/packages/fre/src/public/img/sliders/1.jpg -------------------------------------------------------------------------------- /packages/fre/src/public/img/sliders/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cliclitv/fre-clicli/a60d1f6b647c5da69f29e046cee52d68adf1b072/packages/fre/src/public/img/sliders/2.jpg -------------------------------------------------------------------------------- /packages/fre/src/public/img/sliders/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cliclitv/fre-clicli/a60d1f6b647c5da69f29e046cee52d68adf1b072/packages/fre/src/public/img/sliders/3.jpg -------------------------------------------------------------------------------- /packages/fre/src/public/js/const.js: -------------------------------------------------------------------------------- 1 | export const clink = 'https://www.clicli.cc' 2 | export const alink = 'https://admin.clicli.cc' 3 | export const isDev = process.env.NODE_ENV === 'development' -------------------------------------------------------------------------------- /packages/fre/src/public/js/fetch.js: -------------------------------------------------------------------------------- 1 | export function get(url) { 2 | return new Promise((resolve,reject) => { 3 | fetch(url) 4 | .then(res => res.json()) 5 | .then(data => { 6 | resolve(data) 7 | }).catch(e=>{ 8 | reject(e) 9 | }) 10 | }) 11 | } -------------------------------------------------------------------------------- /packages/fre/src/public/js/util.js: -------------------------------------------------------------------------------- 1 | import md5 from 'blueimp-md5' 2 | 3 | export function getAvatar(avatar) { 4 | if (/^[0-9]+$/.test(avatar)) { 5 | return `http://q1.qlogo.cn/g?b=qq&nk=${avatar}&s=640` 6 | } else { 7 | let hash = md5(avatar) 8 | return `https://sdn.geekzu.org/avatar/${hash}?s=100&d=retro` 9 | } 10 | } 11 | 12 | export function getSuo(content) { 13 | let m = content.match(/suo(.+?)\)/i) 14 | return m ? m[1].slice(2) : 'https://wx4.sinaimg.cn/mw690/0060lm7Tly1fvmtrka9p5j30b40b43yo.jpg' 15 | } 16 | 17 | export function getAv(id) { 18 | return id.substring(2, id.length) 19 | } -------------------------------------------------------------------------------- /packages/fre/src/use-location.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from 'fre' 2 | // wouter's implement for fre https://github.com/molefrog/wouter/blob/master/use-location.js 3 | 4 | export const useLocation = ({ base = '' } = {}) => { 5 | const [path, update] = useState(currentPathname(base)) 6 | const prevPath = useRef(path) 7 | 8 | useEffect(() => { 9 | patchHistoryEvents() 10 | const checkForUpdates = () => { 11 | const pathname = currentPathname(base) 12 | prevPath.current !== pathname && update((prevPath.current = pathname)) 13 | } 14 | 15 | const events = ['popstate', 'pushState', 'replaceState'] 16 | events.map(e => addEventListener(e, checkForUpdates)) 17 | checkForUpdates() 18 | 19 | return () => events.map(e => removeEventListener(e, checkForUpdates)) 20 | }, []) 21 | const navigate = (to, replace) => 22 | history[replace ? 'replaceState' : 'pushState'](0, 0, base + to) 23 | return [path, navigate] 24 | } 25 | 26 | let patched = 0 27 | 28 | const patchHistoryEvents = () => { 29 | if (patched) return 30 | ['pushState', 'replaceState'].map(type => { 31 | const original = history[type] 32 | 33 | history[type] = function() { 34 | const result = original.apply(this, arguments) 35 | const event = new Event(type) 36 | event.arguments = arguments 37 | 38 | dispatchEvent(event) 39 | return result 40 | } 41 | }) 42 | 43 | return (patched = 1) 44 | } 45 | 46 | const currentPathname = (base, path = location.pathname) => 47 | !path.indexOf(base) ? path.slice(base.length) || '/' : path 48 | -------------------------------------------------------------------------------- /packages/fre/src/widget/ad/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'fre' 2 | 3 | export default function Ad(props) { 4 | return ( 5 |
    6 | 7 |
    11 |
    12 |
    13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /packages/fre/src/widget/eplayer/index.js: -------------------------------------------------------------------------------- 1 | import { h, useEffect, useRef } from 'fre' 2 | import { getPlayUrl } from '../../api/get' 3 | import './index.styl' 4 | 5 | export default function Eplayer(props) { 6 | const t = useRef(null) 7 | const s = useRef(null) 8 | const isMobile = document.body.clientWidth < 480 9 | const steam = (type, url) => { 10 | switch (type) { 11 | case 'm3u8': 12 | if (Hls.isSupported()) { 13 | let hls = new Hls() 14 | hls.loadSource(url) 15 | hls.attachMedia(s.current) 16 | } 17 | break 18 | default: 19 | s.current.src = url 20 | } 21 | } 22 | useEffect(() => { 23 | getPlayUrl(props.url).then(res => { 24 | if (isMobile) { 25 | steam(res.result.mtype, res.result.url) 26 | s.current.load() 27 | } else { 28 | const type = res.result.mtype === "m3u8" ? "hls" : res.result.mtype 29 | t.current.setAttribute('type', type) 30 | t.current.setAttribute('src', res.result.url) 31 | } 32 | }) 33 | }, [props.url]) 34 | 35 | return ( 36 |
    37 |
    38 | {isMobile ?
    43 |
    44 |
    45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /packages/fre/src/widget/eplayer/index.styl: -------------------------------------------------------------------------------- 1 | .ep 2 | position relative 3 | .mark 4 | background rgba(0, 0, 0, 0.8) 5 | position fixed 6 | top 0 7 | bottom 0 8 | right 0 9 | left 0 10 | .ep 11 | width 800px 12 | height 450px 13 | position fixed 14 | top 50% 15 | left 50% 16 | transform translate(-50%, -50%) 17 | z-index 1 18 | .close 19 | position absolute 20 | top 0 21 | right -50px 22 | z-index 1 23 | color #fff 24 | cursor pointer 25 | padding 10px 26 | .icon-close 27 | font-size 24px 28 | video 29 | width 100% 30 | height 100% -------------------------------------------------------------------------------- /packages/fre/src/widget/search/index.js: -------------------------------------------------------------------------------- 1 | import { h, useState, useEffect } from 'fre' 2 | import { clink } from 'public/js/const' 3 | import './index.styl' 4 | 5 | export default function Search({ push }) { 6 | const [word, setWord] = useState('') 7 | useEffect(() => { 8 | document.onkeydown = e => { 9 | if (e.keyCode == 13 && word) { 10 | push(`/search/${word}`) 11 | } 12 | } 13 | }) 14 | const inputWord = e => { 15 | setWord(e.target.value) 16 | } 17 | return ( 18 |
    19 | 20 | push(`/search/${word}`)}> 21 | 22 | 23 |
    24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /packages/fre/src/widget/search/index.styl: -------------------------------------------------------------------------------- 1 | .search 2 | position absolute 3 | top 50% 4 | left 50% 5 | transform translate(-50%, -70%) 6 | input 7 | padding 10px 15px 8 | border-radius 22px 9 | border 3px solid $theme 10 | background #fff 11 | width 300px 12 | span 13 | position absolute 14 | right 10px 15 | top: 8px 16 | .icon-search 17 | font-size: 24px 18 | font-weight: bold 19 | color: $theme -------------------------------------------------------------------------------- /packages/fre/src/widget/tab/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'fre' 2 | import './index.styl' 3 | import { clink } from 'public/js/const' 4 | 5 | export default function Tab() { 6 | const obj = { 7 | 投稿教程: 905, 8 | 使用说明: 31, 9 | 补档: 99 10 | } 11 | return ( 12 | 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /packages/fre/src/widget/tab/index.styl: -------------------------------------------------------------------------------- 1 | .tab 2 | background $theme 3 | height auto 4 | li 5 | display inline-block 6 | padding 20px 7 | cursor: pointer 8 | color #fff 9 | border-top 3px solid $theme 10 | li:hover 11 | border-top-color #fff 12 | .active 13 | border-top-color #fff!important 14 | .tab-right 15 | float right -------------------------------------------------------------------------------- /packages/fre/src/widget/tag/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'fre' 2 | import './index.styl' 3 | 4 | 5 | export default function Tag () { 6 | const arr = [ 7 | '耽美', 8 | '乙女', 9 | '百合', 10 | '后宫', 11 | '热血', 12 | '运动', 13 | '冒险', 14 | '战斗', 15 | 'r15', 16 | '治愈', 17 | '推理', 18 | '特摄' 19 | ] 20 | return ( 21 |
    22 |
    23 | {arr.map(item => ( 24 | {item} 25 | ))} 26 | 更多> 27 |
    28 |
    29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /packages/fre/src/widget/tag/index.styl: -------------------------------------------------------------------------------- 1 | .tags 2 | background $bg 3 | padding 10px 4 | .wrap 5 | display flex 6 | a 7 | color #fff 8 | display inline-block 9 | padding 10px 0 10 | flex 1 -------------------------------------------------------------------------------- /packages/fre/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 5 | 6 | module.exports = { 7 | entry: './src/index.js', 8 | output: { 9 | path: path.resolve(__dirname, 'dist'), 10 | filename: '[name].js', 11 | publicPath: '/' 12 | }, 13 | resolve: { 14 | alias: { 15 | component: path.resolve(__dirname, 'src/component'), 16 | public: path.resolve(__dirname, 'src/public'), 17 | api: path.resolve(__dirname, 'src/api'), 18 | widget: path.resolve(__dirname, 'src/widget') 19 | } 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.js$/, 25 | use: { 26 | loader: 'babel-loader' 27 | } 28 | }, 29 | { 30 | test: /\.styl$/, 31 | use: [MiniCssExtractPlugin.loader, 'css-loader', { 32 | loader: 'stylus-loader', 33 | options: { 34 | import: [path.resolve(__dirname, 'src/public/css/var.styl')] 35 | } 36 | }] 37 | }, 38 | { 39 | test: /\.css$/, 40 | use: [MiniCssExtractPlugin.loader, 'css-loader'] 41 | } 42 | ] 43 | }, 44 | optimization: { 45 | splitChunks: { 46 | chunks: 'all' 47 | } 48 | }, 49 | plugins: [ 50 | new HtmlWebpackPlugin({ 51 | template: './src/index.html' 52 | }), 53 | new CleanWebpackPlugin({ 54 | cleanOnceBeforeBuildPatterns: ['*/**', '!CNAME'] 55 | }), 56 | 57 | new MiniCssExtractPlugin({ 58 | filename: '[name].css', 59 | }) 60 | ], 61 | devServer: { 62 | headers: { 'Access-Control-Allow-Origin': '*' }, 63 | contentBase: path.join(__dirname, 'dist'), 64 | compress: true, 65 | port: 8080, 66 | historyApiFallback: true, 67 | hot: true 68 | } 69 | } -------------------------------------------------------------------------------- /packages/react/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | [ 8 | "@babel/plugin-proposal-decorators", 9 | { 10 | "legacy": true 11 | } 12 | ], 13 | "@babel/plugin-syntax-dynamic-import", 14 | "@babel/plugin-transform-runtime" 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/react/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | yarn-error.log 4 | 5 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | 2 | # emmm -------------------------------------------------------------------------------- /packages/react/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | clicli后台 6 | 7 | 8 | 9 |
    10 |
    11 | 12 | -------------------------------------------------------------------------------- /packages/react/dist/runtime~main.js: -------------------------------------------------------------------------------- 1 | !function(e){function r(r){for(var t,o,i=r[0],c=r[1],d=r[2],a=0,l=[];a=0&&n._disposeHandlers.splice(r,1)},invalidate:function(){switch(this._selfInvalidated=!0,u){case"idle":(v={})[r]=e[r],f("ready");break;case"ready":I(r);break;case"prepare":case"check":case"dispose":case"apply":(b=b||[]).push(r)}},check:D,apply:x,status:function(e){if(!e)return u;p.push(e)},addStatusHandler:function(e){p.push(e)},removeStatusHandler:function(e){var r=p.indexOf(e);r>=0&&p.splice(r,1)},data:d[r]};return o=void 0,n}var p=[],u="idle";function f(e){u=e;for(var r=0;r0;){var o=t.pop(),i=o.id,c=o.chain;if((s=k[i])&&(!s.hot._selfAccepted||s.hot._selfInvalidated)){if(s.hot._selfDeclined)return{type:"self-declined",chain:c,moduleId:i};if(s.hot._main)return{type:"unaccepted",chain:c,moduleId:i};for(var d=0;d ")),E.type){case"self-declined":n.onDeclined&&n.onDeclined(E),n.ignoreDeclined||(P=new Error("Aborted because of self decline: "+E.moduleId+A));break;case"declined":n.onDeclined&&n.onDeclined(E),n.ignoreDeclined||(P=new Error("Aborted because of declined dependency: "+E.moduleId+" in "+E.parentId+A));break;case"unaccepted":n.onUnaccepted&&n.onUnaccepted(E),n.ignoreUnaccepted||(P=new Error("Aborted because "+p+" is not accepted"+A));break;case"accepted":n.onAccepted&&n.onAccepted(E),x=!0;break;case"disposed":n.onDisposed&&n.onDisposed(E),I=!0;break;default:throw new Error("Unexception type "+E.type)}if(P)return f("abort"),Promise.reject(P);if(x)for(p in m[p]=v[p],h(O,E.outdatedModules),E.outdatedDependencies)Object.prototype.hasOwnProperty.call(E.outdatedDependencies,p)&&(w[p]||(w[p]=[]),h(w[p],E.outdatedDependencies[p]));I&&(h(O,[E.moduleId]),m[p]=g)}var U,q=[];for(i=0;i0;)if(p=J.pop(),s=k[p]){var L={},N=s.hot._disposeHandlers;for(l=0;l=0&&X.parents.splice(U,1))}}for(p in w)if(Object.prototype.hasOwnProperty.call(w,p)&&(s=k[p]))for(T=w[p],l=0;l=0&&s.children.splice(U,1);f("apply"),void 0!==y&&(c=y,y=void 0);for(p in v=void 0,m)Object.prototype.hasOwnProperty.call(m,p)&&(e[p]=m[p]);var C=null;for(p in w)if(Object.prototype.hasOwnProperty.call(w,p)&&(s=k[p])){T=w[p];var G=[];for(i=0;i", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "cross-env NODE_ENV=production webpack --mode production", 10 | "start": "cross-env NODE_ENV=development webpack-dev-server --mode development" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.21.2", 14 | "blueimp-md5": "^2.10.0", 15 | "js-base64": "^2.4.9", 16 | "js-cookie": "^2.2.0", 17 | "react": "^16.5.2", 18 | "react-dom": "^16.5.2", 19 | "react-loadable": "^5.5.0", 20 | "react-router-dom": "^4.3.1", 21 | "simplemde": "^1.11.2", 22 | "smox": "^1.3.3" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.1.0", 26 | "@babel/plugin-proposal-decorators": "^7.1.0", 27 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 28 | "@babel/plugin-transform-runtime": "^7.2.0", 29 | "@babel/preset-env": "^7.1.0", 30 | "@babel/preset-react": "^7.0.0", 31 | "@babel/runtime": "^7.3.1", 32 | "babel-loader": "^8.0.2", 33 | "cross-env": "^5.2.0", 34 | "css-loader": "^1.0.0", 35 | "file-loader": "^2.0.0", 36 | "html-webpack-plugin": "^3.2.0", 37 | "mini-css-extract-plugin": "^0.4.3", 38 | "style-loader": "^0.23.0", 39 | "url-loader": "^1.1.1", 40 | "webpack": "^4.20.2", 41 | "webpack-cli": "^3.1.1", 42 | "webpack-dev-server": "^3.1.9" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/react/src/api/comment.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import {isDev, API_LINK} from 'common/js/util' 3 | 4 | 5 | // 根据pid查找评论 6 | export function getCommentByPid(pid, page, pageSize) { 7 | return request.get('/comments', { 8 | params: {pid, page, pageSize} 9 | }) 10 | } 11 | 12 | // 根据 uid 查找评论 13 | export function getCommentByUid(uid, page, pageSize) { 14 | return request.get('/comments', { 15 | params: {uid, page, pageSize} 16 | }) 17 | } 18 | 19 | 20 | // 根据pid删除评论 21 | export function deleteCommentByPid(pid) { 22 | return axios.post(`/comment/delete?pid=${pid}`) 23 | } -------------------------------------------------------------------------------- /packages/react/src/api/jx.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export function getHcyList(fid) { 4 | return axios.get('/hcy/list', { 5 | params: { 6 | fid 7 | } 8 | }) 9 | 10 | } -------------------------------------------------------------------------------- /packages/react/src/api/post.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import {isDev, API_LINK} from 'common/js/util' 3 | import Cookies from 'js-cookie' 4 | 5 | axios.defaults.withCredentials = true 6 | 7 | export function getPosts(status, sort, tag, uid, page, pageSize) { 8 | return axios.get(`//api.clicli.cc/posts`, { 9 | params: { 10 | status, sort, tag, uid, page, pageSize 11 | } 12 | }) 13 | } 14 | 15 | export function getPost(id) { 16 | return axios.get(`//api.clicli.cc/post/${id}`) 17 | } 18 | 19 | export function add({title, content, status, sort, tag, uid, videos}) { 20 | return axios.post('//api.clicli.cc/post/add', { 21 | title, 22 | content, 23 | status, 24 | sort, 25 | tag, 26 | uid, 27 | videos 28 | }, { 29 | headers: { 30 | 'token': Cookies.get('token') 31 | } 32 | }) 33 | } 34 | 35 | // 更新文章 36 | export function update({id, title, content, status, sort, tag, uid, time,videos}) { 37 | return axios.post(`//api.clicli.cc/post/update/${id}`, { 38 | title, 39 | content, 40 | status, 41 | sort, 42 | tag, 43 | uid, 44 | time, 45 | videos 46 | }, { 47 | headers: { 48 | 'token': Cookies.get('token') 49 | } 50 | }) 51 | } 52 | 53 | // 删除一篇文章 54 | export function deletePost(id) { 55 | return axios.post(`//api.clicli.cc/post/delete/${id}`, {}, { 56 | headers: { 57 | 'token': Cookies.get('token') 58 | } 59 | }) 60 | } 61 | 62 | -------------------------------------------------------------------------------- /packages/react/src/api/user.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import Cookies from "js-cookie" 3 | 4 | axios.defaults.withCredentials = true 5 | 6 | // 用户登录 7 | export function login(user) { 8 | return axios.post('//api.clicli.cc/user/login', { 9 | name: user.name, 10 | pwd: user.pwd 11 | }) 12 | } 13 | 14 | 15 | // 用户注册 16 | export function register(user) { 17 | return axios.post('//api.clicli.cc/user/register', { 18 | name: user.name, 19 | pwd: user.pwd, 20 | qq: user.qq, 21 | level: 1, 22 | desc: '人懒,竟然没有签名~' 23 | }) 24 | } 25 | 26 | // 更新用户信息 27 | export function update(user) { 28 | return axios.post(`//api.clicli.cc/user/update/${user.id}`, { 29 | name: user.name, 30 | pwd: user.pwd, 31 | qq: user.qq, 32 | level: parseInt(user.level), 33 | desc: user.desc 34 | }, { 35 | headers: { 36 | 'token': Cookies.get('token') 37 | } 38 | }) 39 | } 40 | 41 | // 获取用户列表 42 | export function userList(level) { 43 | return axios.get('//api.clicli.cc/users', { 44 | params: { 45 | level, 46 | page: 1, 47 | pageSize: 100 48 | } 49 | }) 50 | } 51 | 52 | 53 | // 用户退出 54 | export function logout() { 55 | return axios.post('//api.clicli.cc/user/logout') 56 | } 57 | 58 | // 获取用户cookie 59 | export function getCookie(uid) { 60 | return axios.get(`//api.clicli.cc/cookie/${uid}`) 61 | } 62 | 63 | export function getUser(uname, uid, uqq) { 64 | return axios.get('//api.clicli.cc/user', { 65 | params: { 66 | uname, 67 | uid, 68 | uqq 69 | } 70 | }) 71 | } 72 | 73 | // 替换用户cookie 74 | export function replaceCookie(data) { 75 | return axios.post('//api.clicli.cc/cookie/replace', { 76 | uid: parseInt(data.uid), 77 | hcy: data.hcy, 78 | quqi: data.quqi 79 | }) 80 | 81 | } 82 | 83 | export function auth() { 84 | return axios.get('//api.clicli.cc/auth') 85 | } -------------------------------------------------------------------------------- /packages/react/src/api/video.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import {replaceContent, isDev, API_LINK} from 'common/js/util' 3 | import Cookies from "js-cookie" 4 | 5 | const request = axios.create({ 6 | baseURL: API_LINK 7 | }) 8 | 9 | // 根据pid查找视频 10 | export function getVideos(pid, page, pageSize) { 11 | return request.get('/videos', { 12 | params: { 13 | pid: pid, 14 | page, 15 | pageSize 16 | } 17 | }) 18 | } 19 | 20 | // 根据vid查找单一视频 21 | 22 | export function getVideo(vid) { 23 | return request.get(`/video/${vid}`) 24 | 25 | } 26 | 27 | // 添加视频 28 | export function addVideo({oid, title, content, pid, uid}) { 29 | content = replaceContent(content) 30 | return axios.post('/video/add', { 31 | oid: parseInt(oid), 32 | title, 33 | content, 34 | pid: parseInt(pid), 35 | uid 36 | }, { 37 | headers: { 38 | 'token': Cookies.get('token') 39 | } 40 | }) 41 | } 42 | 43 | // 修改视频 44 | export function updateVideo({id, oid, title, content, pid, uid}) { 45 | content = replaceContent(content) 46 | return axios.post(`/video/update/${id}`, { 47 | oid: parseInt(oid), 48 | title, 49 | content, 50 | pid: parseInt(pid), 51 | uid 52 | }, { 53 | headers: { 54 | 'token': Cookies.get('token') 55 | } 56 | }) 57 | } 58 | 59 | // 根据id删除视频 60 | export function deleteVideoById(id) { 61 | return axios.post(`/video/delete?id=${id}`, {}, { 62 | headers: { 63 | 'token': Cookies.get('token') 64 | } 65 | }) 66 | } 67 | 68 | // 根据pid删除视频 69 | export function deleteVideoByPid(pid) { 70 | return axios.post(`/video/delete?pid=${pid}`, {}, { 71 | headers: { 72 | 'token': Cookies.get('token') 73 | } 74 | }) 75 | } 76 | 77 | 78 | //上传 79 | 80 | export function getUploadToken(fname, rname) { 81 | return axios.get('https://jx.clicli.us/upload/auth', { 82 | params: { 83 | fname, 84 | rname 85 | } 86 | }) 87 | } -------------------------------------------------------------------------------- /packages/react/src/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Header from 'component/header/header' 4 | import EditorUser from 'component/editor-user/editor-user' 5 | import WriteArticle from 'component/write-article/wirte-article' 6 | import EditorVideo from 'component/editor-video/editor-video' 7 | import Login from 'component/login/login' 8 | import UserList from 'component/user-list/user-list' 9 | import ArticleList from 'component/post-list/post-list' 10 | import Register from 'component/register/register' 11 | import UserInfo from 'component/user-info/user-info' 12 | 13 | import {Route, Switch} from 'react-router-dom' 14 | 15 | class App extends React.Component { 16 | render() { 17 | return ( 18 | 19 | 20 | 21 | ( 22 |
    23 |
    24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
    36 | )}/> 37 |
    38 | ) 39 | } 40 | } 41 | 42 | export default App -------------------------------------------------------------------------------- /packages/react/src/base/confirm/confirm.css: -------------------------------------------------------------------------------- 1 | .confirm { 2 | width: 280px; 3 | position: absolute; 4 | top: 50%; 5 | left: 50%; 6 | transform: translate(-50%, -50%); 7 | background: #fff; 8 | box-shadow: -1px 0px 1px rgba(70,123,150, 0.2) 9 | 10 | } 11 | 12 | .confirm .text { 13 | text-align: center; 14 | padding: 30px 0; 15 | } 16 | 17 | .confirm .option { 18 | display: flex; 19 | } 20 | 21 | .confirm .option li { 22 | display: block; 23 | flex: 1; 24 | text-align: center; 25 | cursor: pointer; 26 | } 27 | 28 | .confirm .option li:last-child { 29 | background: #7e5fd9; 30 | color: #fff; 31 | } 32 | -------------------------------------------------------------------------------- /packages/react/src/base/confirm/confirm.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './confirm.css' 3 | 4 | class Confirm extends React.Component { 5 | // constructor(props) { 6 | // super(props) 7 | // this.state = { 8 | // isShow: true 9 | // } 10 | // } 11 | 12 | closeShow() { 13 | this.props.closeShow() 14 | } 15 | 16 | confirm() { 17 | this.props.confirm(this.props.id) 18 | } 19 | 20 | render() { 21 | return ( 22 |
    23 |
    24 | 确认删除吗? 25 |
    26 |
      27 |
    • 取消
    • 28 |
    • 确认
    • 29 |
    30 |
    31 | ) 32 | } 33 | } 34 | 35 | export default Confirm -------------------------------------------------------------------------------- /packages/react/src/base/list-view/list-view.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './listview.css' 3 | import {Link} from 'react-router-dom' 4 | import Confirm from '../confirm/confirm' 5 | import {withRouter} from 'react-router-dom' 6 | import {deletePost} from 'api/post' 7 | import {deleteCommentByPid} from "api/comment" 8 | import {deleteVideoByPid} from "api/video" 9 | 10 | @withRouter 11 | 12 | class ListView extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.state = { 16 | isShow: false, 17 | id: '' 18 | } 19 | } 20 | 21 | handleDelete(id) { 22 | Promise.all([deletePost(id), deleteCommentByPid(id), deleteVideoByPid(id)]).then(() => { 23 | this.props.refresh() 24 | }) 25 | 26 | this.setState({ 27 | isShow: false, 28 | id: '', 29 | }) 30 | } 31 | 32 | handleShow(id) { 33 | this.setState({ 34 | isShow: true, 35 | id: id 36 | }) 37 | } 38 | 39 | closeShow() { 40 | this.setState({ 41 | isShow: false 42 | }) 43 | } 44 | 45 | render() { 46 | return ( 47 | 48 |
    49 | {this.state.isShow === true ? : null} 51 | 52 |
  • 53 |
    标题
    54 |
    分类
    55 |
    状态
    56 |
    操作
    57 |
  • 58 |
      59 | 60 | {this.props.list ? this.props.list.map((item) => { 61 | return ( 62 |
    • 63 |
      {item.title}
      64 |
      {item.sort}
      65 |
      {item.status}
      66 |
      { 67 | this.handleShow(item.id) 68 | }}/>
      69 |
    • 70 | ) 71 | }) :

      没有文章(′⌒`)

      } 72 |
    73 |
    74 | ) 75 | } 76 | } 77 | 78 | export default ListView -------------------------------------------------------------------------------- /packages/react/src/base/list-view/listview.css: -------------------------------------------------------------------------------- 1 | .article-list { 2 | width: 900px; 3 | margin: 50px auto; 4 | } 5 | 6 | .article-list h1 { 7 | color: #7e5fd9; 8 | font-size: 24px; 9 | padding: 10px; 10 | } 11 | 12 | .listview { 13 | box-shadow: 0px 1px 0px rgba(70, 75, 150, 0.2) 14 | } 15 | 16 | .listview li { 17 | display: flex; 18 | padding: 10px; 19 | border-bottom: 1px #ddd dashed; 20 | } 21 | 22 | .listview ul li:last-child { 23 | border: 0 24 | } 25 | 26 | .listview li .title { 27 | flex: 3; 28 | color: #7e5fd9; 29 | } 30 | 31 | .listview li .sort, .listview li .status, .listview li .action { 32 | flex: 1; 33 | } 34 | 35 | .listview li .action i { 36 | color: #7e5fd9; 37 | cursor: pointer; 38 | padding-right: 10px; 39 | } 40 | 41 | .reach-box input{ 42 | background: #fff!important; 43 | margin: 20px 0; 44 | } -------------------------------------------------------------------------------- /packages/react/src/base/loading/loading.css: -------------------------------------------------------------------------------- 1 | .loading{ 2 | position: absolute; 3 | width: 100px; 4 | left: 50%; 5 | top: 50%; 6 | transform: translate(-50%,-50%); 7 | z-index: 1; 8 | } 9 | 10 | .loading .img{ 11 | height: 18px; 12 | width: 18px; 13 | background: url(loading.gif); 14 | margin: 0 auto; 15 | 16 | } 17 | 18 | .loading .text{ 19 | text-align: center; 20 | font-size: 12px; 21 | color: #7e5fd9; 22 | } -------------------------------------------------------------------------------- /packages/react/src/base/loading/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cliclitv/fre-clicli/a60d1f6b647c5da69f29e046cee52d68adf1b072/packages/react/src/base/loading/loading.gif -------------------------------------------------------------------------------- /packages/react/src/base/loading/loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import axios from 'axios' 3 | import './loading.css' 4 | 5 | class Loading extends React.Component { 6 | constructor(props) { 7 | super(props) 8 | this.state = { 9 | isShow: true 10 | } 11 | } 12 | 13 | componentWillMount() { 14 | this._isMounted = true 15 | } 16 | 17 | componentWillUnmount() { 18 | this._isMounted = false 19 | } 20 | 21 | render() { 22 | axios.interceptors.request.use((config) => { 23 | if (this._isMounted) { 24 | this.setState({ 25 | isShow: true 26 | }) 27 | } 28 | 29 | return config 30 | }) 31 | axios.interceptors.response.use((config) => { 32 | if (this._isMounted) { 33 | this.setState({ 34 | isShow: false 35 | }) 36 | } 37 | 38 | return config 39 | }) 40 | if (this.state.isShow === true) { 41 | return ( 42 |
    43 |
    44 |
    45 |
    46 | 努力加载中…… 47 |
    48 |
    49 | ) 50 | } else { 51 | return null 52 | } 53 | 54 | } 55 | 56 | } 57 | 58 | export default Loading -------------------------------------------------------------------------------- /packages/react/src/base/markdown/markdown.css: -------------------------------------------------------------------------------- 1 | /** 2 | * simplemde v1.11.2 3 | * Copyright Next Step Webs, Inc. 4 | * @link https://github.com/NextStepWebs/simplemde-markdown-editor 5 | * @license MIT 6 | */ 7 | 8 | .CodeMirror { 9 | color: #000 10 | } 11 | 12 | .CodeMirror-lines { 13 | padding: 4px 0 14 | } 15 | 16 | .CodeMirror pre { 17 | padding: 0 4px 18 | } 19 | 20 | .CodeMirror-gutter-filler, .CodeMirror-scrollbar-filler { 21 | background-color: #fff 22 | } 23 | 24 | .CodeMirror-gutters { 25 | border-right: 1px solid #ddd; 26 | background-color: #f7f7f7; 27 | white-space: nowrap 28 | } 29 | 30 | .CodeMirror-linenumber { 31 | padding: 0 3px 0 5px; 32 | min-width: 20px; 33 | text-align: right; 34 | color: #999; 35 | white-space: nowrap 36 | } 37 | 38 | .CodeMirror-guttermarker { 39 | color: #000 40 | } 41 | 42 | .CodeMirror-guttermarker-subtle { 43 | color: #999 44 | } 45 | 46 | .CodeMirror-cursor { 47 | border-left: 1px solid #000; 48 | border-right: none; 49 | width: 0 50 | } 51 | 52 | .CodeMirror div.CodeMirror-secondarycursor { 53 | border-left: 1px solid silver 54 | } 55 | 56 | .cm-fat-cursor .CodeMirror-cursor { 57 | width: auto; 58 | border: 0 !important; 59 | background: #7e7 60 | } 61 | 62 | .cm-fat-cursor div.CodeMirror-cursors { 63 | z-index: 1 64 | } 65 | 66 | .cm-animate-fat-cursor { 67 | width: auto; 68 | border: 0; 69 | -webkit-animation: blink 1.06s steps(1) infinite; 70 | -moz-animation: blink 1.06s steps(1) infinite; 71 | animation: blink 1.06s steps(1) infinite; 72 | background-color: #7e7 73 | } 74 | 75 | @-moz-keyframes blink { 76 | 50% { 77 | background-color: transparent 78 | } 79 | } 80 | 81 | @-webkit-keyframes blink { 82 | 50% { 83 | background-color: transparent 84 | } 85 | } 86 | 87 | @keyframes blink { 88 | 50% { 89 | background-color: transparent 90 | } 91 | } 92 | 93 | .cm-tab { 94 | display: inline-block; 95 | text-decoration: inherit 96 | } 97 | 98 | .CodeMirror-ruler { 99 | border-left: 1px solid #ccc; 100 | position: absolute 101 | } 102 | 103 | .cm-s-default .cm-header { 104 | color: #00f 105 | } 106 | 107 | .cm-s-default .cm-quote { 108 | color: #090 109 | } 110 | 111 | .cm-negative { 112 | color: #d44 113 | } 114 | 115 | .cm-positive { 116 | color: #292 117 | } 118 | 119 | .cm-header, .cm-strong { 120 | font-weight: 700 121 | } 122 | 123 | .cm-em { 124 | font-style: italic 125 | } 126 | 127 | .cm-link { 128 | text-decoration: underline 129 | } 130 | 131 | .cm-strikethrough { 132 | text-decoration: line-through 133 | } 134 | 135 | .cm-s-default .cm-keyword { 136 | color: #708 137 | } 138 | 139 | .cm-s-default .cm-atom { 140 | color: #219 141 | } 142 | 143 | .cm-s-default .cm-number { 144 | color: #164 145 | } 146 | 147 | .cm-s-default .cm-def { 148 | color: #00f 149 | } 150 | 151 | .cm-s-default .cm-variable-2 { 152 | color: #05a 153 | } 154 | 155 | .cm-s-default .cm-variable-3 { 156 | color: #085 157 | } 158 | 159 | .cm-s-default .cm-comment { 160 | color: #a50 161 | } 162 | 163 | .cm-s-default .cm-string { 164 | color: #a11 165 | } 166 | 167 | .cm-s-default .cm-string-2 { 168 | color: #f50 169 | } 170 | 171 | .cm-s-default .cm-meta, .cm-s-default .cm-qualifier { 172 | color: #555 173 | } 174 | 175 | .cm-s-default .cm-builtin { 176 | color: #30a 177 | } 178 | 179 | .cm-s-default .cm-bracket { 180 | color: #997 181 | } 182 | 183 | .cm-s-default .cm-tag { 184 | color: #170 185 | } 186 | 187 | .cm-s-default .cm-attribute { 188 | color: #00c 189 | } 190 | 191 | .cm-s-default .cm-hr { 192 | color: #999 193 | } 194 | 195 | .cm-s-default .cm-link { 196 | color: #00c 197 | } 198 | 199 | .cm-invalidchar, .cm-s-default .cm-error { 200 | color: red 201 | } 202 | 203 | .CodeMirror-composing { 204 | border-bottom: 2px solid 205 | } 206 | 207 | div.CodeMirror span.CodeMirror-matchingbracket { 208 | color: #0f0 209 | } 210 | 211 | div.CodeMirror span.CodeMirror-nonmatchingbracket { 212 | color: #f22 213 | } 214 | 215 | .CodeMirror-matchingtag { 216 | background: rgba(255, 150, 0, .3) 217 | } 218 | 219 | .CodeMirror-activeline-background { 220 | background: #e8f2ff 221 | } 222 | 223 | .CodeMirror { 224 | position: relative; 225 | overflow: hidden; 226 | background: #fff 227 | } 228 | 229 | .CodeMirror-scroll { 230 | overflow: scroll !important; 231 | margin-bottom: -30px; 232 | margin-right: -30px; 233 | padding-bottom: 30px; 234 | height: 100%; 235 | outline: 0; 236 | position: relative 237 | } 238 | 239 | .CodeMirror-sizer { 240 | position: relative; 241 | border-right: 30px solid transparent 242 | } 243 | 244 | .CodeMirror-gutter-filler, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-vscrollbar { 245 | position: absolute; 246 | z-index: 6; 247 | display: none 248 | } 249 | 250 | .CodeMirror-vscrollbar { 251 | right: 0; 252 | top: 0; 253 | overflow-x: hidden; 254 | overflow-y: scroll 255 | } 256 | 257 | .CodeMirror-hscrollbar { 258 | bottom: 0; 259 | left: 0; 260 | overflow-y: hidden; 261 | overflow-x: scroll 262 | } 263 | 264 | .CodeMirror-scrollbar-filler { 265 | right: 0; 266 | bottom: 0 267 | } 268 | 269 | .CodeMirror-gutter-filler { 270 | left: 0; 271 | bottom: 0 272 | } 273 | 274 | .CodeMirror-gutters { 275 | position: absolute; 276 | left: 0; 277 | top: 0; 278 | min-height: 100%; 279 | z-index: 3 280 | } 281 | 282 | .CodeMirror-gutter { 283 | white-space: normal; 284 | height: 100%; 285 | display: inline-block; 286 | vertical-align: top; 287 | margin-bottom: -30px 288 | } 289 | 290 | .CodeMirror-gutter-wrapper { 291 | position: absolute; 292 | z-index: 4; 293 | background: 0 0 !important; 294 | border: none !important; 295 | -webkit-user-select: none; 296 | -moz-user-select: none; 297 | user-select: none 298 | } 299 | 300 | .CodeMirror-gutter-background { 301 | position: absolute; 302 | top: 0; 303 | bottom: 0; 304 | z-index: 4 305 | } 306 | 307 | .CodeMirror-gutter-elt { 308 | position: absolute; 309 | cursor: default; 310 | z-index: 4 311 | } 312 | 313 | .CodeMirror-lines { 314 | cursor: text; 315 | min-height: 1px 316 | } 317 | 318 | .CodeMirror pre { 319 | -moz-border-radius: 0; 320 | -webkit-border-radius: 0; 321 | border-radius: 0; 322 | border-width: 0; 323 | background: 0 0; 324 | font-family: inherit; 325 | font-size: inherit; 326 | margin: 0; 327 | white-space: pre; 328 | word-wrap: normal; 329 | line-height: inherit; 330 | color: inherit; 331 | z-index: 2; 332 | position: relative; 333 | overflow: visible; 334 | -webkit-tap-highlight-color: transparent; 335 | -webkit-font-variant-ligatures: none; 336 | font-variant-ligatures: none 337 | } 338 | 339 | .CodeMirror-wrap pre { 340 | word-wrap: break-word; 341 | white-space: pre-wrap; 342 | word-break: normal 343 | } 344 | 345 | .CodeMirror-linebackground { 346 | position: absolute; 347 | left: 0; 348 | right: 0; 349 | top: 0; 350 | bottom: 0; 351 | z-index: 0 352 | } 353 | 354 | .CodeMirror-linewidget { 355 | position: relative; 356 | z-index: 2; 357 | overflow: auto 358 | } 359 | 360 | .CodeMirror-code { 361 | outline: 0 362 | } 363 | 364 | .CodeMirror-gutter, .CodeMirror-gutters, .CodeMirror-linenumber, .CodeMirror-scroll, .CodeMirror-sizer { 365 | -moz-box-sizing: content-box; 366 | box-sizing: content-box 367 | } 368 | 369 | .CodeMirror-measure { 370 | position: absolute; 371 | width: 100%; 372 | height: 0; 373 | overflow: hidden; 374 | visibility: hidden 375 | } 376 | 377 | .CodeMirror-cursor { 378 | position: absolute 379 | } 380 | 381 | .CodeMirror-measure pre { 382 | position: static 383 | } 384 | 385 | div.CodeMirror-cursors { 386 | visibility: hidden; 387 | position: relative; 388 | z-index: 3 389 | } 390 | 391 | .CodeMirror-focused div.CodeMirror-cursors, div.CodeMirror-dragcursors { 392 | visibility: visible 393 | } 394 | 395 | .CodeMirror-selected { 396 | background: #d9d9d9 397 | } 398 | 399 | .CodeMirror-focused .CodeMirror-selected, .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { 400 | background: #d7d4f0 401 | } 402 | 403 | .CodeMirror-crosshair { 404 | cursor: crosshair 405 | } 406 | 407 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { 408 | background: #d7d4f0 409 | } 410 | 411 | .cm-searching { 412 | background: #ffa; 413 | background: rgba(255, 255, 0, .4) 414 | } 415 | 416 | .cm-force-border { 417 | padding-right: .1px 418 | } 419 | 420 | @media print { 421 | .CodeMirror div.CodeMirror-cursors { 422 | visibility: hidden 423 | } 424 | } 425 | 426 | .cm-tab-wrap-hack:after { 427 | content: '' 428 | } 429 | 430 | span.CodeMirror-selectedtext { 431 | background: 0 0 432 | } 433 | 434 | .CodeMirror { 435 | height: auto; 436 | min-height: 300px; 437 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.2); 438 | padding: 10px; 439 | font: inherit; 440 | z-index: 1 441 | } 442 | 443 | .CodeMirror-scroll { 444 | min-height: 300px 445 | } 446 | 447 | .CodeMirror-fullscreen { 448 | background: #fff; 449 | position: fixed !important; 450 | top: 50px; 451 | left: 0; 452 | right: 0; 453 | bottom: 0; 454 | height: auto; 455 | z-index: 9 456 | } 457 | 458 | .CodeMirror-sided { 459 | width: 50% !important 460 | } 461 | 462 | .editor-toolbar { 463 | position: relative; 464 | opacity: .6; 465 | -webkit-user-select: none; 466 | -moz-user-select: none; 467 | -ms-user-select: none; 468 | -o-user-select: none; 469 | user-select: none; 470 | } 471 | 472 | .editor-toolbar:after, .editor-toolbar:before { 473 | display: block; 474 | content: ' '; 475 | height: 1px 476 | } 477 | 478 | .editor-toolbar:before { 479 | margin-bottom: 5px 480 | } 481 | 482 | .editor-toolbar:after { 483 | margin-top: 5px 484 | } 485 | 486 | .editor-toolbar:hover, .editor-wrapper input.title:focus, .editor-wrapper input.title:hover { 487 | opacity: .8 488 | } 489 | 490 | .editor-toolbar.fullscreen { 491 | width: 100%; 492 | height: 50px; 493 | overflow-x: auto; 494 | overflow-y: hidden; 495 | white-space: nowrap; 496 | padding-top: 10px; 497 | padding-bottom: 10px; 498 | box-sizing: border-box; 499 | background: #fff; 500 | border: 0; 501 | position: fixed; 502 | top: 0; 503 | left: 0; 504 | opacity: 1; 505 | z-index: 9 506 | } 507 | 508 | .editor-toolbar.fullscreen::before { 509 | width: 20px; 510 | height: 50px; 511 | background: -moz-linear-gradient(left, rgba(255, 255, 255, 1) 0, rgba(255, 255, 255, 0) 100%); 512 | background: -webkit-gradient(linear, left top, right top, color-stop(0, rgba(255, 255, 255, 1)), color-stop(100%, rgba(255, 255, 255, 0))); 513 | background: -webkit-linear-gradient(left, rgba(255, 255, 255, 1) 0, rgba(255, 255, 255, 0) 100%); 514 | background: -o-linear-gradient(left, rgba(255, 255, 255, 1) 0, rgba(255, 255, 255, 0) 100%); 515 | background: -ms-linear-gradient(left, rgba(255, 255, 255, 1) 0, rgba(255, 255, 255, 0) 100%); 516 | background: linear-gradient(to right, rgba(255, 255, 255, 1) 0, rgba(255, 255, 255, 0) 100%); 517 | position: fixed; 518 | top: 0; 519 | left: 0; 520 | margin: 0; 521 | padding: 0 522 | } 523 | 524 | .editor-toolbar.fullscreen::after { 525 | width: 20px; 526 | height: 50px; 527 | background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 1) 100%); 528 | background: -webkit-gradient(linear, left top, right top, color-stop(0, rgba(255, 255, 255, 0)), color-stop(100%, rgba(255, 255, 255, 1))); 529 | background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 1) 100%); 530 | background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 1) 100%); 531 | background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 1) 100%); 532 | background: linear-gradient(to right, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 1) 100%); 533 | position: fixed; 534 | top: 0; 535 | right: 0; 536 | margin: 0; 537 | padding: 0 538 | } 539 | 540 | .editor-toolbar a { 541 | display: inline-block; 542 | text-align: center; 543 | text-decoration: none !important; 544 | color: #2c3e50 !important; 545 | width: 25px; 546 | height: 25px; 547 | margin: 0; 548 | cursor: pointer; 549 | } 550 | 551 | .editor-toolbar a.active, .editor-toolbar a:hover { 552 | background: #E9E9E6; 553 | } 554 | 555 | .editor-toolbar a:before { 556 | line-height: 25px 557 | } 558 | 559 | .editor-toolbar i.separator { 560 | display: inline-block; 561 | width: 0; 562 | border-left: 1px solid #d9d9d9; 563 | border-right: 1px solid #fff; 564 | color: transparent; 565 | text-indent: -10px; 566 | margin: 0 6px 567 | } 568 | 569 | .editor-toolbar a.fa-header-x:after { 570 | font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; 571 | font-size: 65%; 572 | vertical-align: text-bottom; 573 | position: relative; 574 | top: 2px 575 | } 576 | 577 | .editor-toolbar a.fa-header-1:after { 578 | content: "1" 579 | } 580 | 581 | .editor-toolbar a.fa-header-2:after { 582 | content: "2" 583 | } 584 | 585 | .editor-toolbar a.fa-header-3:after { 586 | content: "3" 587 | } 588 | 589 | .editor-toolbar a.fa-header-bigger:after { 590 | content: "▲" 591 | } 592 | 593 | .editor-toolbar a.fa-header-smaller:after { 594 | content: "▼" 595 | } 596 | 597 | .editor-toolbar.disabled-for-preview a:not(.no-disable) { 598 | pointer-events: none; 599 | background: #fff; 600 | border-color: transparent; 601 | text-shadow: inherit 602 | } 603 | 604 | @media only screen and (max-width: 700px) { 605 | .editor-toolbar a.no-mobile { 606 | display: none 607 | } 608 | } 609 | 610 | .editor-statusbar { 611 | padding: 8px 10px; 612 | font-size: 12px; 613 | color: #959694; 614 | text-align: right 615 | } 616 | 617 | .editor-statusbar span { 618 | display: inline-block; 619 | min-width: 4em; 620 | margin-left: 1em 621 | } 622 | 623 | .editor-preview, .editor-preview-side { 624 | padding: 10px; 625 | background: #fafafa; 626 | overflow: auto; 627 | display: none; 628 | box-sizing: border-box 629 | } 630 | 631 | .editor-statusbar .lines:before { 632 | content: 'lines: ' 633 | } 634 | 635 | .editor-statusbar .words:before { 636 | content: 'words: ' 637 | } 638 | 639 | .editor-statusbar .characters:before { 640 | content: 'characters: ' 641 | } 642 | 643 | .editor-preview { 644 | position: absolute; 645 | width: 100%; 646 | height: 100%; 647 | top: 0; 648 | left: 0; 649 | z-index: 7 650 | } 651 | 652 | .editor-preview-side { 653 | position: fixed; 654 | bottom: 0; 655 | width: 50%; 656 | top: 50px; 657 | right: 0; 658 | z-index: 9; 659 | border: 1px solid #ddd 660 | } 661 | 662 | .editor-preview-active, .editor-preview-active-side { 663 | display: block 664 | } 665 | 666 | .editor-preview-side > p, .editor-preview > p { 667 | margin-top: 0 668 | } 669 | 670 | .editor-preview pre, .editor-preview-side pre { 671 | background: #eee; 672 | margin-bottom: 10px 673 | } 674 | 675 | .editor-preview table td, .editor-preview table th, .editor-preview-side table td, .editor-preview-side table th { 676 | border: 1px solid #ddd; 677 | padding: 5px 678 | } 679 | 680 | .CodeMirror .CodeMirror-code .cm-tag { 681 | color: #63a35c 682 | } 683 | 684 | .CodeMirror .CodeMirror-code .cm-attribute { 685 | color: #795da3 686 | } 687 | 688 | .CodeMirror .CodeMirror-code .cm-string { 689 | color: #183691 690 | } 691 | 692 | .CodeMirror .CodeMirror-selected { 693 | background: #d9d9d9 694 | } 695 | 696 | .CodeMirror .CodeMirror-code .cm-header-1 { 697 | font-size: 200%; 698 | line-height: 200% 699 | } 700 | 701 | .CodeMirror .CodeMirror-code .cm-header-2 { 702 | font-size: 160%; 703 | line-height: 160% 704 | } 705 | 706 | .CodeMirror .CodeMirror-code .cm-header-3 { 707 | font-size: 125%; 708 | line-height: 125% 709 | } 710 | 711 | .CodeMirror .CodeMirror-code .cm-header-4 { 712 | font-size: 110%; 713 | line-height: 110% 714 | } 715 | 716 | .CodeMirror .CodeMirror-code .cm-comment { 717 | background: rgba(0, 0, 0, .05); 718 | border-radius: 2px 719 | } 720 | 721 | .CodeMirror .CodeMirror-code .cm-link { 722 | color: #7f8c8d 723 | } 724 | 725 | .CodeMirror .CodeMirror-code .cm-url { 726 | color: #aab2b3 727 | } 728 | 729 | .CodeMirror .CodeMirror-code .cm-strikethrough { 730 | text-decoration: line-through 731 | } 732 | 733 | .CodeMirror .CodeMirror-placeholder { 734 | opacity: .5 735 | } 736 | 737 | .CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word) { 738 | background: rgba(255, 0, 0, .15) 739 | } -------------------------------------------------------------------------------- /packages/react/src/base/markdown/markdown.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Mde from 'simplemde' 3 | import './markdown.css' 4 | 5 | class Markdown extends React.Component { 6 | 7 | componentDidMount() { 8 | this.loadEditor() 9 | } 10 | 11 | componentWillReceiveProps(nextProps) { 12 | if (this.props.defaultValue !== nextProps.defaultValue) { 13 | this.mde.value(nextProps.defaultValue) 14 | } 15 | } 16 | 17 | loadEditor() { 18 | this.mde = new Mde({ 19 | element:this.marked, 20 | autoDownloadFontAwesome: true, 21 | status: false, 22 | spellChecker: false, 23 | forceSync: true 24 | }) 25 | 26 | this.bindMde() 27 | } 28 | 29 | bindMde() { 30 | this.mde.codemirror.on('change', () => { 31 | this.props.handleMde(this.mde.value()) 32 | }) 33 | } 34 | 35 | render() { 36 | return ( 37 |
    38 | 39 |
    40 | ) 41 | } 42 | } 43 | 44 | export default Markdown -------------------------------------------------------------------------------- /packages/react/src/base/reach-box/reach-box.css: -------------------------------------------------------------------------------- 1 | .reach-box input { 2 | padding: 10px; 3 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.2); 4 | border: 0; 5 | width: 300px; 6 | margin-right: 20px; 7 | box-sizing: border-box; 8 | background: #F6F6F3; 9 | } 10 | 11 | .reach-box button { 12 | background: #7e5fd9; 13 | padding: 8px 30px; 14 | color: #fff; 15 | margin: 0 auto; 16 | border: 0; 17 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.6); 18 | cursor: pointer; 19 | } -------------------------------------------------------------------------------- /packages/react/src/base/reach-box/reach-box.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './reach-box.css' 3 | import {withRouter} from "react-router-dom" 4 | 5 | @withRouter 6 | 7 | class ReachBox extends React.Component { 8 | constructor(props) { 9 | super(props) 10 | this.state = { 11 | v: '' 12 | } 13 | } 14 | 15 | selectInput() { 16 | this.props.history.push(`${this.props.prefix}${this.state.v}`) 17 | } 18 | 19 | handleChange(v) { 20 | this.setState({ 21 | v 22 | }) 23 | } 24 | 25 | render() { 26 | return ( 27 |
    28 | this.handleChange(e.target.value)}/> 29 | 30 |
    31 | ) 32 | } 33 | 34 | } 35 | 36 | export default ReachBox -------------------------------------------------------------------------------- /packages/react/src/base/top-tip/top-tip.css: -------------------------------------------------------------------------------- 1 | .toptip { 2 | position: fixed; 3 | width: 100%; 4 | padding: 10px; 5 | color: #fff; 6 | top: -40px; 7 | text-align: center; 8 | z-index: 999; 9 | animation: moveup 5s ease-in-out infinite; 10 | } 11 | 12 | @keyframes moveup { 13 | 0% { 14 | top: -40px; 15 | opacity: 1; 16 | } 17 | 25%{ 18 | top: 0; 19 | opacity: 1; 20 | } 21 | 50%{ 22 | opacity: 1; 23 | } 24 | 100% { 25 | top: 0; 26 | opacity: 0; 27 | } 28 | } -------------------------------------------------------------------------------- /packages/react/src/base/top-tip/top-tip.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './top-tip.css' 3 | 4 | 5 | class TopTip extends React.Component { 6 | 7 | render() { 8 | let text = this.props.text 9 | return ( 10 |
    11 | {text} 12 |
    13 | ) 14 | } 15 | } 16 | 17 | export default TopTip -------------------------------------------------------------------------------- /packages/react/src/common/js/axios.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cliclitv/fre-clicli/a60d1f6b647c5da69f29e046cee52d68adf1b072/packages/react/src/common/js/axios.js -------------------------------------------------------------------------------- /packages/react/src/common/js/util.js: -------------------------------------------------------------------------------- 1 | import md5 from 'blueimp-md5' 2 | import Cookies from 'js-cookie' 3 | 4 | export function getAvatar(avatar) { 5 | if (!avatar) return '' 6 | if (/^[0-9]+$/.test(avatar)) { 7 | return `http://q1.qlogo.cn/g?b=qq&nk=${avatar}&s=5` 8 | } else { 9 | let hash = md5(avatar) 10 | return `https://sdn.geekzu.org/avatar/${hash}` 11 | } 12 | } 13 | 14 | export function replaceContent(content) { 15 | content = content.replace('piaoquantv', 'yishihui') 16 | return content 17 | } 18 | 19 | export let isDev = process.env.NODE_ENV === 'development' 20 | export const HOME_LINK = '//www.clicli.cc' 21 | export const API_LINK = '//api.clicli.cc' 22 | -------------------------------------------------------------------------------- /packages/react/src/common/style/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "icon-font"; 3 | src: url('//at.alicdn.com/t/font_474499_sbdek9r9luucjtt9.eot?t=1519207777376'); /* IE9*/ 4 | src: url('//at.alicdn.com/t/font_474499_sbdek9r9luucjtt9.eot?t=1519207777376#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAZAAAsAAAAACSwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7kiWY21hcAAAAYAAAABxAAABsgKC1C5nbHlmAAAB9AAAAkEAAALEomRh/mhlYWQAAAQ4AAAALwAAADYQhhCnaGhlYQAABGgAAAAcAAAAJAfeA4ZobXR4AAAEhAAAABMAAAAUE+kAAGxvY2EAAASYAAAADAAAAAwBcgISbWF4cAAABKQAAAAeAAAAIAEUAF1uYW1lAAAExAAAAUoAAAJ5PNf/ynBvc3QAAAYQAAAAMAAAAEGh1WBeeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/s04gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDxbydzwv4EhhrmBoQEozAiSAwAw4A0YeJzFkcENgDAIRR+2GmMcxSGcwxk8eXIAl3Ex1tDf4kEn8JPXwA+hDQVaIIlJZLANo2iVa9VPDNXPzKoHehrF7osffl6XvHceMvVFlDypv8zu+E3239VfjfWcn0pbYX/QE30Jyo/4EWh3+BmQb4zoGXgAAAB4nH2RS2sTURTHz7k3M5PUTJK580qmmSSTycwkNMZmJo/S0lTBRVOkCCLq0sdWNwoFUYhgpQsf0aWCoAh+BqWLuNZPIG6kbnTVL5DRO1TEhXg5XP7n3AP/c34XBICfX+k+LYIKTejCaTgLgOIS1nPERifodcgS6o6gm1qOBm7gSG69Q9fRrIuaEQ56vilKYh5zWMHICQdBhwTY743IGoaGjVhatM4xr8zoFBeKQWU33iKvUa+65fzoeDxub2hhTU3vZBkrMfYwLQpCmpBUPofXTSMjZBbE+I2Qt/T9aotUMVsKrDOX5Noiu7zXu2F7ZgZxMkF1sZZ7u6FYCo87lqGyklSQ00VLdhsa7nw7VlSztn8A/CS7fqDP6AZkQIYClMHmuwbDQVhFQ5MwQBOHphAamig5HewNIoeuPB2zrrrKWKxPcW86v3XxMb4qaFphfjuv63nygJyfbjK2qi6rScfuNBZ+PHmkVzVEraoDpLjnjN6jp7inDT6c4I4dlFxflDhVRTMSan0lIek6CVOvN4xMRawHlAs9EWQm9Z2PzrKEk1j2mvip6TU9rzl/zm98gSNMraRmuEb7KQpeGbHsnfwS+w3fb+Dn1laLR5FDja9UKvjytzia6z49pNeAQQMGsMnn4v76n4n4x7vU6db9dQ5i6ISGqefQ7f+r0sF+N4F1VJHozXkpGm9G5CDaVp22dTcuyIzJeJhliIr8v4xe3Y7mdjgeR+R7qV1X5xcUGfkT75CV93/pd7xZZkoi8RcLBXkRAAAAeJxjYGRgYADil7NO9Mbz23xl4GZhAIFrm9MSEfT/FhYG5hQgl4OBCSQKAFITCyEAeJxjYGRgYG7438AQw8IAAkCSkQEVsAIARwsCbnicY2FgYGB+ycDAwoDAAA6bAP0AAAAAAAB2ALAA/AFieJxjYGRgYGBlCARiEGACYi4gZGD4D+YzAAARLQFyAAB4nG2RvU4CQRSFz/JnWKIFRkudRgsNy09jQikJFHYU9LDMLpD9y+xAwiP4PD6DT2Bv5zPYelguFMhu5ua7554zuckAaOIbDvbfDc+eHdTZ7bmEC9wJl6k/CFfInnAVDbwI16i/Crt4xptwA9fY8AanUmf3hHdhhzt8CJdwhU/hMvUv4Qr5R7iKW/wK19B0LoVdTJx74QYenbU7MHpq9VzNtmrpp0mQJtbdQWtHYx2uo6k59keYaJMv00R1vc5RG+lEm8Nd+SbsWRuowKSxGnKqoyhVmUlX2rfewtqs324Hont+GnO1AQw0prCscyjMsGVdwkeKBEFRLX0HpXXUxkyEWCNi2pyZ/1cmTBjknOx6hS6fqXPGN6IvKbyne+V8qBA9qpZuxWOYiUlDyWruE5EVsmK2ouJT97AoUhn6aPMPTvxesUX8Bx1ocJYAAHicY2BigAAuBuyAlZGJkZmRhZGVkY2BsYItNSWzJL+IOSU1h6UktaKEgQEARuIGMg==') format('woff'), 6 | url('//at.alicdn.com/t/font_474499_sbdek9r9luucjtt9.ttf?t=1519207777376') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('//at.alicdn.com/t/font_474499_sbdek9r9luucjtt9.svg?t=1519207777376#icon-font') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .icon-font { 11 | font-family:"icon-font" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-editor:before { content: "\e684"; } 19 | 20 | .icon-del:before { content: "\e65a"; } 21 | 22 | .icon-text:before { content: "\e6a9"; } 23 | 24 | -------------------------------------------------------------------------------- /packages/react/src/common/style/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 14px 微软雅黑; 3 | color: #444; 4 | background: #f4f6fa; 5 | } 6 | 7 | a { 8 | color: #7e5fd9; 9 | } 10 | 11 | .center { 12 | text-align: center; 13 | } 14 | 15 | li { 16 | list-style: none; 17 | } 18 | -------------------------------------------------------------------------------- /packages/react/src/common/style/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | 27 | /* HTML5 display-role reset for older browsers */ 28 | article, aside, details, figcaption, figure, 29 | footer, header, hgroup, menu, nav, section { 30 | display: block; 31 | } 32 | 33 | body { 34 | line-height: 1; 35 | } 36 | 37 | ol, ul { 38 | list-style: none; 39 | } 40 | 41 | blockquote, q { 42 | quotes: none; 43 | } 44 | 45 | blockquote:before, blockquote:after, 46 | q:before, q:after { 47 | content: ''; 48 | content: none; 49 | } 50 | 51 | table { 52 | border-collapse: collapse; 53 | border-spacing: 0; 54 | } 55 | 56 | a{ 57 | text-decoration: none; 58 | } -------------------------------------------------------------------------------- /packages/react/src/component/editor-user/editor-user.css: -------------------------------------------------------------------------------- 1 | .editor-user { 2 | width: 800px; 3 | margin: 50px auto; 4 | } 5 | 6 | .editor-user h1 { 7 | color: #7e5fd9; 8 | font-size: 24px; 9 | margin: 20px 0; 10 | } 11 | 12 | .editor-user li { 13 | padding: 10px; 14 | font-weight: bold; 15 | } 16 | 17 | .editor-user span { 18 | cursor: pointer; 19 | } 20 | 21 | .editor-user ul { 22 | width: 500px; 23 | margin: 50px auto; 24 | } 25 | 26 | .editor-user li input, textarea { 27 | padding: 10px; 28 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.2); 29 | border: 0; 30 | width: 100%; 31 | margin: 10px 0; 32 | box-sizing: border-box; 33 | } 34 | 35 | .editor-user li textarea { 36 | height: 50px; 37 | } 38 | 39 | .editor-user button { 40 | background: #7e5fd9; 41 | padding: 8px 30px; 42 | color: #fff; 43 | margin: 20px; 44 | border: 0; 45 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.6); 46 | cursor: pointer; 47 | } 48 | 49 | .editor-user select { 50 | padding: 5px 17px; 51 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.2); 52 | border: 0; 53 | color: #666; 54 | margin: 10px 0; 55 | display: block; 56 | } 57 | 58 | .per { 59 | position: absolute; 60 | top: 50%; 61 | left: 50%; 62 | transform: translate(-50%, -50%); 63 | height: 60px; 64 | width: 60px; 65 | color: #fff; 66 | font-size: 20px; 67 | background: #608ea8; 68 | text-align: center; 69 | border-radius: 30px; 70 | line-height: 60px; 71 | } 72 | 73 | #uploader{ 74 | position: relative; 75 | } 76 | 77 | #uploader input{ 78 | opacity: 0; 79 | position: absolute; 80 | top: 0; 81 | left: 0; 82 | width: 100px; 83 | height: 35px; 84 | } 85 | 86 | #uploader span{ 87 | position: absolute; 88 | top: 15px; 89 | left: 0; 90 | background: #6c43b6; 91 | color: #fff; 92 | width: 100px; 93 | text-align: center; 94 | line-height: 35px; 95 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.6); 96 | cursor: pointer; 97 | height: 35px; 98 | } -------------------------------------------------------------------------------- /packages/react/src/component/editor-user/editor-user.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import TopTip from 'base/top-tip/top-tip' 4 | 5 | import {getUser, update} from 'api/user' 6 | import {adminAuth} from "hoc/auth/auth" 7 | 8 | import './editor-user.css' 9 | 10 | @adminAuth 11 | class EditorUser extends React.Component { 12 | 13 | constructor(props) { 14 | super(props) 15 | this.state = { 16 | qq: this.props.match.params.qq, 17 | msg: '', 18 | name: '', 19 | desc: '', 20 | level: '' 21 | } 22 | } 23 | 24 | componentDidMount() { 25 | this.loadUser() 26 | } 27 | 28 | loadUser() { 29 | getUser('', 0, this.state.qq).then(res => { 30 | if (res.data.code === 200) { 31 | this.setState(res.data.result) 32 | } 33 | }) 34 | } 35 | 36 | handleChange(key, val) { 37 | this.setState({ 38 | [key]: val 39 | }) 40 | } 41 | 42 | handleClick() { 43 | update(this.state).then(res => { 44 | this.setState({ 45 | msg: res.data.msg 46 | }) 47 | setTimeout(() => { 48 | this.setState({ 49 | msg: '' 50 | }) 51 | }, 5000) 52 | }) 53 | } 54 | 55 | render() { 56 | return ( 57 |
    58 | {this.state.msg ? : null} 59 | 60 |
    61 |

    编辑用户

    62 |
      63 |
    • 用户名: this.handleChange('name', e.target.value)}/>
    • 65 |
    • 密码: this.handleChange('pwd', e.target.value)}/> 66 |
    • 67 |
    • QQ: this.handleChange('qq', e.target.value)}/> 68 |
    • 69 | {this.props.state.level > 3 ? ( 70 |
    • 权限: 71 |
    • 77 | ) : null} 78 | 79 |
    • 签名: 80 | 82 |
    • 83 |
    • 84 | 85 |
    • 86 |
    87 |
    88 |
    89 | ) 90 | } 91 | } 92 | 93 | export default EditorUser -------------------------------------------------------------------------------- /packages/react/src/component/editor-video/editor-video.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TopTip from 'base/top-tip/top-tip' 3 | import {getVideo, updateVideo, deleteVideoById, addVideo, getUploadToken} from "api/video" 4 | import {adminAuth} from "hoc/auth/auth" 5 | import {withRouter} from 'react-router-dom' 6 | 7 | import '../editor-user/editor-user.css' 8 | import Cookies from "js-cookie" 9 | 10 | @adminAuth 11 | @withRouter 12 | class EditorVideo extends React.Component { 13 | 14 | constructor(props) { 15 | super(props) 16 | this.state = { 17 | id: this.props.match.params.id, 18 | oid: '', 19 | title: '', 20 | content: '', 21 | pid: this.props.match.params.pid, 22 | text: '修改', 23 | uid: parseInt(Cookies.get('uid')), 24 | zhilian: true, 25 | per: 0 26 | } 27 | } 28 | 29 | componentDidMount() { 30 | if (this.props.location.pathname.indexOf('add-video') > -1) { 31 | this.setState({text: '添加'}) 32 | } else { 33 | this.loadVideo() 34 | this.setState({text: '修改'}) 35 | } 36 | } 37 | 38 | loadVideo() { 39 | getVideo(this.state.id).then(res => { 40 | if (res.data.code === 200) { 41 | this.setState(res.data.result) 42 | } 43 | }) 44 | } 45 | 46 | handleChange(key, val) { 47 | this.setState({ 48 | [key]: val 49 | }) 50 | } 51 | 52 | deleteVideo() { 53 | deleteVideoById(this.state.id).then(res => { 54 | if (res.data.code === 200) { 55 | this.setState({ 56 | msg: '删除成功啦' 57 | }) 58 | setTimeout(() => { 59 | this.props.history.goBack() 60 | this.setState({ 61 | msg: '' 62 | }) 63 | }, 2000) 64 | } 65 | }) 66 | } 67 | 68 | handleClick() { 69 | if (this.props.location.pathname.indexOf('editor-video') > -1) { 70 | updateVideo(this.state).then(res => { 71 | if (res.data.code === 200) { 72 | this.setState({ 73 | msg: '更新成功啦' 74 | }) 75 | } 76 | setTimeout(() => { 77 | this.props.history.goBack() 78 | this.setState({ 79 | msg: '' 80 | }) 81 | }, 2000) 82 | }) 83 | } else { 84 | this.addVideos() 85 | } 86 | } 87 | 88 | addVideos() { 89 | addVideo(this.state).then(res => { 90 | if (res.data.code === 200) { 91 | this.setState({ 92 | msg: '添加成功啦' 93 | }) 94 | } 95 | setTimeout(() => { 96 | this.props.history.goBack() 97 | this.setState({ 98 | msg: '' 99 | }) 100 | }, 2000) 101 | }) 102 | } 103 | 104 | async onUpload() { 105 | let self = this 106 | let file = this.refs.uploader.files[0] 107 | let rname = this.state.pid + ' | ' + this.state.oid 108 | const token = await getUploadToken(file.name, rname).then(res => { 109 | return res.data.uploadToken 110 | }) 111 | console.log(token) 112 | const uploader = new DogeUploader({ 113 | file, 114 | token, 115 | next(progress) { 116 | self.setState({ 117 | per: `${Math.floor(progress.percent)}` 118 | }) 119 | }, 120 | error(err) { 121 | alert('上传出错( ' + err.code + ' ):' + err.message + '') 122 | }, 123 | complete(res) { 124 | console.log('上传完成,视频 vid:' + res.vid) 125 | self.setState({ 126 | content: `${res.vid}@dogecloud`, 127 | per: 0 128 | }) 129 | } 130 | }) 131 | 132 | uploader.upload() 133 | } 134 | 135 | zhilian() { 136 | this.setState({ 137 | zhilian: !this.state.zhilian 138 | }) 139 | } 140 | 141 | render() { 142 | return ( 143 |
    144 | {this.state.msg ? : null} 145 | {this.state.per ?
    {this.state.per + '%'}
    : null} 146 | 147 |
    148 |

    {this.state.text}视频

    149 |
      150 |
    • 序号: this.handleChange('oid', e.target.value)}/>
    • 152 |
    • 备注: this.handleChange('title', e.target.value)}/> 154 |
    • 155 |
    • {this.state.zhilian ? '直链' : '上传'}:{this.state.zhilian ? 156 | this.handleChange('content', e.target.value)}/> 159 | :
      160 |
      上传
      162 |
      163 |
      164 | }
    • 165 |
    • 166 | 167 | 168 |
    • 169 |
    170 |
    171 |
    172 | ) 173 | } 174 | } 175 | 176 | export default EditorVideo -------------------------------------------------------------------------------- /packages/react/src/component/header/header.css: -------------------------------------------------------------------------------- 1 | 2 | header { 3 | background: #7e5fd9; 4 | padding: 10px; 5 | width: 100%; 6 | position: fixed; 7 | top: 0; 8 | z-index: 9; 9 | } 10 | 11 | header li { 12 | display: inline-block; 13 | color: #fff; 14 | padding: 10px 30px; 15 | cursor: pointer; 16 | position: relative; 17 | } 18 | 19 | header li:hover { 20 | background: #6c43b6; 21 | } 22 | 23 | header li:hover ul { 24 | display: block; 25 | position: absolute; 26 | top: 38px; 27 | left: 0; 28 | white-space: nowrap; 29 | background: #6c43b6; 30 | padding: 10px 0 15px 0; 31 | } 32 | 33 | header li:hover ul li { 34 | display: block; 35 | font-size: 13px; 36 | } 37 | 38 | header li:hover ul li:hover { 39 | font-weight: bold; 40 | } 41 | 42 | header li ul { 43 | display: none; 44 | } 45 | 46 | header .logout { 47 | background: #9579e8; 48 | float: right; 49 | border-right: 1px solid #7e5fd9; 50 | } 51 | 52 | header .logout:hover { 53 | background: #9579e8; 54 | font-weight: bold; 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /packages/react/src/component/header/header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import {Link, withRouter} from 'react-router-dom' 4 | import {HOME_LINK} from "common/js/util" 5 | import {adminAuth} from "hoc/auth/auth" 6 | import {map} from "smox" 7 | import {logout} from "api/user" 8 | 9 | import './header.css' 10 | 11 | @map({ 12 | state: ['redirectTo'], 13 | mutations: ['logout'] 14 | }) 15 | @withRouter 16 | @adminAuth 17 | class Header extends React.Component { 18 | onLogout() { 19 | this.props.logout() 20 | logout().then(res => { 21 | if (res.data.code === 200) { 22 | this.props.history.push('/login') 23 | } 24 | }) 25 | 26 | } 27 | 28 | render() { 29 | return ( 30 |
    31 |
      32 | 33 |
    • 控制台
    • 34 | 35 | {this.props.state.level >= 2 ? 36 | 37 |
    • 撰写
    • 38 | : null} 39 | {this.props.state.level >= 3 ? 40 |
    • 管理 41 |
        42 | 43 |
      • 审核稿件
      • 44 | 45 | 46 |
      • 管理稿件
      • 47 | 48 | 49 |
      • 管理用户
      • 50 | 51 |
      52 |
    • 53 | : null} 54 |
    • 55 | 退出 56 |
    • 57 | 58 |
    • 59 | 网站首页 60 |
    • 61 |
      62 |
    63 |
    64 | ) 65 | } 66 | 67 | } 68 | 69 | export default Header -------------------------------------------------------------------------------- /packages/react/src/component/login/login.css: -------------------------------------------------------------------------------- 1 | .login { 2 | width: 280px; 3 | position: absolute; 4 | top: 50%; 5 | left: 50%; 6 | transform: translate(-50%, -50%); 7 | } 8 | 9 | .login li { 10 | padding: 8px 0; 11 | text-align: center; 12 | } 13 | 14 | .login li input { 15 | padding: 10px; 16 | width: 100%; 17 | box-sizing: border-box; 18 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.2); 19 | border: 0px; 20 | } 21 | 22 | .login li button{ 23 | background: #7e5fd9; 24 | width: 100%; 25 | border: 0px; 26 | box-shadow: 1px 1px 1px rgba(60, 32, 105, 0.6); 27 | padding: 10px; 28 | color: #fff; 29 | cursor: pointer; 30 | } 31 | .login li a{ 32 | text-align: center; 33 | } 34 | 35 | .login h1{ 36 | color: #7e5fd9; 37 | font-size: 40px; 38 | padding: 10px; 39 | } -------------------------------------------------------------------------------- /packages/react/src/component/login/login.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import {Link, withRouter} from 'react-router-dom' 4 | 5 | import {map} from 'smox' 6 | import {handleForm} from "hoc/handle-form/handle-form" 7 | import TopTip from 'base/top-tip/top-tip' 8 | 9 | import './login.css' 10 | 11 | @map({ 12 | state: ['isAuth', 'msg', 'bg'], 13 | actions: ['onLogin'] 14 | }) 15 | @handleForm 16 | @withRouter 17 | class Login extends React.Component { 18 | 19 | onKeyUp(e) { 20 | if (e.keyCode === 13) { 21 | this.onLogin() 22 | } 23 | } 24 | 25 | onLogin() { 26 | this.props.onLogin(this.props.state) 27 | } 28 | 29 | render() { 30 | return ( 31 |
    32 | {this.props.isAuth ? setTimeout(() => { 33 | this.props.history.push('/') 34 | }, 2000) : null} 35 | {this.props.msg ? : null} 36 | 37 |
    38 |

    登录

    39 |
      40 |
    • this.props.handleChange('name', e.target.value)} 41 | onKeyUp={e => this.onKeyUp(e)}/>
    • 42 |
    • this.props.handleChange('pwd', e.target.value)} 43 | onKeyUp={e => this.onKeyUp(e)}/>
    • 44 |
    • 45 | 46 |
    • 47 |
    • 注册
    • 48 |
    49 |
    50 |
    51 | ) 52 | } 53 | } 54 | 55 | export default Login -------------------------------------------------------------------------------- /packages/react/src/component/post-list/post-list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {getPosts} from 'api/post' 3 | import ListView from '../../base/list-view/list-view' 4 | import {withRouter, Link} from 'react-router-dom' 5 | 6 | import ReachBox from 'base/reach-box/reach-box' 7 | 8 | 9 | @withRouter 10 | class PostList extends React.Component { 11 | constructor(props) { 12 | super(props) 13 | this.state = { 14 | posts: [] 15 | } 16 | } 17 | 18 | loadArticles() { 19 | let status = this.props.match.params.status 20 | getPosts(status, '', '', '', 1, 100).then((res) => { 21 | if (res.data.code === 200) { 22 | this.setState({ 23 | posts: res.data.posts 24 | }) 25 | } 26 | }) 27 | } 28 | 29 | loadAuthorArticle() { 30 | getPosts('', '', '', this.props.match.params.author, 1, 100).then(res => { 31 | if (res.data.code === 200) { 32 | this.setState({ 33 | posts: res.data.posts 34 | }) 35 | } 36 | }) 37 | } 38 | 39 | refresh() { 40 | let p = this.props.location.pathname 41 | if (p === '/posts/wait' || p === '/posts/public' || p === '/posts/under') { 42 | return this.loadArticles() 43 | } 44 | this.loadAuthorArticle() 45 | } 46 | 47 | render() { 48 | return ( 49 |
    50 | 51 | 待审核 52 | 已下架 53 | 已发布 54 | 55 |
    56 | ) 57 | } 58 | 59 | componentDidMount() { 60 | this.refresh() 61 | } 62 | } 63 | 64 | export default PostList -------------------------------------------------------------------------------- /packages/react/src/component/register/register.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {register} from "api/user" 3 | import {handleForm} from "hoc/handle-form/handle-form" 4 | 5 | import TopTip from '../../base/top-tip/top-tip' 6 | import '../login/login.css' 7 | 8 | @handleForm 9 | class Home extends React.Component { 10 | constructor(props) { 11 | super(props) 12 | this.state = { 13 | msg: '', 14 | bg: '' 15 | } 16 | 17 | this.onRegister = this.onRegister.bind(this) 18 | 19 | } 20 | 21 | onRegister() { 22 | if (!this.props.state.name || !this.props.state.pwd || !this.props.state.repeatPwd || !this.props.state.qq) { 23 | this.setState({ 24 | msg: '都要填写都要填(〃>皿<)!', 25 | bg: '#ef736e' 26 | }) 27 | setTimeout(() => { 28 | this.setState({ 29 | msg: '' 30 | }) 31 | }, 4500) 32 | return 33 | } 34 | register(this.props.state).then(res => { 35 | if (res.status === 200 && res.data.code === 201) { 36 | this.setState({ 37 | msg: res.data.msg, 38 | bg: '#b4d896' 39 | }) 40 | setTimeout(() => { 41 | this.props.history.push('/login') 42 | }, 2000) 43 | } else { 44 | this.setState({ 45 | msg: res.data.msg, 46 | bg: '#ef736e' 47 | }) 48 | } 49 | }) 50 | 51 | } 52 | 53 | onKeyUp(e) { 54 | if (e.keyCode === 13) { 55 | this.onRegister() 56 | } 57 | } 58 | 59 | render() { 60 | return ( 61 |
    62 | {this.state.msg ? : null} 63 | 64 |
    65 |

    注册

    66 |
      67 |
    • this.props.handleChange('qq', e.target.value)} 68 | onKeyUp={e => this.onKeyUp(e)}/>
    • 69 |
    • this.props.handleChange('name', e.target.value)} 70 | onKeyUp={e => this.onKeyUp(e)}/>
    • 71 |
    • this.props.handleChange('pwd', e.target.value)} 72 | onKeyUp={e => this.onKeyUp(e)}/> 73 |
    • 74 |
    • this.props.handleChange('repeatPwd', e.target.value)} 76 | onKeyUp={e => this.onKeyUp(e)}/> 77 |
    • 78 |
    • 79 | 80 |
    • 81 |
    • 返回首页
    • 82 |
    83 |
    84 |
    85 | ) 86 | } 87 | } 88 | 89 | export default Home -------------------------------------------------------------------------------- /packages/react/src/component/user-info/user-info.css: -------------------------------------------------------------------------------- 1 | .user-info { 2 | padding: 200px 0; 3 | } 4 | 5 | .user-info .avatar { 6 | text-align: center; 7 | padding-bottom: 30px; 8 | 9 | } 10 | 11 | .avatar img { 12 | height: 60px; 13 | width: 60px; 14 | border-radius: 50%; 15 | } 16 | 17 | .name { 18 | text-align: center; 19 | font-weight: bold; 20 | font-size: 16px; 21 | } 22 | 23 | .uid { 24 | text-align: center; 25 | font-weight: bold; 26 | font-size: 16px; 27 | color: #fff; 28 | margin-top: 10px; 29 | } 30 | 31 | .uid span{ 32 | background: #6c43b6; 33 | padding: 0px 15px; 34 | } 35 | 36 | .quick-start { 37 | padding: 100px; 38 | color: #999; 39 | text-align: center; 40 | } 41 | 42 | .quick-start span { 43 | color: #7e5fd9; 44 | padding: 10px; 45 | } -------------------------------------------------------------------------------- /packages/react/src/component/user-info/user-info.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Link } from 'react-router-dom' 4 | import { adminAuth } from "hoc/auth/auth" 5 | import { getUser } from 'api/user' 6 | import { getAvatar } from "common/js/util" 7 | 8 | import './user-info.css' 9 | import Cookies from "js-cookie" 10 | 11 | @adminAuth 12 | class UserInfo extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.state = { 16 | user: { 17 | qq: 10010, 18 | name:'emmm', 19 | id:2 20 | } 21 | } 22 | } 23 | 24 | componentDidMount() { 25 | const uid = Cookies.get('uid') || "2" 26 | if (uid) { 27 | getUser('', uid).then(res => { 28 | this.setState({ 29 | user: res.data.result 30 | }) 31 | }) 32 | } 33 | } 34 | 35 | render() { 36 | const qq = getAvatar(this.state.user.qq) 37 | const router = `/article/` + this.state.user.id 38 | const info = `/editor-user/` + this.state.user.qq 39 | return ( 40 |
    41 |
    42 | 作者头像 43 |
    44 |
    欢迎!{this.state.user.name} sama~
    45 |
    uid : {this.state.user.id}
    46 |
    47 | 快速开始: 48 | {this.props.state.level > 1 ? 49 | 投稿 50 | : null} 51 | {this.props.state.level > 1 ? 查看稿件 : null} 52 | 设置 53 |
    54 |
    55 | ) 56 | } 57 | } 58 | 59 | export default UserInfo -------------------------------------------------------------------------------- /packages/react/src/component/user-list/user-list.css: -------------------------------------------------------------------------------- 1 | .user-list { 2 | width: 900px; 3 | margin: 50px auto; 4 | } 5 | 6 | .user-list .role { 7 | margin-bottom: 20px; 8 | } 9 | 10 | .user-list h1 { 11 | color: #7e5fd9; 12 | font-size: 24px; 13 | padding: 10px; 14 | margin-bottom: 20px; 15 | } 16 | 17 | .user-list ul { 18 | display: flex; 19 | padding: 20px; 20 | box-shadow: 0 1px 0 rgba(70, 75, 150, 0.2); 21 | background: #fff; 22 | flex-wrap: wrap; 23 | } 24 | 25 | .user-list ul li { 26 | flex: 1; 27 | padding: 20px; 28 | } 29 | 30 | .user-list .avatar { 31 | padding-bottom: 10px; 32 | text-align: center; 33 | border-radius: 50%; 34 | } 35 | 36 | .user-list .avatar img { 37 | height: 100px; 38 | width: 100px; 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /packages/react/src/component/user-list/user-list.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import {userList} from 'api/user' 4 | import {Link, withRouter} from 'react-router-dom' 5 | import ReachBox from 'base/reach-box/reach-box' 6 | 7 | import './user-list.css' 8 | 9 | @withRouter 10 | class UserList extends React.Component { 11 | constructor(props) { 12 | super(props) 13 | this.state = { 14 | users: [], 15 | uid: '' 16 | } 17 | } 18 | 19 | componentDidMount() { 20 | userList(this.props.match.params.level).then((res) => { 21 | if (res.data.code === 200) { 22 | this.setState({users: res.data.users}) 23 | } 24 | }) 25 | } 26 | 27 | render() { 28 | 29 | return ( 30 |
    31 |
    32 | 用户 33 | 作者 34 | 审核 35 |
    36 | 37 |
      38 | {this.props.location.pathname === '/users/user' ? 39 | 40 | : 41 | this.state.users.map((item) => { 42 | const qq = `http://q2.qlogo.cn/headimg_dl?dst_uin=` + item.qq + `&spec=100` 43 | return ( 44 |
    • 45 | 46 |
      47 |
      {item.name}
      48 | 49 |
    • 50 | ) 51 | })} 52 |
    53 |
    54 | 55 | ) 56 | } 57 | } 58 | 59 | export default UserList -------------------------------------------------------------------------------- /packages/react/src/component/write-article/wirte-article.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { add, getPost, update } from 'api/post' 3 | import { getVideos } from "api/video" 4 | import { adminAuth } from "hoc/auth/auth" 5 | import { Link } from 'react-router-dom' 6 | import Cookies from 'js-cookie' 7 | 8 | import Markdown from 'base/markdown/markdown' 9 | import TopTip from 'base/top-tip/top-tip' 10 | 11 | import './write-article.css' 12 | 13 | @adminAuth 14 | class WriteArticle extends React.Component { 15 | constructor(props) { 16 | 17 | super(props) 18 | this.state = { 19 | title: '', 20 | content: '', 21 | sort: '完结', 22 | status: 'wait', 23 | msg: '', 24 | uid: parseInt(Cookies.get('uid')), 25 | id: this.props.match.params.editor, 26 | text: '撰写', 27 | bg: '', 28 | tag: '', 29 | time: '', 30 | videos: [] 31 | } 32 | } 33 | 34 | componentDidMount() { 35 | this.loadArticle() 36 | this.props.location.pathname === '/write-article' ? this.setState({ text: '添加' }) : this.setState({ text: '更新' }) 37 | } 38 | 39 | handleChange(key, val) { 40 | this.setState({ 41 | [key]: val 42 | }) 43 | } 44 | 45 | changeMde(content) { 46 | this.setState({ 47 | content 48 | }) 49 | } 50 | 51 | loadArticle() { 52 | if (this.state.id) { 53 | getPost(this.state.id).then((res) => { 54 | const { title, content, sort, tag, status, time, videos } = res.data.result 55 | this.setState({ 56 | title, 57 | content, 58 | sort, 59 | tag, 60 | status, 61 | time: time.slice(0, -3), 62 | videos, 63 | defaultValue: content 64 | }) 65 | }) 66 | } 67 | } 68 | 69 | handleClick() { 70 | if (!this.state.title || !this.state.content || !this.state.status || !this.state.sort || !this.state.tag) { 71 | this.setState({ 72 | msg: '都要填写都要填(〃>皿<)!', 73 | bg: '#ef736e' 74 | }) 75 | setTimeout(() => { 76 | this.setState({ 77 | msg: '' 78 | }) 79 | }, 5000) 80 | return 81 | } 82 | 83 | if (this.props.location.pathname !== '/write-article') { 84 | update(this.state).then((res) => { 85 | if (res.data.code === 200) { 86 | this.setState({ 87 | msg: '更新成功啦!', 88 | text: '更新', 89 | bg: '#b4d896' 90 | }) 91 | } 92 | setTimeout(() => { 93 | this.props.history.push(`/article/${this.state.uid}`) 94 | this.setState({ 95 | msg: '' 96 | }) 97 | }, 5000) 98 | }) 99 | } else { 100 | add(this.state).then((res) => { 101 | if (res.data.code === 200) { 102 | this.setState({ 103 | msg: '添加成功啦', 104 | bg: '#b4d896' 105 | }) 106 | } 107 | setTimeout(() => { 108 | this.props.history.push(`/article/${this.state.uid}`) 109 | this.setState({ 110 | msg: '' 111 | }) 112 | }, 5000) 113 | }) 114 | } 115 | } 116 | 117 | selectTag(item) { 118 | if (this.state.tag.indexOf(item) > -1) { 119 | this.setState({ 120 | tag: this.state.tag.replace(` ${item}`, '') 121 | }) 122 | } else { 123 | this.setState({ 124 | tag: this.state.tag + ' ' + item 125 | }) 126 | } 127 | } 128 | 129 | render() { 130 | const tags = ['推荐', '转载', '漫画改', '小说改', '耽美', '乙女', '百合', '后宫', '热血', '战斗', '运动', '奇幻', '神魔', 131 | '搞笑', '冒险', '校园', '恐怖', '穿越', '推理', '科幻', '日常', '古风', '恋爱', 'r15', '泡面番', '治愈', 132 | '鬼畜', 'AMV/MAD', '音乐·PV', '游戏·GMV', 'VOCALOID', '影视', 133 | '特摄', '真人剧', '其它'] 134 | return ( 135 |
    {this.state.msg ? : null} 136 |
    137 |

    {this.state.text}稿件

    138 |
  • this.handleChange('title', e.target.value)} />
  • 141 | this.changeMde(content)} 142 | value={this.state.content} defaultValue={this.state.defaultValue} /> 143 |
    144 |
    分P
    145 | 148 |
    149 | 150 | 158 | 166 | {this.props.location.pathname === '/write-article' ? null : this.handleChange('time', e.target.value)} />} 168 |
    169 |
      170 | {tags.map((item, index) =>
    • this.selectTag(item)} key={index} 171 | className={this.state.tag.indexOf(item) > -1 ? 'active' : ''}>{item}
    • )} 172 |
    173 |
    174 | 175 |
    176 | 177 |
    178 |
    179 |
    180 | ) 181 | } 182 | } 183 | 184 | 185 | export default WriteArticle -------------------------------------------------------------------------------- /packages/react/src/component/write-article/write-article.css: -------------------------------------------------------------------------------- 1 | .write-article { 2 | width: 800px; 3 | margin: 100px auto; 4 | } 5 | 6 | .write-article h1 { 7 | color: #7e5fd9; 8 | font-size: 24px; 9 | margin: 20px 0; 10 | } 11 | 12 | .write-article li input { 13 | padding: 10px; 14 | width: 100%; 15 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.2); 16 | border: 0; 17 | margin: 10px 0; 18 | box-sizing: border-box; 19 | } 20 | .write-article span input { 21 | padding: 4px 10px; 22 | border: 0; 23 | background: #ffeec7; 24 | box-sizing: border-box; 25 | border-radius: 4px; 26 | outline: none; 27 | } 28 | .write-article button { 29 | background: #7e5fd9; 30 | padding: 8px 30px; 31 | color: #fff; 32 | margin: 20px 0; 33 | border: 0; 34 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.6); 35 | cursor: pointer; 36 | float: right; 37 | } 38 | 39 | .write-article select { 40 | padding: 5px 17px; 41 | box-shadow: 1px 1px 1px rgba(70, 75, 150, 0.2); 42 | border: 0; 43 | color: #666; 44 | margin: 20px 20px 20px 0; 45 | position: relative; 46 | top: 3px; 47 | } 48 | 49 | .write-article span { 50 | color: #666; 51 | font-size: 13px; 52 | } 53 | 54 | .write-article .video-list li { 55 | height: 40px; 56 | width: 40px; 57 | text-align: center; 58 | background: #fff; 59 | list-style: none; 60 | display: inline-block; 61 | margin: 10px 15px 0 0; 62 | padding: 10px; 63 | box-sizing: border-box; 64 | border-radius: 50%; 65 | border: 1px solid #7e5fd9; 66 | } 67 | 68 | .write-article .video-list .add { 69 | font-weight: bold; 70 | background: #7e5fd9; 71 | color: #fff; 72 | } 73 | 74 | .write-article .video-list .add div { 75 | position: relative; 76 | top: -2px; 77 | } 78 | 79 | .write-article .tags { 80 | padding-bottom: 20px; 81 | } 82 | 83 | .write-article .videos-area{ 84 | white-space: pre; 85 | word-wrap: break-word; 86 | overflow: scroll; 87 | outline: none; 88 | } 89 | 90 | .write-article .tags li { 91 | display: inline-block; 92 | padding: 2px 10px; 93 | background: #e0d8f7;; 94 | color: #6c43b6; 95 | border-radius: 4px; 96 | margin: 10px 10px 0 0; 97 | cursor: pointer; 98 | } 99 | 100 | .write-article .tags .active { 101 | background: #6c43b6; 102 | color: #fff; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /packages/react/src/hoc/auth/auth.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import {API_LINK} from "common/js/util" 3 | import {auth} from 'api/user' 4 | import Cookies from 'js-cookie' 5 | 6 | export function adminAuth(Component) { 7 | return class WrapperComp extends React.Component { 8 | constructor(props) { 9 | super(props) 10 | this.state = { 11 | level: 1 12 | } 13 | } 14 | 15 | componentDidMount() { 16 | auth().then(res => { 17 | if (res.data.code === 401) { 18 | this.props.history.push('/login') 19 | } else { 20 | this.setState({ 21 | level: Cookies.get('level') 22 | }) 23 | } 24 | }) 25 | } 26 | 27 | render() { 28 | return 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /packages/react/src/hoc/handle-form/handle-form.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | // 表单提交公共方法 4 | export function handleForm(Component) { 5 | return class WrapperComp extends React.Component { 6 | constructor(props) { 7 | super(props) 8 | this.state = {} 9 | this.handleChange = this.handleChange.bind(this) 10 | } 11 | 12 | handleChange(key, val) { 13 | this.setState({ 14 | [key]: val 15 | }) 16 | } 17 | 18 | render() { 19 | return 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /packages/react/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | clicli后台 6 | 7 | 8 | 9 |
    10 |
    11 | 12 | -------------------------------------------------------------------------------- /packages/react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDom from 'react-dom' 3 | import {BrowserRouter} from 'react-router-dom' 4 | 5 | 6 | import App from './app' 7 | 8 | import './common/style/reset.css' 9 | import './common/style/index.css' 10 | import './common/style/iconfont.css' 11 | 12 | import {store} from './store/index' 13 | import {Provider} from 'smox' 14 | 15 | 16 | ReactDom.render(( 17 | 18 | 19 | 20 | 21 | 22 | 23 | ), document.getElementById('app')) 24 | 25 | -------------------------------------------------------------------------------- /packages/react/src/store/actions.js: -------------------------------------------------------------------------------- 1 | import {login, logout} from "api/user" 2 | 3 | export default { 4 | onLogin({commit}, user) { 5 | if (!user.name || !user.pwd) { 6 | commit('errMsg', '都要填都要填!') 7 | setTimeout(() => { 8 | commit('errMsg', '') 9 | }, 4500) 10 | } 11 | return login(user).then(res => { 12 | commit('errMsg', '') 13 | if (res.status === 200) { 14 | commit('login', res.data) 15 | } else { 16 | commit('errMsg', res.data.msg) 17 | setTimeout(() => { 18 | commit('errMsg', '') 19 | }, 4500) 20 | } 21 | }) 22 | }, 23 | onLogout({commit}) { 24 | return logout().then(res => { 25 | if (res.data.code === 200) { 26 | commit('logout', res.data) 27 | setTimeout(() => { 28 | commit('errMsg', '') 29 | }, 4500) 30 | } 31 | }) 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /packages/react/src/store/index.js: -------------------------------------------------------------------------------- 1 | import { Store } from 'smox' 2 | import state from './state' 3 | import mutations from './mutations' 4 | import actions from './actions' 5 | 6 | export const store = new Store({ 7 | state, 8 | mutations, 9 | actions 10 | }) 11 | -------------------------------------------------------------------------------- /packages/react/src/store/mutations.js: -------------------------------------------------------------------------------- 1 | export default { 2 | login(state, data) { 3 | state.msg = data.msg 4 | state.isAuth = true 5 | state.bg = '#b4d896' 6 | }, 7 | errMsg(state, msg) { 8 | state.msg = msg 9 | state.bg = '#ef736e' 10 | }, 11 | logout(state) { 12 | state.isAuth = false 13 | state.msg = '退出成功啦' 14 | state.bg = '#b4d896' 15 | } 16 | } -------------------------------------------------------------------------------- /packages/react/src/store/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | isAuth: false, 3 | redirectTo: '', 4 | bg: '', 5 | msg: '', 6 | user:{} 7 | } 8 | -------------------------------------------------------------------------------- /packages/react/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin") 4 | const webpack = require('webpack') 5 | 6 | module.exports = { 7 | entry: './src/index.js', 8 | output: { 9 | path: path.resolve(__dirname, 'dist'), 10 | filename: '[name].js', 11 | publicPath:'/' 12 | }, 13 | resolve: { 14 | alias: { 15 | component: path.resolve(__dirname, 'src/component'), 16 | common: path.resolve(__dirname, 'src/common'), 17 | api: path.resolve(__dirname, 'src/api'), 18 | base: path.resolve(__dirname, 'src/base'), 19 | store: path.resolve(__dirname, 'src/store'), 20 | hoc: path.resolve(__dirname, 'src/hoc') 21 | } 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.js$/, 27 | exclude: /node_modules/, 28 | use: { 29 | loader: 'babel-loader' 30 | } 31 | }, 32 | { 33 | test: /\.css$/, 34 | use: [ 35 | MiniCssExtractPlugin.loader, 36 | "css-loader" 37 | ] 38 | 39 | }, 40 | { 41 | test: /\.(png|jpg|gif)$/, 42 | use: [ 43 | { 44 | loader: 'url-loader', 45 | options: { 46 | limit: 8192, 47 | name: 'static/img/[name].[ext]' 48 | } 49 | } 50 | ] 51 | } 52 | ] 53 | }, 54 | optimization: { 55 | splitChunks: { 56 | chunks: 'all' 57 | }, 58 | runtimeChunk: false, 59 | minimize:false 60 | }, 61 | plugins: [ 62 | new webpack.HotModuleReplacementPlugin(), 63 | new HtmlWebpackPlugin({ 64 | template: './src/index.html' 65 | }), 66 | new MiniCssExtractPlugin({ 67 | filename: "[name].css" 68 | }), 69 | ], 70 | devServer: { 71 | headers: {'Access-Control-Allow-Origin': '*'}, 72 | contentBase: path.join(__dirname, "dist"), 73 | compress: true, 74 | port: 1122, 75 | historyApiFallback: true, 76 | hot: true, 77 | proxy: { 78 | '/api/*': { 79 | pathRewrite: {'^/api': ''}, 80 | target: 'https://api.clicli.cc' 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.3 2 | 3 | importers: 4 | 5 | .: 6 | specifiers: 7 | react: ^0.0.0-experimental-1314299c7-20210901 8 | react-dom: ^0.0.0-experimental-1314299c7-20210901 9 | dependencies: 10 | react: 0.0.0-experimental-1314299c7-20210901 11 | react-dom: 0.0.0-experimental-1314299c7-20210901_e51ce3ac058c0d2f1ad274f789274e97 12 | 13 | packages/create-nfes: 14 | specifiers: 15 | '@ctrip/nfes': workspace:^1.0.0 16 | dependencies: 17 | '@ctrip/nfes': link:../nfes 18 | 19 | packages/nfes: 20 | specifiers: 21 | '@types/node': ^16.7.12 22 | esbuild: ^0.12.25 23 | esbuild-register: ^3.0.0 24 | fs-extra: ^10.0.0 25 | polka: ^0.5.2 26 | dependencies: 27 | fs-extra: 10.0.0 28 | polka: 0.5.2 29 | devDependencies: 30 | '@types/node': 16.7.12 31 | esbuild: 0.12.25 32 | esbuild-register: 3.0.0_esbuild@0.12.25 33 | 34 | packages: 35 | 36 | /@arr/every/1.0.1: 37 | resolution: {integrity: sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==} 38 | engines: {node: '>=4'} 39 | dev: false 40 | 41 | /@polka/url/0.5.0: 42 | resolution: {integrity: sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==} 43 | dev: false 44 | 45 | /@types/node/16.7.12: 46 | resolution: {integrity: sha512-IrhrusVM9QJAn1xLFFqbZH+XCI8L6eZoVHjR4sZeGTBBvjQSuchXGgKpown1jP75TCMSndcbudYBDTekOhcIZA==} 47 | dev: true 48 | 49 | /esbuild-register/3.0.0_esbuild@0.12.25: 50 | resolution: {integrity: sha512-No7U3ZUd6gPrrC6gqdb3XFcf2lNqzn8nvQXcgcyOl8szMVuN6YUvOplnmakxWyogI9d8SiJMl0uzBzJck+Aoxw==} 51 | peerDependencies: 52 | esbuild: '>=0.12 <1' 53 | dependencies: 54 | esbuild: 0.12.25 55 | jsonc-parser: 3.0.0 56 | dev: true 57 | 58 | /esbuild/0.12.25: 59 | resolution: {integrity: sha512-woie0PosbRSoN8gQytrdCzUbS2ByKgO8nD1xCZkEup3D9q92miCze4PqEI9TZDYAuwn6CruEnQpJxgTRWdooAg==} 60 | hasBin: true 61 | requiresBuild: true 62 | dev: true 63 | 64 | /fs-extra/10.0.0: 65 | resolution: {integrity: sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==} 66 | engines: {node: '>=12'} 67 | dependencies: 68 | graceful-fs: 4.2.8 69 | jsonfile: 6.1.0 70 | universalify: 2.0.0 71 | dev: false 72 | 73 | /graceful-fs/4.2.8: 74 | resolution: {integrity: sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==} 75 | dev: false 76 | 77 | /js-tokens/4.0.0: 78 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 79 | dev: false 80 | 81 | /jsonc-parser/3.0.0: 82 | resolution: {integrity: sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==} 83 | dev: true 84 | 85 | /jsonfile/6.1.0: 86 | resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} 87 | dependencies: 88 | universalify: 2.0.0 89 | optionalDependencies: 90 | graceful-fs: 4.2.8 91 | dev: false 92 | 93 | /loose-envify/1.4.0: 94 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 95 | hasBin: true 96 | dependencies: 97 | js-tokens: 4.0.0 98 | dev: false 99 | 100 | /matchit/1.1.0: 101 | resolution: {integrity: sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==} 102 | engines: {node: '>=6'} 103 | dependencies: 104 | '@arr/every': 1.0.1 105 | dev: false 106 | 107 | /object-assign/4.1.1: 108 | resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=} 109 | engines: {node: '>=0.10.0'} 110 | dev: false 111 | 112 | /polka/0.5.2: 113 | resolution: {integrity: sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==} 114 | dependencies: 115 | '@polka/url': 0.5.0 116 | trouter: 2.0.1 117 | dev: false 118 | 119 | /react-dom/0.0.0-experimental-1314299c7-20210901_e51ce3ac058c0d2f1ad274f789274e97: 120 | resolution: {integrity: sha512-hKxIBZb0oZYUoFaDuhcwIG+Xtk/3bkd+Jtuf0KXakKKFGVrPTR77S9sJDW2mTqADDP+5oKHm8vYONE0Ua3z38Q==} 121 | peerDependencies: 122 | react: 0.0.0-experimental-1314299c7-20210901 123 | dependencies: 124 | loose-envify: 1.4.0 125 | object-assign: 4.1.1 126 | react: 0.0.0-experimental-1314299c7-20210901 127 | scheduler: 0.0.0-experimental-1314299c7-20210901 128 | dev: false 129 | 130 | /react/0.0.0-experimental-1314299c7-20210901: 131 | resolution: {integrity: sha512-A7oXsm7J0SerEWEwt5j6No3jDFaWAhgBerhwSWh2Si81opJb4UpgQ8LfgtLoHItK3VFlVySH2WCViKAVbOzbNQ==} 132 | engines: {node: '>=0.10.0'} 133 | dependencies: 134 | loose-envify: 1.4.0 135 | object-assign: 4.1.1 136 | dev: false 137 | 138 | /scheduler/0.0.0-experimental-1314299c7-20210901: 139 | resolution: {integrity: sha512-ZE2wFqqPnGL2d4GRgZ/0AEwBSpg7fHOe+nY5dZFI/RpjDZmKN+13/Yldn/sM5Fz0SyqliqmMy4vQzyb+ndBi5g==} 140 | dependencies: 141 | loose-envify: 1.4.0 142 | object-assign: 4.1.1 143 | dev: false 144 | 145 | /trouter/2.0.1: 146 | resolution: {integrity: sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==} 147 | engines: {node: '>=6'} 148 | dependencies: 149 | matchit: 1.1.0 150 | dev: false 151 | 152 | /universalify/2.0.0: 153 | resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} 154 | engines: {node: '>= 10.0.0'} 155 | dev: false 156 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | # all packages in subdirs of packages/ and components/ 3 | - 'packages/**' --------------------------------------------------------------------------------