├── .babelrc ├── .gitignore ├── README.md ├── app.js ├── app ├── controllers │ ├── h5.server.controller.js │ └── h5.server.rpc.js ├── models │ └── h5.server.model.js └── routes │ └── h5.server.routes.js ├── bin └── www ├── config ├── config.js ├── express.js └── mongoose.js ├── images ├── createNewWork.png ├── design.gif ├── design.png ├── design2.gif ├── login.gif ├── login.png ├── music.png ├── newWork.gif ├── output.gif └── work.png ├── package.json ├── views ├── app.js ├── components │ ├── Alert.vue │ ├── DesignWorkPage.vue │ ├── LoginImage.vue │ ├── LoginInput.vue │ ├── Navigation.vue │ ├── createWorkDialog.vue │ └── shadow.vue ├── images │ ├── H5.png │ ├── add.png │ ├── mobilePhone.png │ └── music.png ├── index.html ├── layouts │ ├── Home.vue │ ├── Login.vue │ ├── MobilePhone.vue │ ├── PageList.vue │ ├── Setup.vue │ ├── Work.vue │ ├── imageDialog.vue │ ├── modifyDesign.vue │ └── musicDialog.vue ├── pages │ ├── 404.vue │ ├── Design.vue │ ├── Home.vue │ ├── Main.vue │ └── Register.vue ├── routes.js ├── show.ejs ├── static │ ├── animation.css │ ├── jquery-3.0.0.js │ ├── show.css │ ├── show.js │ └── toucher.js └── store │ ├── actions.js │ ├── modules │ ├── Design.js │ ├── User.js │ └── Work.js │ ├── mutations.js │ ├── state.js │ └── store.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-1", 5 | "stage-2", 6 | "stage-3", 7 | ], 8 | "plugins": ["transform-async-to-generator"] 9 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | views/static/ 3 | .DS_Store 4 | npm-debug.log 5 | npm-debug.log.* 6 | yarn-debug.log* 7 | yarn-error.log*· -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # H5 2 | 3 | 4 | 5 | ## 简介 6 | 7 | H5是一款可视化编辑手机H5页面的单页应用WebApp,可以根据用户的设计自动产生精美的H5界面。 8 | 9 | 类似市面上易企秀、简客这样的app。 10 | 11 | 这是我的个人项目,旨在学习Vue.js以及前后端开发流程。 12 | 13 | 14 | 15 | ## 运行 16 | 17 | ```bash 18 | cd H5 19 | 20 | # install dependencies 21 | npm install 22 | 23 | # app running 24 | npm start 25 | ``` 26 | 27 | 28 | open localhost:2000 29 | 30 | 31 | ## 技术 32 | 33 | 前端:Vue.js + Vuex 34 | 35 | 后端:Node.js 36 | 37 | 构建:webpack 38 | 39 | 数据库: MongoDB 40 | 41 | UI: Muse-UI (https://museui.github.io/#/index) 42 | 43 | 44 | ## 目录结构 45 |
 46 | ├─  package.json        # 项目配置
 47 | ├─  README.md           # 项目说明
 48 | ├─  app.js              # 项目入口文件
 49 | ├─  images              # 静态图片
 50 | ├─  userData            # 用户数据
 51 | ├─  node_modules        # npm依赖包
 52 | ├─  webpack.config.js   # webpack配置文件
 53 | ├─  .babelrc            # babel配置
 54 | │
 55 | │
 56 | ├─  app                 # node后端业务
 57 |       │
 58 |       │ controllers     #  控制器
 59 |       │ models          #  数据模型
 60 |       │ routes          #  路由分发
 61 | │
 62 | │
 63 | ├─  bin                 # node启动
 64 | │
 65 | │
 66 | └─  views               # 前端代码
 67 |       │ app.js          # 前端页面入口文件
 68 |       │ components      # 基础组件
 69 |       │ dist            # 编译生成文件
 70 |       │ index.html      # 首页
 71 |       │ layouts         # 布局
 72 |       │ music           # 用户静态音乐
 73 |       │ pages           # 根据路由切换页面
 74 |       │ routes.js       # 路由表
 75 |       │ show.ejs        # ejs模板,用以产生H5页面
 76 |       │ static          # 静态html以及css文件
 77 |       │ store           # vuex store
 78 | │
 79 | │
 80 | └─  config              # node配置
 81 | 
82 | 83 | 84 | ## 效果图 85 | 86 | ![img](./images/login.gif) 87 | 88 | ![img](./images/newWork.gif) 89 | 90 | ![img](./images/design.gif) 91 | 92 | ![img](./images/design2.gif) 93 | 94 | ![img](./images/output.gif) 95 | 96 | ![img](./images/login.png) 97 | 98 | ![img](./images/work.png) 99 | 100 | ![img](./images/createNewWork.png) 101 | 102 | ![img](./images/design.png) 103 | 104 | ![img](./images/music.png) 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let express = require('./config/express'); 4 | let mongodb = require('./config/mongoose'); 5 | 6 | let db = mongodb(); 7 | let app = express(); 8 | 9 | module.exports = app; -------------------------------------------------------------------------------- /app/controllers/h5.server.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let mongoose = require('mongoose'); 4 | let rpc = require('./h5.server.rpc'); 5 | let formidable = require('formidable'); 6 | let fs = require('fs'); 7 | let cfg = require('../../config/config'); 8 | let qr = require('qr-image') 9 | 10 | /*mongoose*/ 11 | let Users = mongoose.model('Users'); 12 | let UserMusics = mongoose.model('UserMusics'); 13 | let UserImages = mongoose.model('UserImages'); 14 | let UserDesigns = mongoose.model('UserDesigns'); 15 | 16 | /*产生4位随机数带上时间*/ 17 | function RandomKey(){ 18 | function getNum(){ 19 | return Math.ceil(Math.random() * 10) - 1; /*产生0-9的随机数*/ 20 | } 21 | 22 | let Key = ""; 23 | for(let i=0; i<4; i++){ 24 | Key += getNum(); 25 | } 26 | 27 | return (Key + Date.parse(new Date())); 28 | } 29 | 30 | module.exports = { 31 | 32 | /** 33 | * 用户登陆 34 | * 35 | * 用户登陆接口,返回是否登陆成功。 36 | * 37 | * @param req 38 | * @param res 39 | * @param next 40 | * @returns void 41 | * 42 | * @date 2016-12-27 43 | * @author Cao Yang 44 | */ 45 | login(req,res,next){ 46 | let params = req.body.params || {}; 47 | 48 | if (!params.userName) { 49 | res.json({result: false, content: '用户名为空'}); 50 | return; 51 | } 52 | 53 | if (!params.passWord) { 54 | res.json({result: false, content: '密码为空'}); 55 | return; 56 | } 57 | 58 | Users.find({userName:params.userName}, null,{},function(err,result){ 59 | if (err) { 60 | console.log('login find err!'); 61 | res.json({result: false, content: '登陆失败'}); 62 | return next(err); 63 | } 64 | else{ 65 | if (result.length && (result[0].passWord === params.passWord)) { 66 | req.session.user = {'userName': params.userName}; 67 | console.log('login req.session.user',req.session.user) 68 | res.json({ 69 | result: true, 70 | params: { 71 | userInfo: result[0], 72 | } 73 | }); 74 | } 75 | else if(result.length === 0){ 76 | res.json({result: false, content: '用户不存在'}); 77 | } 78 | else{ 79 | res.json({result: false, content: '用户名或密码错误,请重试'}); 80 | } 81 | } 82 | }) 83 | 84 | }, 85 | 86 | /** 87 | * 用户注册 88 | * 89 | * 用户注册接口,注册新用户。 90 | * 91 | * @param req 92 | * @param res 93 | * @param next 94 | * @returns void 95 | * 96 | * @date 2016-12-27 97 | * @author Cao Yang 98 | */ 99 | register(req,res,next){ 100 | if (req.body && req.body.params) { 101 | let params = req.body.params || {}; 102 | 103 | if (!params.userName) { 104 | res.json({result: false, content: '用户名为空'}); 105 | return; 106 | } 107 | 108 | if (!params.passWord) { 109 | res.json({result: false, content: '密码为空'}); 110 | return; 111 | } 112 | 113 | if (!params.eMail) { 114 | res.json({result: false, content: '邮箱为空'}); 115 | return; 116 | } 117 | 118 | let user = new Users({ 119 | userName: params.userName, 120 | passWord: params.passWord, 121 | eMail: params.eMail, 122 | nikeName: params.userName, 123 | userImage: "/image/defaultHeadPortrait.png", 124 | Gender: 'Man', 125 | age: '18', 126 | personalizedSignature: '', 127 | place: '', 128 | }); 129 | 130 | user.save(function(err){ 131 | if (err) { 132 | console.log('Register err!' + err); 133 | res.json({result: false, content: '注册失败'}); 134 | return next(err); 135 | } 136 | else{ 137 | console.log('Register ' + params.userName + ' successed.'); 138 | res.json({result: true}); 139 | } 140 | }) 141 | 142 | } 143 | else{ 144 | res.json({result: false, content: '数据格式有误'}); 145 | } 146 | }, 147 | 148 | /** 149 | * RPC接口 150 | * 151 | * 对RPC接口做同一操作。 152 | * 153 | * @param req 154 | * @param res 155 | * @param next 156 | * @returns void 157 | * 158 | * @date 2017-1-1 159 | * @author Cao Yang 160 | */ 161 | rpcMethon(req,res,next){ 162 | let params = req.body.params || {}; 163 | let method = req.body.method; 164 | if ((typeof rpc[method]) === 'function') { 165 | rpc[method](req,res,next); 166 | } 167 | else{ 168 | res.json({result: false, content: '未找到RPC接口'}); 169 | } 170 | }, 171 | 172 | /** 173 | * 上传音乐 174 | * 175 | * 上传本地音乐文件接口 176 | * 177 | * @param req 178 | * @param res 179 | * @param next 180 | * @returns void 181 | * 182 | * @date 2017-1-12 183 | * @author Cao Yang 184 | */ 185 | UploadMusic(req, res, next){ 186 | let form = new formidable.IncomingForm(); 187 | form.uploadDir = __dirname+'/../../userData/musics'; 188 | form.keepExtensions = true; 189 | form.maxFieldsSize = 2*1024*1024;/*限制图片大小最大为2M*/ 190 | 191 | form.parse(req,(err,fields,files) => { 192 | if (files.music.type.indexOf('audio') >= 0) { 193 | let path = files.music.path; 194 | let newPath = path.slice(0, path.lastIndexOf('/'))+'/'+req.session.user.userName+'-'+RandomKey()+ path.slice(path.lastIndexOf('.'),path.length); 195 | fs.rename(path, newPath); 196 | 197 | /*save in mongoDB*/ 198 | let music = new UserMusics({ 199 | userName: req.session.user.userName, 200 | path: newPath, 201 | musicName: files.music.name, 202 | type: files.music.type, 203 | size: files.music.size, 204 | }); 205 | 206 | music.save(err => { 207 | if (err) { 208 | res.json({result: false, content: '上传音乐失败'}); 209 | return next(err); 210 | } 211 | else{ 212 | res.json({result: true}); 213 | } 214 | }) 215 | } 216 | else{ 217 | res.json({result: false, content: '音频文件格式有误'}); 218 | } 219 | }) 220 | }, 221 | 222 | /** 223 | * 播放音乐 224 | * 225 | * 播放音乐接口 226 | * 227 | * @param req 228 | * @param res 229 | * @param next 230 | * @returns void 231 | * 232 | * @date 2017-1-16 233 | * @author Cao Yang 234 | */ 235 | PlayMusic(req, res, next){ 236 | UserMusics.findById(req.query.id, (err, result) => { 237 | if (result) { 238 | res.writeHead(200, {'Content-Type': 'video/mp4'}); 239 | let rs = fs.createReadStream(result.path); 240 | 241 | rs.pipe(res); 242 | rs.on('end',function(){ 243 | res.end(); 244 | }); 245 | } 246 | }); 247 | }, 248 | 249 | /** 250 | * 上传图片 251 | * 252 | * 上传本地图片文件接口 253 | * 254 | * @param req 255 | * @param res 256 | * @param next 257 | * @returns void 258 | * 259 | * @date 2017-1-27 260 | * @author Cao Yang 261 | */ 262 | UploadImage(req, res, next){ 263 | let form = new formidable.IncomingForm(); 264 | form.uploadDir = __dirname+'/../../userData/images'; 265 | form.encoding = 'utf-8'; 266 | form.keepExtensions = true; 267 | form.maxFieldsSize = 2*1024*1024;/*限制图片大小最大为2M*/ 268 | 269 | form.parse(req,(err,fields,files) => { 270 | let fileType; 271 | switch(files.image.type){ 272 | case 'image/jpeg': 273 | fileType = 'jpeg'; 274 | break; 275 | case 'image/png': 276 | fileType = 'png'; 277 | break; 278 | case 'image/jpg': 279 | fileType = 'jpg'; 280 | break; 281 | } 282 | 283 | if (fileType === undefined) {/*上传的图片格式没有按照指定要求*/ 284 | res.send('uploadIcon img type err'); 285 | return; 286 | }; 287 | 288 | let path = files.image.path; 289 | let newPath = path.slice(0, path.lastIndexOf('/'))+'/'+req.session.user.userName+'-'+RandomKey()+ path.slice(path.lastIndexOf('.'),path.length); 290 | fs.rename(path, newPath); 291 | 292 | /*save in mongoDB*/ 293 | let image = new UserImages({ 294 | userName: req.session.user.userName, 295 | path: newPath, 296 | type: files.image.type, 297 | size: files.image.size, 298 | }); 299 | 300 | image.save(err => { 301 | if (err) { 302 | res.json({result: false, content: '上传图片失败'}); 303 | return next(err); 304 | } 305 | else{ 306 | res.json({result: true}); 307 | } 308 | }) 309 | }) 310 | 311 | }, 312 | 313 | /** 314 | * 显示图片 315 | * 316 | * 显示图片接口 317 | * 318 | * @param req 319 | * @param res 320 | * @param next 321 | * @returns void 322 | * 323 | * @date 2017-1-29 324 | * @author Cao Yang 325 | */ 326 | showImage(req, res, next){ 327 | UserImages.findById(req.query.id, (err, result) => { 328 | if (result) { 329 | 330 | res.writeHead(200, {'Content-Type': result.type}); 331 | let rs = fs.createReadStream(result.path); 332 | 333 | rs.pipe(res); 334 | rs.on('end',function(){ 335 | res.end(); 336 | }); 337 | } 338 | else{ 339 | next(); 340 | } 341 | }); 342 | }, 343 | 344 | /** 345 | * 显示生成页面 346 | * 347 | * 显示生成页面接口 348 | * 349 | * @param req 350 | * @param res 351 | * @param next 352 | * @returns void 353 | * 354 | * @date 2017-2-3 355 | * @author Cao Yang 356 | */ 357 | showPage(req, res, next){ 358 | UserDesigns.find({userName: req.query.userName, workName: req.query.workName,}, null,{}, (err, result) => { 359 | if (err) { 360 | console.log('showPage err!' + err); 361 | res.json({result: false, content: '获取页面失败'}); 362 | return next(err); 363 | } 364 | else{ 365 | console.log(result) 366 | if (result.length) { 367 | 368 | res.render("show",{cfg,"designInfos":result[0].designInfos}); 369 | } 370 | else{ 371 | res.send('404 no found.'); 372 | } 373 | } 374 | }) 375 | }, 376 | 377 | /** 378 | * 二维码接口 379 | * 380 | * 根据URL生成对应的二维码图片 381 | * 382 | * @param req 383 | * @param res 384 | * @param next 385 | * @returns void 386 | * 387 | * @date 2017-3-19 388 | * @author Cao Yang 389 | */ 390 | QRcode(req, res, next){ 391 | let userName = req.query.userName || ''; 392 | let workName = req.query.workName || ''; 393 | let url = cfg.ip + ':' + cfg.port + '/H5/Show?userName=' + userName + '&workName=' + workName; 394 | try { 395 | var img = qr.image(url,{size :10}); 396 | res.writeHead(200, {'Content-Type': 'image/png'}); 397 | img.pipe(res); 398 | } catch (e) { 399 | res.writeHead(414, {'Content-Type': 'text/html'}); 400 | res.end('

414 Request-URI Too Large

'); 401 | } 402 | } 403 | 404 | } -------------------------------------------------------------------------------- /app/controllers/h5.server.rpc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let mongoose = require('mongoose'); 4 | let fs = require('fs'); 5 | let cfg = require('../../config/config'); 6 | 7 | /*mongoose*/ 8 | let Users = mongoose.model('Users'); 9 | let UserMusics = mongoose.model('UserMusics'); 10 | let UserImages = mongoose.model('UserImages'); 11 | let UserDesigns = mongoose.model('UserDesigns'); 12 | 13 | module.exports = { 14 | /** 15 | * 用户登出 16 | * 17 | * 用户登出接口,释放资源 18 | * 19 | * @param req 20 | * @param res 21 | * @param next 22 | * @returns void 23 | * 24 | * @date 2017-1-14 25 | * @author Cao Yang 26 | */ 27 | logout(req,res,next){ 28 | delete req.session.user; 29 | res.json({result: true}); 30 | }, 31 | 32 | /** 33 | * 获取所有音乐 34 | * 35 | * 获取用户上传的所有音乐的列表 36 | * 37 | * @param req 38 | * @param res 39 | * @param next 40 | * @returns void 41 | * 42 | * @date 2017-1-14 43 | * @author Cao Yang 44 | */ 45 | getAllUserMusics(req, res, next){ 46 | UserMusics.find({userName: req.session.user.userName}, null,{}, (err, result) => { 47 | if (err) { 48 | console.log('getAllUserMusics err!' + err); 49 | res.json({result: false, content: '查询失败'}); 50 | return next(err); 51 | } 52 | else{ 53 | let params = []; 54 | result.forEach((item, index) => { 55 | params.push({ 56 | id: item._id, 57 | musicName: item.musicName, 58 | }) 59 | }) 60 | res.json({result: true, params}); 61 | } 62 | }) 63 | }, 64 | 65 | /** 66 | * 删除用户音乐 67 | * 68 | * 删除用户音乐接口 69 | * 70 | * @param req 71 | * @param res 72 | * @param next 73 | * @returns void 74 | * 75 | * @date 2017-1-14 76 | * @author Cao Yang 77 | */ 78 | delUserMusics(req, res, next){ 79 | let szId = req.body.params.id || []; 80 | (typeof szId === 'string') && (szId = [szId]); 81 | 82 | szId.forEach(id => { 83 | UserMusics.findById(id, (err, result) => { 84 | if (result.userName === req.session.user.userName) {/*校验用户是否是自己*/ 85 | result.remove(err => { 86 | if (err) { 87 | console.log('delUserMusics err!' + err); 88 | res.json({result: false, content: '删除失败'}); 89 | return next(err); 90 | } 91 | else{ 92 | fs.unlinkSync(result.path); 93 | } 94 | }) 95 | } 96 | }); 97 | }) 98 | 99 | res.json({result: true}); 100 | }, 101 | 102 | /** 103 | * 获取所有图片 104 | * 105 | * 获取用户上传的所有图片的列表 106 | * 107 | * @param req 108 | * @param res 109 | * @param next 110 | * @returns void 111 | * 112 | * @date 2017-1-14 113 | * @author Cao Yang 114 | */ 115 | getAllUserImages(req, res, next){ 116 | UserImages.find({userName: req.session.user.userName}, null,{}, (err, result) => { 117 | if (err) { 118 | console.log('getAllUserImages err!' + err); 119 | res.json({result: false, content: '查询失败'}); 120 | return next(err); 121 | } 122 | else{ 123 | let params = []; 124 | result.forEach((item, index) => { 125 | params.push({ 126 | id: item._id, 127 | }) 128 | }) 129 | res.json({result: true, params}); 130 | } 131 | }) 132 | }, 133 | 134 | /** 135 | * 删除用户图片 136 | * 137 | * 删除用户图片接口 138 | * 139 | * @param req 140 | * @param res 141 | * @param next 142 | * @returns void 143 | * 144 | * @date 2017-1-29 145 | * @author Cao Yang 146 | */ 147 | delUserImages(req, res, next){ 148 | let szId = req.body.params.id || []; 149 | (typeof szId === 'string') && (szId = [szId]); 150 | 151 | szId.forEach(id => { 152 | UserImages.findById(id, (err, result) => { 153 | if (result.userName === req.session.user.userName) {/*校验用户是否是自己*/ 154 | result.remove(err => { 155 | if (err) { 156 | console.log('delUserImages err!' + err); 157 | res.json({result: false, content: '删除失败'}); 158 | return next(err); 159 | } 160 | else{ 161 | fs.unlinkSync(result.path); 162 | } 163 | }) 164 | } 165 | }); 166 | }) 167 | 168 | res.json({result: true}); 169 | }, 170 | 171 | 172 | /** 173 | * 保存用户设计界面 174 | * 175 | * 保存用户设计界面接口 176 | * 177 | * @param req 178 | * @param res 179 | * @param next 180 | * @returns void 181 | * 182 | * @date 2017-1-29 183 | * @author Cao Yang 184 | */ 185 | saveDesign(req, res, next){ 186 | UserDesigns.find({ 187 | userName: req.session.user.userName, 188 | workName: req.body.params.name, 189 | }, null,{}, (err, result) => { 190 | if (err) { 191 | console.log('saveDesign err!' + err); 192 | res.json({result: false, content: '保存失败'}); 193 | return next(err); 194 | } 195 | else{ 196 | if (result.length) { 197 | /*修改数据*/ 198 | result[0].designInfos = req.body.params.designInfos; 199 | result[0].save(err => { 200 | if (err) { 201 | console.log('saveDesign modify err!' + err); 202 | res.json({result: false, content: '保存失败'}); 203 | return next(err); 204 | } 205 | else{ 206 | res.json({result: true}); 207 | } 208 | }) 209 | } 210 | else{ 211 | /*第一次保存*/ 212 | let design = new UserDesigns({ 213 | userName: req.session.user.userName, 214 | workName: req.body.params.name, 215 | designInfos: req.body.params.designInfos, 216 | }); 217 | 218 | design.save(function(err){ 219 | if (err) { 220 | console.log('saveDesign err!' + err); 221 | res.json({result: false, content: '保存失败'}); 222 | return next(err); 223 | } 224 | else{ 225 | res.json({result: true}); 226 | } 227 | }) 228 | } 229 | } 230 | }) 231 | }, 232 | 233 | /** 234 | * 获取所有用户设计界面 235 | * 236 | * 获取所有用户设计界面接口,用以再次编辑 237 | * 238 | * @param req 239 | * @param res 240 | * @param next 241 | * @returns void 242 | * 243 | * @date 2017-2-5 244 | * @author Cao Yang 245 | */ 246 | getAllUserDesigns(req, res, next){ 247 | UserDesigns.find({userName: req.session.user.userName,}, null,{}, (err, result) => { 248 | if (err) { 249 | console.log('getAllUserDesigns err!' + err); 250 | res.json({result: false, content: '获取失败'}); 251 | return next(err); 252 | } 253 | else{ 254 | res.json({result: true, params: result}); 255 | } 256 | }); 257 | }, 258 | 259 | /** 260 | * 删除用户设计界面 261 | * 262 | * 根据作品名称删除相应的用户设计界面 263 | * 264 | * @param req 265 | * @param res 266 | * @param next 267 | * @returns void 268 | * 269 | * @date 2017-3-3 270 | * @author Cao Yang 271 | */ 272 | delDesign(req, res, next){ 273 | let params = req.body.params; 274 | let delObj = { 275 | userName: req.session.user.userName, 276 | workName: params.workName, 277 | } 278 | UserDesigns.remove(delObj, function(err){ 279 | if (err) { 280 | console.log('err',err); 281 | return next(err); 282 | } 283 | else{ 284 | console.log('delete ' + JSON.stringify(delObj) + 'successed!'); 285 | res.status(200); 286 | res.send({result:true,params:null}); 287 | } 288 | }) 289 | }, 290 | 291 | /** 292 | * 修改用户信息 293 | * 294 | * 修改用户的相关信息 295 | * 296 | * @param req 297 | * @param res 298 | * @param next 299 | * @returns void 300 | * 301 | * @date 2017-5-14 302 | * @author Cao Yang 303 | */ 304 | saveUserInfo (req, res, next) { 305 | let params = req.body.params; 306 | Users.find({userName: req.session.user.userName,}, null,{}, (err, result) => { 307 | if (err) { 308 | console.log('saveUserInfo err!' + err); 309 | res.json({result: false, content: '获取失败'}); 310 | return next(err); 311 | } 312 | else{ 313 | /*只修改部分可修改字段,不直接覆盖*/ 314 | result[0].Gender = (params.Gender === 'Man') ? 'Man' : 'Woman'; 315 | result[0].age = params.age; 316 | result[0].eMail = params.eMail; 317 | result[0].nikeName = params.nikeName; 318 | result[0].personalizedSignature = params.personalizedSignature; 319 | result[0].place = params.place; 320 | 321 | result[0].save(err => { 322 | if (err) { 323 | console.log('saveUserInfo err!' + err); 324 | res.json({result: false, content: '保存失败'}); 325 | return next(err); 326 | } 327 | else{ 328 | res.json({result: true}); 329 | } 330 | }); 331 | } 332 | }); 333 | } 334 | } 335 | 336 | 337 | 338 | 339 | 340 | 341 | -------------------------------------------------------------------------------- /app/models/h5.server.model.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let mongoose = require('mongoose'); 4 | 5 | /*用户表*/ 6 | let SchemaUsers = new mongoose.Schema({ 7 | userName: String, /*用户名*/ 8 | passWord: String, /*密码*/ 9 | eMail: String, /*邮箱*/ 10 | nikeName: String, /*昵称*/ 11 | userImage: String, /*头像*/ 12 | Gender: String, /*性别*/ 13 | age: String, /*年龄*/ 14 | personalizedSignature:String, /*个性签名*/ 15 | place: String /*所在区域*/ 16 | }) 17 | 18 | let Users = mongoose.model('Users', SchemaUsers); 19 | 20 | /*音乐表*/ 21 | let SchemaMusics = new mongoose.Schema({ 22 | userName: String, /*用户名*/ 23 | path: String, /*路径*/ 24 | musicName: String, /*音乐名称*/ 25 | type: String, /*音乐类型*/ 26 | size: Number, /*音乐大小*/ 27 | date: {type:Date,default:Date.now}, /*上传时间*/ 28 | }) 29 | 30 | let Musics = mongoose.model('UserMusics', SchemaMusics); 31 | 32 | /*音乐表*/ 33 | let SchemaImages = new mongoose.Schema({ 34 | userName: String, /*用户名*/ 35 | path: String, /*路径*/ 36 | type: String, /*图片类型*/ 37 | size: Number, /*图片大小*/ 38 | date: {type:Date,default:Date.now}, /*上传时间*/ 39 | }) 40 | 41 | let Images = mongoose.model('UserImages', SchemaImages); 42 | 43 | /*设计表*/ 44 | let SchemaDesign = new mongoose.Schema({ 45 | userName: String, /*用户名*/ 46 | workName: String, /*设计名称*/ 47 | designInfos: Object, /*设计信息*/ 48 | date: {type:Date,default:Date.now}, /*创建时间*/ 49 | }) 50 | 51 | let Designs = mongoose.model('UserDesigns', SchemaDesign); -------------------------------------------------------------------------------- /app/routes/h5.server.routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let H5Controller = require('../controllers/h5.server.controller'); 4 | 5 | module.exports = function(app){ 6 | app.route('/') 7 | .get(function(req,res,next){ 8 | res.sendFile('index.html'); 9 | }); 10 | 11 | app.route('/H5/Login') 12 | .post(H5Controller.login); 13 | 14 | app.route('/H5/Register') 15 | .post(H5Controller.register); 16 | 17 | app.route('/H5/rpc') 18 | .post(H5Controller.rpcMethon); 19 | 20 | app.route('/H5/UploadMusic') 21 | .post(H5Controller.UploadMusic); 22 | 23 | app.route('/H5/UploadImage') 24 | .post(H5Controller.UploadImage); 25 | 26 | app.route('/H5/PlayMusic') 27 | .get(H5Controller.PlayMusic); 28 | 29 | app.route('/H5/image') 30 | .get(H5Controller.showImage); 31 | 32 | app.route('/H5/Show') 33 | .get(H5Controller.showPage); 34 | 35 | app.route('/H5/QRcode') 36 | .get(H5Controller.QRcode); 37 | } -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | let app = require('../app') 5 | let config = require('../config/config'); 6 | 7 | app.listen(config.port, function(){ 8 | console.log('app started,listen on port:',config.port); 9 | }) -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | ip: 'localhost', 5 | port: 3000, 6 | mongodb: 'mongodb://localhost/H5' 7 | } -------------------------------------------------------------------------------- /config/express.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let express = require('express'); 4 | let bodyParser = require('body-parser'); 5 | let config = require('../webpack.config') 6 | let webpack = require('webpack') 7 | let webpackDevMiddleware = require('webpack-dev-middleware') 8 | let webpackHotMiddleware = require('webpack-hot-middleware') 9 | let session = require('express-session'); 10 | let cookieParser = require('cookie-parser'); 11 | 12 | /*产生128位随机数*/ 13 | function RandomSecret(){ 14 | function getNum(){ 15 | return Math.ceil(Math.random() * 10) - 1; /*产生0-9的随机数*/ 16 | } 17 | 18 | let secret = ""; 19 | for(let i=0; i<128; i++){ 20 | secret += getNum(); 21 | } 22 | 23 | return secret; 24 | } 25 | 26 | module.exports = function(){ 27 | console.log('init express..'); 28 | let app = express(); 29 | 30 | /*webpack*/ 31 | let compiler = webpack(config) 32 | app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath })) 33 | app.use(webpackHotMiddleware(compiler)); 34 | 35 | app.set('view engine','ejs'); 36 | app.use(express.static(__dirname+'/../views')); 37 | 38 | app.use(bodyParser.json()); 39 | 40 | app.use(cookieParser()); 41 | 42 | app.use(bodyParser.urlencoded({extended:false})); 43 | 44 | app.use(session({ 45 | resave: false, 46 | rolling: false, 47 | saveUninitialized: true, 48 | secret: RandomSecret(), 49 | name: 'H5Session', 50 | cookie: { maxAge: 60 * 1000 * 60} 51 | })); 52 | 53 | app.use(function(req,res,next){ 54 | console.log(req.url) 55 | if (!req.session.user) { 56 | if (req.url === '/H5/Login' 57 | || req.url === '/H5/Register' 58 | || req.url.indexOf('/H5/PlayMusic') >= 0 59 | || req.url.indexOf('/H5/image') >= 0 60 | || req.url.indexOf('/H5/Show') >= 0) { 61 | next();/*请求为登陆或者注册则不需要校验session*/ 62 | } 63 | else{ 64 | res.json({result: false, content: '用户权限异常'}); 65 | } 66 | } 67 | else if (req.session.user) { 68 | next(); 69 | }; 70 | }) 71 | 72 | require('../app/routes/h5.server.routes')(app); 73 | 74 | app.use(function(req, res, next){ 75 | res.status(404); 76 | try{ 77 | return res.json('No Found!'); 78 | } 79 | catch(e){ 80 | console.error('404 set header after send.'); 81 | } 82 | }) 83 | 84 | app.use(function(err, req, res, next){ 85 | if (!err) { 86 | return next(); 87 | }; 88 | 89 | res.status(500); 90 | try{ 91 | return res.json(err.message || "server err"); 92 | } 93 | catch(e){ 94 | console.error('500 set header after send.') 95 | } 96 | }); 97 | 98 | return app; 99 | }; -------------------------------------------------------------------------------- /config/mongoose.js: -------------------------------------------------------------------------------- 1 | let mongoose = require('mongoose'); 2 | let config = require('./config') 3 | 4 | module.exports = function(){ 5 | mongoose.Promise = require('bluebird'); 6 | 7 | let db = mongoose.connect(config.mongodb); 8 | 9 | require('../app/models/h5.server.model.js'); 10 | 11 | return db; 12 | } -------------------------------------------------------------------------------- /images/createNewWork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/createNewWork.png -------------------------------------------------------------------------------- /images/design.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/design.gif -------------------------------------------------------------------------------- /images/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/design.png -------------------------------------------------------------------------------- /images/design2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/design2.gif -------------------------------------------------------------------------------- /images/login.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/login.gif -------------------------------------------------------------------------------- /images/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/login.png -------------------------------------------------------------------------------- /images/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/music.png -------------------------------------------------------------------------------- /images/newWork.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/newWork.gif -------------------------------------------------------------------------------- /images/output.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/output.gif -------------------------------------------------------------------------------- /images/work.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/images/work.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html5", 3 | "version": "1.0.0", 4 | "description": "an app which automatically generates HTML5 files.", 5 | "main": "www/bin", 6 | "scripts": { 7 | "start": "babel-node bin/www", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "build": "webpack --watch" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/answershuto/H5.git" 14 | }, 15 | "keywords": [ 16 | "Node.js", 17 | "Vue.js", 18 | "h5" 19 | ], 20 | "author": "Cao Yang", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/answershuto/H5/issues" 24 | }, 25 | "homepage": "https://github.com/answershuto/H5#readme", 26 | "dependencies": { 27 | "babel-cli": "^6.18.0", 28 | "babel-core": "^6.21.0", 29 | "babel-loader": "^6.2.10", 30 | "babel-plugin-transform-async-to-generator": "^6.16.0", 31 | "babel-polyfill": "^6.20.0", 32 | "babel-preset-es2015": "^6.18.0", 33 | "bluebird": "^3.4.7", 34 | "body-parser": "^1.15.1", 35 | "cookie-parser": "^1.4.3", 36 | "css-loader": "^0.26.1", 37 | "ejs": "^2.5.5", 38 | "express": "^4.13.4", 39 | "express-session": "^1.14.2", 40 | "fetch": "^1.1.0", 41 | "file": "^0.2.2", 42 | "formidable": "^1.1.1", 43 | "fs": "0.0.1-security", 44 | "js-cookie": "^2.1.3", 45 | "less-loader": "^4.0.3", 46 | "mongoose": "^4.7.5", 47 | "muse-ui": "^2.0.0-beta.2", 48 | "path": "^0.12.7", 49 | "qr-image": "^3.2.0", 50 | "style-loader": "^0.13.1", 51 | "vue": "^2.1.6", 52 | "vue-html-loader": "^1.2.3", 53 | "vue-loader": "^10.0.2", 54 | "vue-style-loader": "^1.0.0", 55 | "vue-template-compiler": "^2.1.6", 56 | "vuex": "^2.1.1", 57 | "webpack": "^1.14.0", 58 | "webpack-dev-middleware": "^1.9.0", 59 | "webpack-hot-middleware": "^2.13.2" 60 | }, 61 | "devDependencies": { 62 | "babel-preset-stage-1": "^6.16.0", 63 | "babel-preset-stage-2": "^6.18.0", 64 | "babel-preset-stage-3": "^6.17.0", 65 | "less": "^2.7.2" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /views/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Vue from 'vue' 4 | import routes from './routes' 5 | import MuseUI from 'muse-ui' 6 | import 'muse-ui/dist/muse-ui.css' 7 | import store from './store/store.js' 8 | 9 | Vue.use(MuseUI) 10 | 11 | 12 | const app = new Vue({ 13 | el: '#app', 14 | store, 15 | data: { 16 | 17 | }, 18 | computed: { 19 | ViewComponent () { 20 | const matchingView = routes[this.$store.state.Main.Route]; 21 | return matchingView 22 | ? require('./pages/' + matchingView + '.vue') 23 | : require('./pages/404.vue') 24 | } 25 | }, 26 | render (h) { 27 | return h(this.ViewComponent) 28 | } 29 | }) -------------------------------------------------------------------------------- /views/components/Alert.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 33 | 34 | -------------------------------------------------------------------------------- /views/components/DesignWorkPage.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 49 | 50 | -------------------------------------------------------------------------------- /views/components/LoginImage.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | -------------------------------------------------------------------------------- /views/components/LoginInput.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 108 | 109 | 165 | 166 | -------------------------------------------------------------------------------- /views/components/Navigation.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 92 | 93 | -------------------------------------------------------------------------------- /views/components/createWorkDialog.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 41 | 42 | -------------------------------------------------------------------------------- /views/components/shadow.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 27 | 28 | -------------------------------------------------------------------------------- /views/images/H5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/views/images/H5.png -------------------------------------------------------------------------------- /views/images/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/views/images/add.png -------------------------------------------------------------------------------- /views/images/mobilePhone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/views/images/mobilePhone.png -------------------------------------------------------------------------------- /views/images/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/answershuto/H5/1089bc283f21d38a53fdbc3e4677d4de0e7687bc/views/images/music.png -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | h5 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 18 | 23 | -------------------------------------------------------------------------------- /views/layouts/Home.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 36 | 37 | -------------------------------------------------------------------------------- /views/layouts/Login.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | -------------------------------------------------------------------------------- /views/layouts/MobilePhone.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 138 | 139 | -------------------------------------------------------------------------------- /views/layouts/PageList.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /views/layouts/Setup.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 73 | 74 | -------------------------------------------------------------------------------- /views/layouts/Work.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 81 | 82 | -------------------------------------------------------------------------------- /views/layouts/imageDialog.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 165 | 166 | -------------------------------------------------------------------------------- /views/layouts/modifyDesign.vue: -------------------------------------------------------------------------------- 1 | 92 | 93 | 469 | 470 | -------------------------------------------------------------------------------- /views/layouts/musicDialog.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 240 | 241 | -------------------------------------------------------------------------------- /views/pages/404.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /views/pages/Design.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 101 | 102 | -------------------------------------------------------------------------------- /views/pages/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | -------------------------------------------------------------------------------- /views/pages/Main.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 39 | 40 | -------------------------------------------------------------------------------- /views/pages/Register.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 113 | 114 | 138 | 139 | -------------------------------------------------------------------------------- /views/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default { 4 | '/': 'Home', 5 | '/Register': 'Register', 6 | '/Main': 'Main', 7 | '/Design': 'Design', 8 | } 9 | -------------------------------------------------------------------------------- /views/show.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | <% if(designInfos.music){%> 10 |
11 | <% } %> 12 |
13 | 14 |
15 | <% for(var i=0;i 16 |
17 | 18 | <% for(var j=0;j 19 |
25 | <%=designInfos.pages[i].text[j].text %> 26 |
27 | <% } %> 28 | 29 | 30 | 31 | <% for(var j=0;j 32 | 38 | 39 | <% } %> 40 | 41 |
42 | <% } %> 43 |
44 | 45 | <% if(designInfos.music){%> 46 | 47 |