DoraCMS 2.1.3 😈😈😈

This commit is contained in:
doramart 2019-08-06 21:59:16 +08:00
parent b9cbb2f537
commit 3138acf1fb
1019 changed files with 18537 additions and 15502 deletions

3
.babelrc Normal file → Executable file
View File

@ -22,7 +22,8 @@
"transform-object-rest-spread",
"transform-vue-jsx",
"transform-remove-strict-mode",
"syntax-dynamic-import"
"syntax-dynamic-import",
"transform-es2015-modules-commonjs"
]
},
"development": {

View File

@ -1,3 +0,0 @@
**/*.min.js
**/node_modules/*
**/build/*

View File

@ -1,29 +0,0 @@
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
"extends": "eslint:recommended",
"parserOptions": {
"sourceType": "module"
},
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"warin",
"double"
],
"semi": [
"error",
"always"
]
}
};

6
.gitignore vendored Normal file → Executable file
View File

@ -1,4 +1,5 @@
.DS_Store
package-lock.json
node_modules/
npm-debug.log
.vscode/
@ -6,5 +7,6 @@ npm-debug.log
admin.lock
server/config/secret.js
public/upload/
public/apidoc/
logs/
logs/
dist/
.cache

300
README.md Normal file → Executable file
View File

@ -1,136 +1,151 @@
# doracms 2.1.2
# DoraCMS 2.1.3
![DoraCMS](https://www.html-js.cn/upload/images/ueditor/1056072974769197056.jpg "DoraCMS")
![DoraCMS](https://www.html-js.cn/upload/images/ueditor/1041325089330696192.png "DoraCMS")
## 2.1.2版本更新
1、前台接口独立抽取api
## 2.1.3 版本更新
1、优化了代码整体的目录结构 ++[重要]++
2、完善国际化逻辑
2、修复了后台文章列表编辑时文章作者和文章分类无法带出的问题
3、细分了文章审核的几种状态
3、重构了服务端代码使其具有前后台api分离的能力 ++[重要]++
4、优化了模板编辑模块修复模板编辑bug加入了模板导入功能更方便的进行新模板开发
4、支持最新稳定版nodejs已通过 nodejs v10.15.0测试)++[重要]++
5、添加了帮助中心和APP版本管理模块
5、优化打包脚本npm run build 执行速度提升30%
6、优化了登录和注册模块
6、优化开发模式下修改文件后的自动重启速度
7、优化文章模块添加文章自动解析为APP可以识别的JSON格式
7、默认redis开关关闭可以不开启。 ++[重要]++
8、对查询比较多的模块如文章分类留言等设置了索引
9、修复了一些内部报错引起的服务端自动重启问题
10、修复了一些安全性方面的问题
[更多升级细节](https://www.html-js.cn/details/ryJwj5rTE.html)
[使用Docker部署DoraCMS](https://www.html-js.cn/details/Bkw5AepT4.html)
8、修复了一些其他bug
## 2.0.0版本更新
1、生产环境日志目录可配置修复用户部署生产报路径找不到的问题
2、新增广告管理模块修复多图轮播显示不正常的问题
3、修复删除文章没有同步删除关联留言的问题
注意:
1、如果在开发环境下只涉及到服务端调试请使用
```javascript
npm run server
```
4、更新前端后台样式
5、修复留言过滤特殊字符的问题
6、添加了留言回复功能
7、修复文章上传缩略图某些jpg文件无法上传的问题
8、完善了后台首页信息总览
9、修复了后台修改其它管理员权限会把自己的权限置空的问题
更新方法:
1、checkout 最新 2.0.0 代码
2、删除 node_modules,重新安装依赖包
3、找到 configs/settings.js将 SYSTEMLOGPATH 参数值修改为正式服务器上(生产环境)的日志目录(very important!)
3、启动数据库执行npm run dev
如果是普通调试,依然是
```javascript
npm run dev
```
2、api开发请按照当前代码中的规范开发完成后执行生成api文档
```javascript
npm run makePrdDoc
```
通过如下方式访问
```javascript
http://localhost:8080/apidoc
```
## 说明
DoraCMS 使用的技术栈:
### DoraCMS 使用的技术栈:
```
1、vue + vuex + vue-router 全家桶
2、webpack 2
3、nodejs 10.15.0 + express 4
4、mongodb 4+
```
3、nodejs 8.0 + express 4
演示地址: [前端开发俱乐部](https://www.html-js.cn)
4、mongodb 3+
后台登录: https://www.html-js.cn/dr-admin
测试账号doracms/123456
演示地址: [前端开发俱乐部](https://www.html-js.cn)
后台登录: https://www.html-js.cn/dr-admin 测试账号doracms/123456
[DoraCMS 2.1.3 尝鲜体验](https://www.html-js.cn/details/VmnGNiF4S.html)
[DoraCMS v2.1.2 Docker 版本(生产环境)](https://www.html-js.cn/details/Bkw5AepT4.html)
部署文档: [前端内容管理框架 DoraCMS2.0 部署介绍](https://www.html-js.cn/details/ryn2kSWqZ.html)
## 目录结构
```
```javascript
├─build // webpack 相关配置文件
├─configs // 配置文件
│ │
│ └─logConfig.js // 日志配置文件
├─logs // 日志目录
├─dist // webpack 生成文档存放目录
│ │
│ ├─server
│ │
│ └─static
│ ├─css
│ │
│ ├─images
│ │
│ ├─img
│ │
│ └─js
├─server // 服务端目录
│ │
│ ├─lib // 核心层
│ │
│ └─routes // 路由文件
├─src // 客户端程序目录
│ │
│ ├─api // api 配置文件
│ │
│ ├─filters // 过滤器
├─client // 客户端文件(前台/后台)
│ │
│ ├─index // 前台组件
│ │
│ ├─manage // 后台组件
│ │
│ ├─template // 初始模版
│ │
│ └─utils // 实用工具
│ └─template // 初始模版
└─utils
├─middleware // 中间件
├─authPower.js // 资源鉴权
├─authSession.js // session 鉴权
├─authToken.js // token鉴权
├─logUtil.js // 日志配置
├─settings.js // 关键信息配置
└─validatorUtil.js // 信息校验
├─databak // 默认数据备份目录
├─logs // 日志目录
├─public // 静态文件目录
│ │
│ ├─admin // 后台vue编译后的文件目录
│ │
│ ├─apidoc // api文档目录
│ │
│ ├─plugins // 前台依赖的相关组件
│ │
│ ├─themes // 皮肤目录
│ │
│ ├─ueditor // ueditor插件目录
│ │
│ ├─upload // 文件上传目录
│ │
│ └─vendor // 后台静态dll目录
├─server // 服务端目录
│ │
│ ├─bootstrap // 前台渲染相关
│ │
│ ├─configs // 系统配置
│ │
│ ├─locales // 国际化
│ │
│ ├─middleware // 中间件
│ │
│ │
│ ├─lib // 核心层
│ │ ├─contorller // 控制器
│ │ │
│ │ ├─model // 数据模型
│ │ │
│ │ ├─service // 数据库操作
│ │ │
│ │ └─utils
│ │ ├─cache // redis缓存
│ │ │
│ │ ├─memoryCache // 内存缓存
│ │ │
│ │ ├─authPower.js // 资源鉴权
│ │ │
│ │ ├─authSession.js // session 鉴权
│ │ │
│ │ ├─authToken.js // token鉴权
│ │ │
│ │ ├─mime.js // 文件类型
│ │ │
│ │ ├─siteFunc.js // 公共方法
│ │ │
│ │ └─validatorUtil.js // 信息校验
│ │
│ │
│ └─routers // 路由
└─views // 前台模板
├─dorawhite // 主题目录
├─admin.html // 后台管理模板
└─adminUserLogin.html // 后台登录模板
```
@ -138,37 +153,86 @@ DoraCMS 使用的技术栈:
## 准备工作:
安装 NodeJS:
## 开发环境准备工作:
### 安装最新稳定版 NodeJS:
```javascript
https://nodejs.org/zh-cn/
安装 Mongodb:
https://www.mongodb.com/download-center#community
```shell
# 安装依赖
$ npm install
# 开发模式
$ npm run dev
# 生产模式
$ npm run build
# 启动(需先生成静态文件)
$ npm run start
```
首页
### 安装并启动 Mongodb (++mongodb不要设置密码访问++)
```javascript
https://www.mongodb.com/download-center#community
```
### 安装全局依赖
```javascript
npm install pm2 -g // nodejs进程守护
npm install apidoc -g // api 文档生成
npm install gulp -g // 脚本构建
npm install nodemon -g // nodejs 代码监控
```
### 安装本系统依赖(代码根目录)
```javascript
npm install
```
### 初始化数据
```javascript
npm run init
```
### 设置环境变量以mac为例修改 .bash_profile文件
```javascript
vi ~/.bash_profile
export NODE_ENV=development
MONGODBPATH=/Users/Dora/Documents/dora/soft/mongodb/bin
PATH="${MONGODBPATH}:${PATH}"
export PATH
source ~/.bash_profile
```
> 以上步骤做了两件事情:
1、设置nodejs环境变量为 development,生产环境记得改为 production
2、将mongodb bin 目录添加到全局变量中便于在终端的任何位置执行mongo脚本,注意改成自己安装mongodb的实际路径
### 开发模式启动
```javascript
npm run dev
```
### 生产模式打包
```javascript
npm run build
```
### 生产模式启动(进入代码根目录执行)
```javascript
node server.js
```
### 首页
```javascript
http://localhost:8080
```
登录
### 后台登录
```javascript
http://localhost:8080/dr-admin
登录账号doramart/123456 doracms/123456
```
# 捐赠
## 捐赠
如果你发现DoraCMS很有用可以请生哥喝杯咖啡(⊙o⊙)哦
<img width="650" src="http://cdn.html-js.cn/payme.jpg" alt="">
# LICENSE
MIT

9
build/copy.js Normal file → Executable file
View File

@ -1,7 +1,6 @@
require('shelljs/global')
rm('-rf', './dist/')
mkdir('-p', './dist/')
cp('-R', 'favicon.ico', './dist/')
cp('-R', 'robots.txt', './dist/')
// cp('-R', 'static/ueditor/', './dist/ueditor/')
rm('-rf', './public/admin/')
mkdir('-p', './public/admin/')
cp('-R', 'favicon.ico', './public/')
cp('-R', 'robots.txt', './public/')

3
build/copyAdminTemp.js Normal file → Executable file
View File

@ -1,5 +1,4 @@
require('shelljs/global')
rm('-rf', './views/admin.html')
cp('-R', './dist/admin.html', './views/')
cp('-R', './public/admin/admin.html', './views/')

0
build/dev-client.js Normal file → Executable file
View File

28
build/dll.js Executable file
View File

@ -0,0 +1,28 @@
// Package common library, like vue, element-ui etc.
const path = require('path');
const webpack = require('webpack');
// 这里将vue和element-ui单独打包
const vendors = ['vue', 'vuex', 'axios', 'vue-router', 'element-ui', 'lodash'];
module.exports = {
entry: {
vendor: vendors
},
output: {
path: path.join(__dirname, '../public/vendor/'),
filename: '[name].dll.js',
library: '[name]'
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new webpack.DllPlugin({
path: path.join(__dirname, '../public/vendor', '[name]-manifest.json'),
name: '[name]',
context: __dirname
})
]
};

27
build/dump.js Normal file → Executable file
View File

@ -1,15 +1,28 @@
// 备份DB数据
require('shelljs/global');
const settings = require("../configs/settings");
const isDev = process.env.NODE_ENV == 'development' ? true : false;
require('shelljs/global')
require('module-alias/register')
const muri = require('muri')
const moment = require("moment");
const settings = require('@configs/settings');
let databackforder = process.cwd() + '/databak/';
let dataPath = databackforder + moment().format('YYYYMMDDHHmmss').toString();;
let dataPath = databackforder + moment().format('YYYYMMDDHHmmss').toString();
let cmdstr = isDev ? 'mongodump -d ' + settings.DB + ' -o "' + dataPath + '"' : 'mongodump -u ' + settings.USERNAME + ' -p ' + settings.PASSWORD + ' -d ' + settings.DB + ' -o "' + dataPath + '"';
const mongoUri = settings.mongo_connection_uri
const parsedUri = muri(mongoUri)
const parameters = []
exec(cmdstr).stdout;
if (parsedUri.auth) {
parameters.push(`-u "${parsedUri.auth.user}"`, `-p "${parsedUri.auth.pass}"`)
}
if (parsedUri.db) {
parameters.push(`-d "${parsedUri.db}"`)
}
parameters.push(`-o "${dataPath}"`)
const cmd = `mongodump ${parameters.join(' ')}`
exec(cmd).stdout;

8
build/renderApiDoc.js Normal file → Executable file
View File

@ -1,23 +1,19 @@
var fs = require('fs');
var settings = require('../configs/settings');
var apiDocHtmlPath = './public/apidoc/api_data.json'
let apiDocPathArr = ['./public/apidoc/api_data.json', './public/apidoc/api_data.js', './public/apidoc/api_project.json', './public/apidoc/api_project.js', './public/shopApidoc/api_data.json', './public/shopApidoc/api_data.js', './public/shopApidoc/api_project.json', './public/shopApidoc/api_project.js'];
let docState = "dev"; // dev 或 prd 可选
require('shelljs/global')
let argvs = process.argv;
// console.log('----argvs[0]---', argvs);
if (argvs[2] == '--docv') {
docState = argvs[3];
}
// console.log('--docState---', docState)
if (docState == 'prd') {
for (const targetPath of apiDocPathArr) {
if (fs.existsSync(targetPath)) {
let apiDocHtmlText = fs.readFileSync(targetPath, 'utf-8');
var reg = new RegExp('localhost:8080', "g")
var newApiHtmlText = apiDocHtmlText.replace(reg, 'www.html-js.cn');
var reg = new RegExp('http://localhost:8080', "g")
var newApiHtmlText = apiDocHtmlText.replace(reg, 'https://www.html-js.cn');
fs.writeFileSync(targetPath, newApiHtmlText);
}
}
cp('-R', './public/themes/dorawhite/js/main.js', './public/apidoc/')
}

View File

@ -1,26 +0,0 @@
var fs = require('fs');
var settings = require('../configs/settings');
var manageHtmlPath = './dist/admin.html'
var cdnPath = settings.origin;
var checkMe = function () {
return new Promise((resolve, reject) => {
fs.exists(manageHtmlPath, function (exists) {
if (exists) {
resolve(1);
} else {
checkMe();
}
})
})
};
if (settings.openqn && settings.assetsCdn) {
checkMe().then((data) => {
if (data == 1) {
var adminText = fs.readFileSync(manageHtmlPath, 'utf-8');
var newAdminContent = adminText.replace(/\/static\/js\/element/, cdnPath + '/element');
fs.writeFileSync(manageHtmlPath, newAdminContent);
}
})
}

34
build/restore.js Normal file → Executable file
View File

@ -1,12 +1,30 @@
// 导入DB数据
require('shelljs/global');
const moment = require('moment');
const setting = require("../configs/settings");
require('module-alias/register')
const muri = require('muri')
const settings = require('@configs/settings');
const dbforder = '20180531145629';
const dbforder = 'doracms2';
if (process.env.NODE_ENV == 'production') {
exec(`mongorestore -d ${setting.DB} -h ${setting.HOST} --port ${setting.PORT} -u ${setting.USERNAME} -p ${setting.PASSWORD} --drop ./databak/${dbforder}/${setting.DB}`).stdout;
} else {
exec(`mongorestore -h 127.0.0.1:${setting.PORT} -d ${setting.DB} --drop ./databak/${dbforder}/${setting.DB}`).stdout;
}
const mongoUri = settings.mongo_connection_uri
const parsedUri = muri(mongoUri)
const parameters = []
if (parsedUri.hosts && parsedUri.hosts.length) {
const host = parsedUri.hosts[0]
parameters.push(`-h ${host.host}`, `--port ${host.port}`)
}
if (parsedUri.auth) {
parameters.push(`-u "${parsedUri.auth.user}"`, `-p "${parsedUri.auth.pass}"`)
}
if (parsedUri.db) {
parameters.push(`-d "${parsedUri.db}"`)
}
parameters.push(`--drop ./databak/${dbforder}/${parsedUri.db}`)
const cmd = `mongorestore ${parameters.join(' ')}`
exec(cmd).stdout;

71
build/setup-dev-server.js Normal file → Executable file
View File

@ -2,35 +2,58 @@ const path = require('path')
const webpack = require('webpack')
const MFS = require('memory-fs')
const clientConfig = require('./webpack.client.config')
const fs = require('fs');
let argvs = process.argv,
renderBackend = true;
for (const argItem of argvs) {
if (argItem == 'model=justServer') {
renderBackend = false;
break;
}
}
module.exports = function setupDevServer(app, callback) {
let backend
// modify client config to work with hot middleware
clientConfig.entry.admin = ['./build/dev-client', clientConfig.entry.admin]
clientConfig.output.filename = '[name].js'
clientConfig.plugins.push(new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin())
let backend;
if (renderBackend) {
// modify client config to work with hot middleware
clientConfig.entry.admin = ['./build/dev-client', clientConfig.entry.admin]
clientConfig.output.filename = '[name].js'
clientConfig.plugins.push(new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin())
// dev middleware
const clientCompiler = webpack(clientConfig)
const devMiddleware = require('webpack-dev-middleware')(clientCompiler, {
publicPath: clientConfig.output.publicPath,
noInfo: true
})
app.use(devMiddleware)
clientCompiler.plugin('done', () => {
const fs = devMiddleware.fileSystem
// dev middleware
const clientCompiler = webpack(clientConfig)
const devMiddleware = require('webpack-dev-middleware')(clientCompiler, {
publicPath: clientConfig.output.publicPath,
noInfo: true
})
app.use(devMiddleware)
clientCompiler.plugin('done', () => {
const fs = devMiddleware.fileSystem
// 后台
const backendPath = path.join(clientConfig.output.path, 'admin.html')
// 后台
let backendPath = path.join(clientConfig.output.path, 'admin.html')
if (fs.existsSync(backendPath)) {
backend = fs.readFileSync(backendPath, 'utf-8')
}
callback({
backend,
justServer: false
})
})
// hot middleware
app.use(require('webpack-hot-middleware')(clientCompiler))
} else {
let backendPath = path.join(clientConfig.output.path, 'admin.html')
if (fs.existsSync(backendPath)) {
backend = fs.readFileSync(backendPath, 'utf-8')
}
callback({ backend })
})
// hot middleware
app.use(require('webpack-hot-middleware')(clientCompiler))
}
callback({
backend,
justServer: true
})
}
}

0
build/utils.js Normal file → Executable file
View File

0
build/vue-loader.config.js Normal file → Executable file
View File

23
build/webpack.base.config.js Normal file → Executable file
View File

@ -2,19 +2,23 @@ const path = require('path')
const webpack = require('webpack')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const isProd = process.env.NODE_ENV === 'production'
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
const config = {
performance: {
maxEntrypointSize: 300000,
hints: isProd ? 'warning' : false
hints: "warning", // 枚举
maxAssetSize: 30000000, // 整数类型(以字节为单位)
maxEntrypointSize: 50000000, // 整数类型(以字节为单位)
assetFilter: function (assetFilename) {
// 提供资源文件名的断言函数
return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
}
},
entry: {
admin: './src/manage/admin.js'
admin: './client/manage/admin.js'
},
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: '/',
path: path.resolve(__dirname, '../public/admin'),
publicPath: '/admin/',
filename: 'static/js/[name].[chunkhash:7].js',
chunkFilename: 'static/js/[name].[chunkhash:7].js',
},
@ -24,7 +28,7 @@ const config = {
path.join(__dirname, '../node_modules')
],
alias: {
'@': path.join(__dirname, '../src/manage'),
'@': path.join(__dirname, '../client/manage'),
'~server': path.resolve(__dirname, '../server')
}
},
@ -47,11 +51,10 @@ const config = {
}]
},
plugins: [
new LodashModuleReplacementPlugin,
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
})
]
}
};
!isProd && config.plugins.push(new FriendlyErrorsPlugin())
module.exports = config
module.exports = config

49
build/webpack.client.config.js Normal file → Executable file
View File

@ -1,24 +1,43 @@
const path = require('path')
const webpack = require('webpack')
const merge = require('webpack-merge')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const baseConfig = require('./webpack.base.config')
const devConfig = require('./webpack.client.dev.config')
const prodConfig = require('./webpack.client.prod.config')
const vueConfig = require('./vue-loader.config')
const projectRoot = path.resolve(__dirname, '../')
const utils = require('./utils')
var config = merge(baseConfig, {
externals: {
},
externals: {},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueConfig
}]
rules: [{
test: /\.vue$/,
loader: 'vue-loader',
options: vueConfig,
include: [utils.resolve('client/manage')],
exclude: /node_modules\/(?!(autotrack|dom-utils))|vendor\.dll\.js/
}, {
test: /\.scss$/,
loader: 'style-loader!css-loader!postcss-loader!sass-loader'
}, {
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [utils.resolve('client/manage/icons')],
options: {
symbolId: 'icon-[name]'
}
}, {
test: /\.css$/,
loader: 'style-loader!css-loader!postcss-loader'
}, {
test: /\.less$/,
loader: 'style-loader!css-loader!postcss-loader!less-loader'
}, {
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/,
loader: 'url-loader',
exclude: [utils.resolve('client/manage/icons')],
query: {
limit: 10000,
name: '/static/img/[name].[hash:7].[ext]'
}
}]
},
plugins: [
@ -30,4 +49,4 @@ if (process.env.NODE_ENV === 'production') {
} else {
config = merge(config, devConfig)
}
module.exports = config
module.exports = config

30
build/webpack.client.dev.config.js Normal file → Executable file
View File

@ -5,31 +5,7 @@ const utils = require('./utils')
module.exports = {
devtool: '#source-map',
module: {
rules: [{
test: /\.scss$/,
loader: 'style-loader!css-loader!postcss-loader!sass-loader'
}, {
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [utils.resolve('src/manage/icons')],
options: {
symbolId: 'icon-[name]'
}
}, {
test: /\.css$/,
loader: 'style-loader!css-loader!postcss-loader'
}, {
test: /\.less$/,
loader: 'style-loader!css-loader!postcss-loader!less-loader'
}, {
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/,
loader: 'url-loader',
exclude: [utils.resolve('src/manage/icons')],
query: {
limit: 10000,
name: 'static/img/[name].[hash:7].[ext]'
}
}]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
@ -41,8 +17,8 @@ module.exports = {
'admin'
],
filename: 'admin.html',
template: 'src/template/admin.html',
template: 'client/template/admin.html',
inject: true,
})
]
}
}

99
build/webpack.client.prod.config.js Normal file → Executable file
View File

@ -3,66 +3,69 @@ const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const SWPrecachePlugin = require('sw-precache-webpack-plugin')
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const srcDir = path.resolve(__dirname, '../dist/').replace(/\\/g, "\/")
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({
size: os.cpus().length
});
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
const srcDir = path.resolve(__dirname, '../public/admin/').replace(/\\/g, "\/")
const prefixMulti = {}
prefixMulti[srcDir] = '';
const utils = require('./utils')
module.exports = {
devtool: false,
module: {
noParse: /node_modules\/(element-ui\.js)/,
rules: [{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [utils.resolve('src/manage/icons')],
options: {
symbolId: 'icon-[name]'
}
}, {
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/,
loader: 'url-loader',
exclude: [utils.resolve('src/manage/icons')],
query: {
limit: 10000,
name: 'static/img/[name].[hash:7].[ext]'
}
}, {
test: /\.css$/,
loader: ExtractTextPlugin.extract(['css-loader', 'postcss-loader'])
}, {
test: /\.scss/,
loader: ExtractTextPlugin.extract(['css-loader', 'postcss-loader', 'sass-loader'])
}, {
test: /\.less/,
loader: ExtractTextPlugin.extract(['css-loader', 'postcss-loader', 'less-loader'])
}]
test: /\.js$/,
//把对.js 的文件处理交给id为happyBabel 的HappyPack 的实例执行
loader: 'happypack/loader?id=happyBabel',
//排除node_modules 目录下的文件
exclude: /node_modules/,
include: path.resolve(__dirname, 'src')
}, ]
},
plugins: [
new HappyPack({
//用id来标识 happypack处理那里类文件
id: 'happyBabel',
//如何处理 用法和loader 的配置一样
loaders: [{
loader: 'babel-loader?cacheDirectory=true',
}],
//共享进程池
threadPool: happyThreadPool,
//允许 HappyPack 输出日志
verbose: true,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new ExtractTextPlugin('static/css/[name].[hash:7].css'),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
return (module.resource && /\.js$/.test(module.resource) && module.resource.indexOf('node_modules') > 0)
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'element',
minChunks: (m) => /node_modules\/(?:element-ui)/.test(m.context)
}),
new webpack.optimize.UglifyJsPlugin({
compressor: {
warnings: false
},
output: {
comments: false
new ParallelUglifyPlugin({
test: /\.(js)($|\?)/i,
cacheDir: '.cache/',
workerCount: 4,
uglifyES: {
output: {
comments: false,
beautify: false
},
compress: {
warnings: false,
drop_console: true,
collapse_vars: true,
reduce_vars: true
}
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: "manifest",
minChunks: Infinity
@ -79,9 +82,13 @@ module.exports = {
'manifest', 'vendor', 'element', 'admin',
],
filename: 'admin.html',
template: 'src/template/admin.html',
template: 'client/template/admin.html',
manageCates: '{{manageCates}}',
inject: true
})
}),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require("../public/vendor/vendor-manifest.json")
}),
]
}
}

View File

@ -530,7 +530,16 @@ header {
padding-top: 20px;
.post-title {
position: relative;
margin: 0 0 15px;
.contentState {
position: absolute;
top: 0px;
right: 0px;
font-size: 15px;
font-weight: 700;
}
}
.post-content {
@ -650,6 +659,14 @@ header {
.main-form {
padding: 15px;
.select-group {
select {
display: inline-block;
width: 30%;
margin-right: 10px;
}
}
}
.right-form {
@ -667,16 +684,34 @@ header {
}
.thumbImg {
.uploader-list {
.thumbnail {
height: 15rem;
.uploader-demo {
position: relative;
img {
width: 100%;
height: 100%;
.uploader-list {
.thumbnail {
height: 15rem;
img {
width: 100%;
height: 100%;
}
}
}
.trashIco {
display: none;
position: absolute;
z-index: 99;
right: 0;
top: 0;
padding: 8px;
font-size: 22px;
color: #fff;
background: rgba(0, 0, 0, .55);
cursor: pointer;
}
}
}
}
}
@ -762,6 +797,21 @@ header {
h2 {
font-weight: 500;
font-size: 20px;
.sticky-post {
display: inline-block;
padding: 0 5px;
margin-top: 4px;
font-size: 12px;
font-weight: 400;
line-height: 20px;
color: #fff;
background: #3ca5f6;
border-radius: 3px;
vertical-align: top;
background-image: -webkit-linear-gradient(0deg, #3ca5f6 0%, #a86af9 100%);
background-image: linear-gradient(90deg, #3ca5f6 0%, #a86af9 100%);
}
}
.post-content {
@ -1656,86 +1706,6 @@ header {
}
}
// 管理员登录
.adminlogin {
height: 100%;
background-color: #2d3a4b;
background-size: cover;
background-repeat: no-repeat;
body {
background: none;
}
.login-container {
background-clip: padding-box;
background: transparent;
.admin-logo-title h3 {
color: #99a9bf;
font-size: 35px;
text-align: center;
font-weight: normal;
margin: 40px 0;
}
.form-group {
position: relative;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.1);
border-radius: 5px;
color: #454545;
margin-bottom: 22px;
.svg-container {
padding: 6px 5px 6px 15px;
color: #889aa4;
vertical-align: middle;
width: 30px;
display: inline-block;
i {
font-size: 16px;
}
}
.form-control {
background: transparent;
width: 85%;
display: inline-block;
height: 47px;
-webkit-box-shadow: 0 0 0px 1000px #283443 inset !important;
-webkit-text-fill-color: #fff !important;
background: transparent;
border: 0px;
-webkit-appearance: none;
border-radius: 0px;
padding: 12px 5px 12px 15px;
color: #eee;
height: 47px;
caret-color: #fff;
}
.imageCode-input {
width: 150px;
}
.imageCode-img {
width: 115px;
height: 100%;
position: absolute;
right: 0;
top: 0;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
}
.btn-primary {
width: 100%;
}
}
}
// 底部加载更多按钮
.loading-more {
@ -1767,7 +1737,7 @@ header {
img {
width: 100%;
height: 100%;
height: auto;
}
}

View File

@ -88,9 +88,9 @@ function getAjaxData(url, success = () => {}, type = 'get', params = {}, error =
};
if (type == 'post') {
baseParams = Object.assign({}, baseParams, {
contentType: 'application/json; charset=utf-8',
traditional: true,
data: JSON.stringify(params),
// contentType: 'application/json; charset=utf-8',
// traditional: true,
data: params,
})
} else {
if (url.indexOf('?') >= 0) {
@ -132,8 +132,8 @@ function loginOut() {
})
}
function watchMaster(id, targetId) {
getAjaxData('/api/v0/user/followMaster?masterId=' + id, (data) => {
function watchCreator(id, targetId) {
getAjaxData('/api/v0/user/followCreator?creatorId=' + id, (data) => {
if (data.status == 200) {
$('#' + targetId).find('i').remove();
$('#' + targetId).text('已关注');
@ -244,65 +244,7 @@ avalon.filters.renderJob = function (data, type) {
return newStr;
}
/**
*
* recMasters avalon controller
*
*/
var recMastersVm = avalon.define({
$id: 'recMasters',
masterList: [],
currentPage: '1',
changeList: () => {
recMastersVm.getTargetMasterList();
},
askApp: () => {
askApp();
},
watchMaster(id) {
getAjaxData('/api/v0/user/followMaster?masterId=' + id, (data) => {
if (data.status === 200) {
layer.msg(data.message, {
icon: 1,
time: msgTime,
anim: 1
});
recMastersVm.masterList = recMastersVm.masterList.map(item => {
if (item._id === id) {
item.had_followed = true
}
return item
})
}
})
},
getTargetMasterList: () => {
getAjaxData('/api/v0/user/getRandomMasters', (data) => {
if (data.status == 200) {
recMastersVm.masterList = data.data;
recMastersVm.currentPage++;
}
})
},
followAllMasters() {
if (recMastersVm.masterList.length) {
let targetIds = recMastersVm.masterList.filter(item => !item.had_followed).map(item => item._id).join(',');
getAjaxData('/api/v0/user/followMaster?masterId=' + targetIds, (data) => {
if (data.status === 200) {
layer.msg(data.message, {
icon: 1,
anim: 1,
time: msgTime
});
recMastersVm.masterList = recMastersVm.masterList.map(item => {
item.had_followed = true
return item
})
}
})
}
}
})
var searchVm = avalon.define({
$id: 'headerCtr',
lsk: [],
@ -367,7 +309,7 @@ var personInfoVm = avalon.define({
total_despiseNum: 0,
total_likeNum: 0,
total_reward_num: 0,
approveMasterIndentify: () => {
approveCreatorIndentify: () => {
layer.alert(getSysValueByKey('label_system_waitfor_update'));
},
industryArr: [],
@ -475,8 +417,6 @@ function askContentThu(askContentThumbsUp) {
var rcStaticlVm = avalon.define({
$id: 'rcStatic',
askLike: (e, type, targetId) => {
let oldLinkNum = $('#thumbs_' + targetId).text();
let oldRewordNum = $('#reword_' + targetId).text();
let oldDespisesNum = $('#despises_' + targetId).text();
@ -533,7 +473,7 @@ function getAppendList(type = '1') { // 1增加2原地刷新
paramsStr = '/api/v0/content/getList?current=' + appendNewsVm.current + '&pageSize=' + appendNewsVm.pageSize + '&searchkey=' + $('#searchkey').val()
} else if (appendType == 'tag') {
paramsStr = '/api/v0/content/getList?current=' + appendNewsVm.current + '&pageSize=' + appendNewsVm.pageSize + '&tagName=' + $('#tagName').val()
} else if (appendType == 'masterContents') {
} else if (appendType == 'creatorContents') {
paramsStr = '/api/v0/content/getList?type=1&current=' + appendNewsVm.current + '&pageSize=' + appendNewsVm.pageSize
}
getAjaxData(paramsStr, (data) => {
@ -602,58 +542,6 @@ var appendNewsVm = avalon.define({
}
}
})
/**
*
* adminlogin avalon controller
*
*/
var adminLoginVm = avalon.define({
$id: 'adminUserlogin',
password: '',
userName: '',
message: '',
imageCode: "",
showErr: false,
imgCodeUrl: "/api/v0/getImgCode",
reSetImgCode: function () {
adminLoginVm.imgCodeUrl = "/api/v0/getImgCode?" + Math.random();
},
validate: {
onError: function (reasons) {
reasons.forEach(function (reason) {
console.log(reason.getMessage())
})
},
onValidateAll: function (reasons) {
if (reasons.length) {
console.log('有表单没有通过', reasons)
adminLoginVm.showErr = true;
adminLoginVm.message = reasons[0].message;
layer.msg(reasons[0].message, {
icon: 2,
shade: [0.001, '#000'],
time: msgTime
});
} else {
console.log('全部通过');
var params = {
userName: adminLoginVm.userName,
password: CryptoJS.MD5('dora' + adminLoginVm.password).toString(),
imageCode: adminLoginVm.imageCode
}
getAjaxData('/api/v0/admin/doLogin', (data) => {
if (data.status == 200) {
window.location.href = "/manage";
} else {
adminLoginVm.showErr = true;
adminLoginVm.message = data.message;
adminLoginVm.reSetImgCode();
}
}, 'post', params)
}
}
}
})
/**
*
@ -901,7 +789,7 @@ var userEmailLoginVm = avalon.define({
*/
function getPostMessages() {
getAjaxData('/api/v0/message/getList?pageSize=100&contentId=' + $('#contentId').val(), (data) => {
getAjaxData('/api/v0/user/getMessages?pageSize=100&contentId=' + $('#contentId').val(), (data) => {
if (data.status == 200) {
postMsgVm.messageList = data.data.docs;
}
@ -920,29 +808,16 @@ var postMsgVm = avalon.define({
adminReplyAuthor: "",
praise_num: 0,
despises_num: 0,
askLike: (type, targetId) => {
askApp();
// if (type == '1') {
// getAjaxData('/api/v0/user/askContentThumbsUp?contentId=' + targetId, (data) => {
// if (data.status == 200) {
// getPostMessages();
// }
// })
// } else if (type == '2') { // 踩
// getAjaxData("/api/v0/user/despiseContent?contentId=" + targetId, (data) => {
// if (data.status == 200) {
// getPostMessages();
// }
// });
// }
},
getAuthor: function (item, adminAuthor, author) {
var obj = item[adminAuthor]
if (obj) {
return obj
if (item) {
var obj = item[adminAuthor]
if (obj) {
return obj
}
return item[author] ? item[author] : '';
} else {
return ''
}
return item[author] ? item[author] : '';
},
reSetData: function () {
postMsgVm.content = "";
@ -953,15 +828,17 @@ var postMsgVm = avalon.define({
postMsgVm.message = "";
},
reply: function (id, user, utype) {
postMsgVm.reSetData();
postMsgVm.replyState = true;
postMsgVm.relationMsgId = id;
if (utype == '0') {
postMsgVm.replyAuthor = user._id;
} else {
postMsgVm.adminReplyAuthor = user._id;
if (id && user) {
postMsgVm.reSetData();
postMsgVm.replyState = true;
postMsgVm.relationMsgId = id;
if (utype == '0') {
postMsgVm.replyAuthor = user._id;
} else {
postMsgVm.adminReplyAuthor = user._id;
}
$("#msgSendBox").appendTo($("#msglist_" + id))
}
$("#msgSendBox").appendTo($("#msglist_" + id))
},
cancelReply: function () {
postMsgVm.reSetData();
@ -981,9 +858,12 @@ var postMsgVm = avalon.define({
} else {
console.log('全部通过');
if ($('#logined').val() == 'true') {
var params = postMsgVm.$model;
params.contentId = $('#contentId').val();
delete params.messageList;
var params = {
contentId: $('#contentId').val(),
replyAuthor: postMsgVm.replyAuthor,
relationMsgId: postMsgVm.relationMsgId,
content: postMsgVm.content,
}
getAjaxData('/api/v0/user/postMessages', (data) => {
if (data.status == 200) {
$("#postMessage").prepend($('#msgSendBox'));
@ -1038,7 +918,8 @@ var myContentsVm = avalon.define({
joinTopicsTotalPage: 1,
contentPageClick: function (e, cur) {
getUserRelevantList('/user/getUserContents', 'myContents', cur, {
userId: $('#userId').val()
userId: $('#userId').val(),
listState: 'all'
})
},
messagePageClick: function (e, cur) {
@ -1047,7 +928,7 @@ var myContentsVm = avalon.define({
})
},
joinTopicPageClick: function (e, cur) {
getUserRelevantList('/user/getUserReplies', 'myJoinTopics', cur, {
getUserRelevantList('/user/getMessages', 'myJoinTopics', cur, {
userId: $('#userId').val()
})
},
@ -1232,6 +1113,47 @@ var regVm = avalon.define({
}
})
var getBackPsdVm = avalon.define({
$id: 'getBackPsd',
password: '',
confirmPassword: '',
message: '',
showErr: false,
validate: {
onError: function (reasons) {
reasons.forEach(function (reason) {
console.log(reason.getMessage())
})
},
onValidateAll: function (reasons) {
if (reasons.length) {
console.log('有表单没有通过', reasons)
getBackPsdVm.showErr = true;
getBackPsdVm.message = reasons[0].message;
} else {
console.log('全部通过');
var params = {
tokenId: $('#tokenId').val(),
confirmPassword: getBackPsdVm.confirmPassword,
password: getBackPsdVm.password
}
getAjaxData('/api/v0/user/updateNewPsd', (data) => {
if (data.status == 200) {
layer.msg(data.message, {
icon: 1
}, function () {
window.location.href = "/users/login";
});
}
}, 'post', params);
}
}
}
})
/**
*
* reSetPassword avalon controller

0
src/manage/App.vue → client/manage/App.vue Normal file → Executable file
View File

25
src/manage/admin.js → client/manage/admin.js Normal file → Executable file
View File

@ -10,6 +10,10 @@ import 'element-ui/lib/theme-chalk/index.css'
import '@/styles/index.scss' // global css
// 代码编辑器CodeMirror样式
import "codemirror/lib/codemirror.css";
import "codemirror/theme/cobalt.css";
import App from './App'
import router from './router'
import store from './store'
@ -19,10 +23,28 @@ import './icons' // icon
import './permission' // permission control
import * as filters from './filters' // global filters
import {
showFullScreenLoading,
tryHideFullScreenLoading,
} from './utils/axiosLoading'
// axios拦截请求
Axios.interceptors.request.use((config) => {
let configParams = config.method == 'get' ? config.params : config.data;
if (configParams.showLoading) {
showFullScreenLoading()
}
return config;
}, (error) => {
return Promise.reject(error)
})
// axios拦截返回拦截token过期
Axios.interceptors.response.use(function (response) {
let configParams = response.config.method == 'get' ? response.config.params : JSON.parse(response.config.data);
if (configParams.showLoading) {
tryHideFullScreenLoading();
}
let res = response.data;
if (res.state === 'error') {
if (res.err && res.err.indexOf('token') !== -1) {
@ -35,6 +57,7 @@ Axios.interceptors.response.use(function (response) {
}
return response;
}, function (error) {
tryHideFullScreenLoading();
return Promise.reject(error);
});
@ -56,4 +79,4 @@ new Vue({
store,
i18n,
render: h => h(App)
})
})

View File

0
src/manage/api/login.js → client/manage/api/login.js Normal file → Executable file
View File

View File

View File

View File

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

View File

View File

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 552 B

After

Width:  |  Height:  |  Size: 552 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 683 B

After

Width:  |  Height:  |  Size: 683 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 752 B

After

Width:  |  Height:  |  Size: 752 B

View File

Before

Width:  |  Height:  |  Size: 983 B

After

Width:  |  Height:  |  Size: 983 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 684 B

After

Width:  |  Height:  |  Size: 684 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 866 B

After

Width:  |  Height:  |  Size: 866 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 664 B

After

Width:  |  Height:  |  Size: 664 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 637 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show More