├── backstage ├── src │ ├── css │ │ ├── keyframe.css │ │ ├── variables.scss │ │ ├── index.scss │ │ ├── mixin.scss │ │ ├── reset.css │ │ ├── element-variables.scss │ │ └── index.css │ ├── store │ │ ├── edit │ │ │ ├── action │ │ │ │ ├── item.js │ │ │ │ ├── phone.js │ │ │ │ └── page.js │ │ │ ├── index.js │ │ │ ├── mutation-types.js │ │ │ ├── getters.js │ │ │ └── modules │ │ │ │ └── page.js │ │ └── index.js │ ├── config.js │ ├── page │ │ ├── list │ │ │ ├── bus.js │ │ │ ├── list.vue │ │ │ ├── header.vue │ │ │ ├── feed.vue │ │ │ └── create.vue │ │ ├── edit │ │ │ ├── event.vue │ │ │ ├── edit.vue │ │ │ ├── page-dom.vue │ │ │ ├── qrcode.vue │ │ │ ├── tool.vue │ │ │ ├── page.vue │ │ │ ├── ani.vue │ │ │ └── phone.vue │ │ └── login │ │ │ └── login.vue │ ├── img │ │ ├── git.png │ │ ├── logo.png │ │ ├── logo2.png │ │ ├── logo_header.png │ │ ├── phonewhite.svg │ │ ├── show.svg │ │ ├── music_btn.svg │ │ ├── bg.svg │ │ └── notfound.svg │ ├── iconfont │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ └── iconfont.woff │ ├── tpl │ │ ├── types.js │ │ ├── tpl.art │ │ └── index.js │ ├── App.vue │ ├── components │ │ ├── crop │ │ │ └── index.js │ │ ├── image │ │ │ ├── index.js │ │ │ └── main.vue │ │ ├── music │ │ │ ├── index.js │ │ │ └── main.vue │ │ ├── shape │ │ │ ├── index.js │ │ │ └── main.vue │ │ ├── background │ │ │ ├── index.js │ │ │ └── main.vue │ │ └── dialog │ │ │ └── dialog.vue │ ├── api │ │ ├── fetch.js │ │ └── index.js │ ├── keycode │ │ └── index.js │ ├── router │ │ └── index.js │ ├── utils │ │ └── index.js │ ├── main.js │ └── directive │ │ ├── drag.js │ │ └── select.js ├── public │ ├── favicon.ico │ └── index.html ├── .editorconfig ├── .gitignore ├── babel.config.js ├── README.md ├── package.json ├── utils │ └── index.js └── vue.config.js ├── server ├── api │ ├── index.js │ ├── types.js │ ├── login.js │ └── scene.js ├── .jsbeautifyrc ├── views │ ├── index.jade │ ├── error.jade │ ├── layout.jade │ └── index.art ├── utils │ └── index.js ├── db │ ├── index.js │ ├── handel.js │ └── model.js ├── public │ └── stylesheets │ │ └── style.css ├── .editorconfig ├── const │ └── index.js ├── upload │ ├── a.js │ ├── down.js │ ├── update_music.js │ ├── b.js │ ├── index.js │ └── app.js ├── models │ ├── login.js │ └── scene.js ├── qiniu │ ├── upload.js │ └── move.js ├── bin │ └── www ├── package.json └── app.js ├── h5 ├── babel.config.js ├── public │ ├── favicon.ico │ ├── index.html │ └── index.art ├── src │ ├── assets │ │ └── logo.png │ ├── img │ │ └── view_bg.png │ ├── api │ │ ├── index.js │ │ └── fetch.js │ ├── art │ │ └── index.art │ ├── main.js │ └── css │ │ └── index.scss ├── .gitignore ├── README.md ├── vue.config.js └── package.json ├── .jsbeautifyrc ├── .editorconfig ├── documents ├── question.md ├── todo.md └── changelog.md ├── .gitignore ├── README.md ├── utils └── index.js ├── LICENSE └── package.json /backstage/src/css/keyframe.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/src/store/edit/action/item.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/src/store/edit/action/phone.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/src/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | revoke: true 3 | } 4 | -------------------------------------------------------------------------------- /server/api/index.js: -------------------------------------------------------------------------------- 1 | require('./edit') 2 | require('./scene') 3 | require('./login') -------------------------------------------------------------------------------- /backstage/src/page/list/bus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | export default new Vue; 3 | -------------------------------------------------------------------------------- /h5/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } -------------------------------------------------------------------------------- /.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent_size": "4", 3 | "brace_style": "collapse-preserve-inline" 4 | } -------------------------------------------------------------------------------- /h5/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/h5/public/favicon.ico -------------------------------------------------------------------------------- /h5/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/h5/src/assets/logo.png -------------------------------------------------------------------------------- /h5/src/img/view_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/h5/src/img/view_bg.png -------------------------------------------------------------------------------- /backstage/src/img/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/backstage/src/img/git.png -------------------------------------------------------------------------------- /backstage/src/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/backstage/src/img/logo.png -------------------------------------------------------------------------------- /backstage/src/img/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/backstage/src/img/logo2.png -------------------------------------------------------------------------------- /server/.jsbeautifyrc: -------------------------------------------------------------------------------- 1 | { 2 | "indent_size": "2", 3 | "brace_style": "collapse-preserve-inline" 4 | } -------------------------------------------------------------------------------- /backstage/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/backstage/public/favicon.ico -------------------------------------------------------------------------------- /server/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} 6 | -------------------------------------------------------------------------------- /backstage/src/img/logo_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/backstage/src/img/logo_header.png -------------------------------------------------------------------------------- /backstage/src/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/backstage/src/iconfont/iconfont.eot -------------------------------------------------------------------------------- /backstage/src/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/backstage/src/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /backstage/src/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Braised-Cakes/myh5/HEAD/backstage/src/iconfont/iconfont.woff -------------------------------------------------------------------------------- /server/views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /h5/src/api/index.js: -------------------------------------------------------------------------------- 1 | import fetch from './fetch' 2 | 3 | export const getSceneData = data => fetch('/aj/scene/getPublishData', data); 4 | -------------------------------------------------------------------------------- /server/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取当前时间 3 | */ 4 | module.exports.getTime = function () { 5 | return new Date().getTime() 6 | } 7 | -------------------------------------------------------------------------------- /server/db/index.js: -------------------------------------------------------------------------------- 1 | let mongoose = require('mongoose') 2 | let db = mongoose.createConnection('mongodb://localhost/test') 3 | mongoose.Promise = Promise 4 | module.exports = db -------------------------------------------------------------------------------- /server/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /server/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /backstage/src/css/variables.scss: -------------------------------------------------------------------------------- 1 | $headerHeight:56px; 2 | $panelZIndex:1002; 3 | $headerZIndex:2; 4 | $pageZIndex:1; 5 | $helpZIndex:1000; 6 | $maskZIndex:1001; 7 | $workspaceZIndex:3; -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /server/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /backstage/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /backstage/src/tpl/types.js: -------------------------------------------------------------------------------- 1 | /* 2 | 元素系列 3 | */ 4 | 5 | export const TXT = 'txt'; 6 | 7 | export const SHAPE = 'shape'; 8 | export const MUSIC = 'music'; 9 | 10 | export const IMAGE = 'image'; 11 | 12 | export const QRCODE = 'qrcode'; -------------------------------------------------------------------------------- /backstage/src/css/index.scss: -------------------------------------------------------------------------------- 1 | @import '~@/css/variables.scss'; 2 | .my-mask { 3 | position: fixed; 4 | left: 0; 5 | top: 0; 6 | width: 100%; 7 | height: 100%; 8 | opacity: 0.5; 9 | background: #000; 10 | z-index: $maskZIndex; 11 | } 12 | -------------------------------------------------------------------------------- /documents/question.md: -------------------------------------------------------------------------------- 1 | # Question 2 | 3 | ## crop 4 | 5 | - [ ] 调用方式? 6 | - [ ] 如何处理图片尺寸问题? 7 | 8 | ```javascript 9 | this.$crop({ 10 | src : 'http://baidu.com/a.png', 11 | callback: ()=>{} 12 | }) 13 | 14 | 15 | 增加两个字段 16 | 17 | originPath 18 | 19 | crop 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /server/const/index.js: -------------------------------------------------------------------------------- 1 | module.exports.DEFAULT_PAGE = { 2 | limit: 15, 3 | page: 1 4 | } 5 | 6 | module.exports.AJ_MESSAGE = { 7 | success: 'success', 8 | error: 'error', 9 | 10 | } 11 | 12 | module.exports.AJ_STATUS = { 13 | success: 0, 14 | error: 1, 15 | notlogin: 2 16 | } 17 | -------------------------------------------------------------------------------- /h5/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /backstage/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /dist2 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | 23 | .key.js -------------------------------------------------------------------------------- /backstage/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ], 5 | plugins: [ 6 | [ 7 | "component", 8 | { 9 | "libraryName": "element-ui", 10 | "styleLibraryName": "theme-chalk" 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /h5/README.md: -------------------------------------------------------------------------------- 1 | # h4 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | yarn run lint 21 | ``` 22 | -------------------------------------------------------------------------------- /backstage/src/store/edit/index.js: -------------------------------------------------------------------------------- 1 | import actions from './action' 2 | import getters from './getters' 3 | import mutations from './mutations' 4 | import * as constant from '@/constant' 5 | 6 | const state = { 7 | ...constant.initState 8 | } 9 | 10 | export default { 11 | state, 12 | getters, 13 | actions, 14 | mutations 15 | } -------------------------------------------------------------------------------- /backstage/README.md: -------------------------------------------------------------------------------- 1 | # backstage 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | yarn run lint 21 | ``` 22 | -------------------------------------------------------------------------------- /server/upload/a.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path'); 3 | const { 4 | uploadFile 5 | } = require('./index.js'); 6 | 7 | 8 | 9 | (async () => { 10 | await uploadFile('yq0KZVbqTEaAXk33AAAD205QFoU790.svg', '/Users/BraisedCakes/Desktop/2018/myh5-store/svg/yq0KZVbqTEaAXk33AAAD205QFoU790.svg') 11 | console.log('成功一张') 12 | })() 13 | -------------------------------------------------------------------------------- /server/db/handel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const models = require("./model.js") 3 | const db = require("./index.js") 4 | const Schemas = {} 5 | for (let key in models) { 6 | Schemas[key] = new mongoose.Schema(models[key]) 7 | } 8 | 9 | module.exports = { 10 | getModel: function (item) { 11 | return db.model(item, Schemas[item]) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # myh5 2 | 3 | > myh5页面制作工具, 一键生成H5酷炫页面, 类似于易企秀! 4 | 5 | ## 技术栈 6 | 7 | vue, node, sass, express, mongoose 8 | 9 | 10 | ## Build Setup 11 | 12 | ``` bash 13 | # install dependencies 14 | npm install 15 | 16 | # serve with hot reload at localhost:8080 17 | npm run start 18 | 19 | # build for production with minification 20 | npm run build 21 | 22 | ``` 23 | ## CHANGELOG 24 | 25 | [@CHANGELOG](./documents/changelog.md) 26 | 27 | ## TODU 28 | 29 | [@TODU](./documents/todo.md) -------------------------------------------------------------------------------- /backstage/src/img/phonewhite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/api/types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 登录相关 3 | */ 4 | module.exports.getUserInfo = '/aj/user/info' 5 | module.exports.userLogin = '/aj/user/login' 6 | module.exports.userRegister = '/aj/user/register' 7 | module.exports.userLogout = '/aj/user/logout' 8 | 9 | 10 | /** 11 | * 指定场景的操作 12 | */ 13 | module.exports.getSceneData = '/aj/scene/get' 14 | module.exports.getPublishScene = '/aj/scene/getPublishData' 15 | module.exports.saveScene = '/aj/scene/save' 16 | module.exports.updateScene = '/aj/scene/update' 17 | -------------------------------------------------------------------------------- /h5/src/api/fetch.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | 3 | export default async function (url, data, type = 'GET') { 4 | return new Promise((resolve) => { 5 | $.ajax({ 6 | url: url, 7 | dataType: 'json', 8 | data: type == 'GET' ? data : JSON.stringify(data), 9 | type: type, 10 | // contentType: 'application/json; charset=UTF-8', 11 | success(rs) { 12 | resolve(rs); 13 | } 14 | }); 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /server/models/login.js: -------------------------------------------------------------------------------- 1 | let dbHandel = require('../db/handel.js') 2 | let collection = dbHandel.getModel('user') 3 | 4 | module.exports = { 5 | // 判断用户名和密码是否匹配 6 | goLogin(user, pass) { 7 | return collection.findOne({ username: user, password: pass }) 8 | }, 9 | 10 | // 判断用户名是否存在 11 | getUser(user) { 12 | return collection.findOne({ username: user }) 13 | }, 14 | 15 | //注册 16 | login({ user, pass, uid }) { 17 | return new collection({ username: user, password: pass, uid: uid }).save() 18 | } 19 | } -------------------------------------------------------------------------------- /backstage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backstage", 3 | "version": "0.3.0", 4 | "scripts": { 5 | "dev": "vue-cli-service serve --open", 6 | "server": "node ./server/bin/www", 7 | "build": "vue-cli-service build", 8 | "start": "concurrently \"yarn dev\" \"yarn server\"", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "postcss": { 12 | "plugins": { 13 | "autoprefixer": {} 14 | } 15 | }, 16 | "browserslist": [ 17 | "> 1%", 18 | "last 2 versions", 19 | "not ie <= 8" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /documents/todo.md: -------------------------------------------------------------------------------- 1 | 2 | # TODU 3 | 4 | * ## h5 5 | - [ ] 发布状态才允许查看 6 | 7 | * ## 列表页 8 | - [x] 创建 9 | - [x] 删除 10 | - [x] 复制 11 | - [x] 回收站 12 | - [x] 发布状态 13 | 14 | * ## 元素操作 15 | - [x] 添加 16 | - [x] 删除 17 | - [x] 单选 18 | - [x] 多选 19 | - [x] 撤销&&重做 20 | 21 | * ## 登录 22 | - [x] 注册 23 | - [x] 登录 24 | - [ ] 找回密码 25 | - [ ] 接入qq登录 26 | 27 | * ## 图片 28 | - [x] 图片库 29 | - [x] 最近使用 30 | - [x] 用户上传 31 | - [ ] 图片管理 32 | - [x] 图片裁切功能 33 | 34 | * ## 音乐 35 | - [x] 音乐库 36 | - [x] 最近使用 37 | - [x] 用户上传 38 | - [ ] 添加外链 39 | - [ ] 音乐管理 40 | 41 | * ## 形状 42 | - [x] 形状库 43 | - [x] 最近使用 44 | - [ ] 用户上传 45 | - [ ] 形状管理 46 | 47 | * ## 二维码 48 | - [x] 生成 49 | - [ ] 自定义logo 50 | 51 | * ## 表单 -------------------------------------------------------------------------------- /backstage/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 26 | -------------------------------------------------------------------------------- /h5/src/art/index.art: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{ each data }} 4 |
5 |
6 | {{each $value.data}} 7 |
8 | {{@ $value.content }} 9 |
10 | {{/each}} 11 |
12 |
13 | {{/each}} 14 |
15 |
-------------------------------------------------------------------------------- /backstage/src/tpl/tpl.art: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{ each data }} 4 |
5 |
6 | {{each $value.data}} 7 |
8 | {{@ $value.content }} 9 |
10 | {{/each}} 11 |
12 |
13 | {{/each}} 14 |
15 |
-------------------------------------------------------------------------------- /h5/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | devServer: { 3 | proxy: { 4 | '/aj': { 5 | target: 'http://localhost:3000', // 你接口的域名 6 | secure: true, 7 | changeOrigin: true 8 | }, 9 | '/store': { 10 | target: 'http://localhost:3000', // 你接口的域名 11 | secure: true, 12 | changeOrigin: true 13 | }, 14 | '/show': { 15 | target: 'http://localhost:3000', // 你接口的域名 16 | secure: true, 17 | changeOrigin: true 18 | }, 19 | '/app.js': { 20 | target: 'http://localhost:3000', // 你接口的域名 21 | secure: true, 22 | changeOrigin: true 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backstage/src/components/crop/index.js: -------------------------------------------------------------------------------- 1 | import Toast from './main.vue' 2 | 3 | export default { 4 | install(Vue, options = {}) { 5 | const VueToast = Vue.extend(Toast) 6 | let toast = null 7 | 8 | function $toast(params) { 9 | return new Promise(resolve => { 10 | if (!toast) { 11 | toast = new VueToast() 12 | 13 | toast.$mount() 14 | 15 | document.querySelector(options.container || 'body').appendChild(toast.$el) 16 | } 17 | console.log('plugin done') 18 | toast.init(params) 19 | resolve() 20 | }) 21 | } 22 | 23 | Vue.prototype.$crop = $toast 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backstage/src/components/image/index.js: -------------------------------------------------------------------------------- 1 | import Toast from './main.vue' 2 | 3 | export default { 4 | install(Vue, options = {}) { 5 | const VueToast = Vue.extend(Toast) 6 | let toast = null 7 | 8 | function $toast(params) { 9 | return new Promise(resolve => { 10 | if (!toast) { 11 | toast = new VueToast() 12 | 13 | toast.$mount() 14 | 15 | document.querySelector(options.container || 'body').appendChild(toast.$el) 16 | } 17 | console.log('plugin done') 18 | toast.init(params) 19 | resolve() 20 | }) 21 | } 22 | 23 | Vue.prototype.$image = $toast 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backstage/src/components/music/index.js: -------------------------------------------------------------------------------- 1 | import Toast from './main.vue' 2 | 3 | export default { 4 | install(Vue, options = {}) { 5 | const VueToast = Vue.extend(Toast) 6 | let toast = null 7 | 8 | function $toast(params) { 9 | return new Promise(resolve => { 10 | if (!toast) { 11 | toast = new VueToast() 12 | 13 | toast.$mount() 14 | 15 | document.querySelector(options.container || 'body').appendChild(toast.$el) 16 | } 17 | console.log('plugin done') 18 | toast.init(params) 19 | resolve() 20 | }) 21 | } 22 | 23 | Vue.prototype.$music = $toast 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backstage/src/components/shape/index.js: -------------------------------------------------------------------------------- 1 | import Toast from './main.vue' 2 | 3 | export default { 4 | install(Vue, options = {}) { 5 | const VueToast = Vue.extend(Toast) 6 | let toast = null 7 | 8 | function $toast(params) { 9 | return new Promise(resolve => { 10 | if (!toast) { 11 | toast = new VueToast() 12 | 13 | toast.$mount() 14 | 15 | document.querySelector(options.container || 'body').appendChild(toast.$el) 16 | } 17 | console.log('plugin done') 18 | toast.init(params) 19 | resolve() 20 | }) 21 | } 22 | 23 | Vue.prototype.$shape = $toast 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backstage/src/components/background/index.js: -------------------------------------------------------------------------------- 1 | import Toast from './main.vue' 2 | 3 | export default { 4 | install(Vue, options = {}) { 5 | const VueToast = Vue.extend(Toast) 6 | let toast = null 7 | 8 | function $toast(params) { 9 | return new Promise(resolve => { 10 | if (!toast) { 11 | toast = new VueToast() 12 | 13 | toast.$mount() 14 | 15 | document.querySelector(options.container || 'body').appendChild(toast.$el) 16 | } 17 | console.log('plugin done') 18 | toast.init(params) 19 | resolve() 20 | }) 21 | } 22 | 23 | Vue.prototype.$image = $toast 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /backstage/src/css/mixin.scss: -------------------------------------------------------------------------------- 1 | //定位上下居中 2 | @mixin ct { 3 | position: absolute; 4 | top: 50%; 5 | transform: translateY(-50%); 6 | } 7 | 8 | //定位左右居中 9 | @mixin cl { 10 | position: absolute; 11 | left: 50%; 12 | transform: translateX(-50%); 13 | } 14 | 15 | //定位上下左右居中 16 | @mixin ctl { 17 | position: absolute; 18 | left: 50%; 19 | top: 50%; 20 | transform: translate(-50%, -50%); 21 | } 22 | 23 | //宽高 24 | @mixin wh($width, $height) { 25 | width: $width; 26 | height: $height; 27 | } 28 | 29 | //字体大小,颜色 30 | @mixin sc($size, $color) { 31 | font-size: $size; 32 | color: $color; 33 | } 34 | 35 | //flex 布局和 子元素 对其方式 36 | @mixin fj($type: space-between) { 37 | display: flex; 38 | justify-content: $type; 39 | } 40 | -------------------------------------------------------------------------------- /backstage/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import $ from 'jquery' 4 | import edit from './edit' 5 | import * as constant from '@/constant' 6 | Vue.use(Vuex); 7 | const state = { 8 | auth: { 9 | request: false, 10 | username: null 11 | } 12 | }; 13 | const getters = {}; 14 | const actions = {}; 15 | const mutations = { 16 | UPDATE_USER_INFO(state, { 17 | username, 18 | uid 19 | }) { 20 | state.auth.username = username; 21 | state.auth.uid = uid; 22 | } 23 | } 24 | export default new Vuex.Store({ 25 | strict: process.env.NODE_ENV !== 'production', 26 | state, 27 | getters, 28 | actions, 29 | mutations, 30 | modules: { 31 | edit 32 | } 33 | }) -------------------------------------------------------------------------------- /backstage/src/api/fetch.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | 3 | export default async function (url, data, type = 'GET') { 4 | return new Promise((resolve, reject) => { 5 | $.ajax({ 6 | url: url, 7 | dataType: 'json', 8 | data: type == 'GET' ? data : JSON.stringify(data), 9 | type: type, 10 | contentType: 'application/json; charset=UTF-8', 11 | xhrFields: { 12 | withCredentials: true 13 | }, 14 | // crossDomain: true, 15 | success(rs) { 16 | //为登录 17 | if (rs.status == 2) { 18 | document.location = '/login'; 19 | reject(); 20 | } else { 21 | resolve(rs); 22 | } 23 | } 24 | }); 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /server/models/scene.js: -------------------------------------------------------------------------------- 1 | let dbHandel = require('../db/handel.js') 2 | let collection = dbHandel.getModel('myh5') 3 | 4 | module.exports = { 5 | // 获取场景数量 6 | getSceneCount(data = {}) { 7 | return collection.countDocuments(data) 8 | }, 9 | 10 | // 添加场景 11 | addScene(data) { 12 | return new collection(data).save() 13 | }, 14 | 15 | // 获取场景列表 16 | getSceneList(data, { page, limit }) { 17 | return collection 18 | .find(data) 19 | .skip((page - 1) * limit) 20 | .limit(limit) 21 | .sort({ updateTime: -1 }) 22 | }, 23 | 24 | // 获取指定场景 25 | getSceneById(id) { 26 | return collection.findOne({ id: id }) 27 | }, 28 | 29 | // 更新指定场景 30 | updateSceneById(id, data) { 31 | return collection.update({ id: id }, data) 32 | }, 33 | 34 | // 删除指定场景 35 | delSceneById(id) { 36 | return collection.update({ id: id }, { status: 3 }) 37 | } 38 | } -------------------------------------------------------------------------------- /server/qiniu/upload.js: -------------------------------------------------------------------------------- 1 | var qiniu = require("qiniu") 2 | const { 3 | AccessKey, 4 | SecretKey 5 | } = require('../../.key.js') 6 | 7 | module.exports.upload = function (bucket, localFile, key) { 8 | var mac = new qiniu.auth.digest.Mac(AccessKey, SecretKey); 9 | var options = { 10 | scope: bucket, 11 | }; 12 | var putPolicy = new qiniu.rs.PutPolicy(options); 13 | var uploadToken = putPolicy.uploadToken(mac); 14 | var config = new qiniu.conf.Config(); 15 | // 空间对应的机房 16 | config.zone = qiniu.zone.Zone_z2; 17 | let glob = require('glob') 18 | var formUploader = new qiniu.form_up.FormUploader(config); 19 | var putExtra = new qiniu.form_up.PutExtra(); 20 | return new Promise((resolve) => { 21 | formUploader.putFile(uploadToken, key, localFile, putExtra, function (respErr, 22 | respBody, respInfo) { 23 | if (respErr) { 24 | throw respErr; 25 | } 26 | resolve(true) 27 | }); 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /server/upload/down.js: -------------------------------------------------------------------------------- 1 | const dbHandel = require('../server/db/handel.js') 2 | let collection = dbHandel.getModel('shape') 3 | const fs = require('fs') 4 | const request = require('request') 5 | function downloadFile(uri, filename) { 6 | var stream = fs.createWriteStream(filename); 7 | return new Promise((resolve, reject) => { 8 | request(uri).pipe(stream).on('close', function () { 9 | // let timer = parseInt(Math.random() * 1000) + 500; 10 | // setTimeout(() => { 11 | resolve(); 12 | // }, timer) 13 | }); 14 | }) 15 | } 16 | 17 | 18 | (async () => { 19 | let docs = await collection.find(); 20 | // console.log(docs) 21 | for(let i = 0; i < docs.length; i++){ 22 | let url = `http://p7d4z759a.bkt.clouddn.com/${docs[i].path}`; 23 | 24 | await downloadFile(url, '/Users/BraisedCakes/Desktop/2018/myh5-store/svg/' + docs[i].path) 25 | console.log('当前第' + i + '个'); 26 | } 27 | })() 28 | -------------------------------------------------------------------------------- /h5/src/main.js: -------------------------------------------------------------------------------- 1 | import 'minireset.css' 2 | import $ from 'jquery' 3 | import 'animate.css' 4 | import '@/css/index.scss' 5 | import * as utils from '../../utils/index' 6 | import * as api from '@/api/index' 7 | api.getSceneData({ 8 | id: 20 9 | }).then((res) => { 10 | console.log(res); 11 | }) 12 | new window.Swiper('.swiper-container', { 13 | direction: 'vertical', 14 | loop: true, 15 | on: { 16 | transitionStart() { 17 | $('.swiper-slide').eq(this.activeIndex).each((index, item) => { 18 | $(item).find('.items').each((index2, item2) => { 19 | $('.' + $(item2).attr('data-id')).css('animation', ''); 20 | $('.' + $(item2).attr('data-id')).show(); 21 | utils.runAni($(item2).attr('data-id'), JSON.parse($('.' + $(item2).attr('data-id')).attr('v-data'))) 22 | }) 23 | }) 24 | }, 25 | slideChangeTransitionEnd() {} 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /utils/index.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | /** 3 | * 小数点后保留几位 4 | * @param {*} num 数字 5 | * @param {*} n 小数点后保留几位 6 | */ 7 | export const round = function (num, n = 2) { 8 | return Number(parseFloat(num).toFixed(n)); 9 | } 10 | export const runAni = function (id, ani, aniIndex) { 11 | let str = ''; 12 | let delay = 0; 13 | ani.forEach(($item, $index) => { 14 | let aniName = $item['animation-name']; 15 | let aniDuration = $item['animation-duration']; 16 | let aniDelay = $item['animation-delay']; 17 | if (typeof aniIndex == 'number' && aniIndex != $index) { 18 | return; 19 | } else if (typeof aniIndex != 'number' && $index > 0) { 20 | str += ',' 21 | } 22 | str = str + 23 | `${aniName} ${aniDuration}s ${round(delay + round(aniDelay, 1), 1)}s 1 normal`; 24 | delay += round(parseFloat(aniDelay) + parseFloat(aniDuration), 1); 25 | }); 26 | 27 | $('.' + id).css({ 28 | 'animation': str 29 | }) 30 | }; 31 | -------------------------------------------------------------------------------- /backstage/utils/index.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | /** 3 | * 小数点后保留几位 4 | * @param {*} num 数字 5 | * @param {*} n 小数点后保留几位 6 | */ 7 | export const round = function (num, n = 2) { 8 | return Number(parseFloat(num).toFixed(n)); 9 | } 10 | export const runAni = function (id, ani, aniIndex) { 11 | let str = ''; 12 | let delay = 0; 13 | ani.forEach(($item, $index) => { 14 | let aniName = $item['animation-name']; 15 | let aniDuration = $item['animation-duration']; 16 | let aniDelay = $item['animation-delay']; 17 | if (typeof aniIndex == 'number' && aniIndex != $index) { 18 | return; 19 | } else if (typeof aniIndex != 'number' && $index > 0) { 20 | str += ',' 21 | } 22 | str = str + 23 | `${aniName} ${aniDuration}s ${round(delay + round(aniDelay, 1), 1)}s 1 normal`; 24 | delay += round(parseFloat(aniDelay) + parseFloat(aniDuration), 1); 25 | }); 26 | 27 | $('.' + id).css({ 28 | 'animation': str 29 | }) 30 | }; 31 | -------------------------------------------------------------------------------- /backstage/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | myh5-project 14 | 15 | 16 | 17 | 20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /backstage/src/store/edit/mutation-types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * page页 3 | */ 4 | export const ADD_PAGE = 'ADD_PAGE'; //添加 5 | export const DEL_PAGE = 'DEL_PAGE'; //删除 6 | export const SELECT_PAGE = 'SELECT_PAGE'; //选中 7 | export const SORT_PAGE = 'SORT_PAGE'; //排序 8 | /** 9 | * phone页面 10 | */ 11 | export const SET_PHONE = 'SET_PHONE'; 12 | 13 | export const ADD_ITEM = 'ADD_ITEM'; 14 | 15 | export const UPDATE_PHONE = 'UPDATE_PHONE'; 16 | export const UPDATE_MAIN = 'UPDATE_MAIN'; 17 | export const UPDATE_ITEM = 'UPDATE_ITEM'; 18 | export const CHANGE_DATA = 'CHANGE_DATA'; 19 | 20 | /** 21 | * 22 | */ 23 | export const SELECT_ITEM = 'SELECT_ITEM'; 24 | export const DEL_ITEM = 'DEL_ITEM'; 25 | export const ADD_CREATED_ID = 'ADD_CREATED_ID'; 26 | /** 27 | * 打开or关闭面板 28 | */ 29 | export const OPEN_PANEL = 'OPEN_PANEL'; 30 | export const CLOSE_PANEL = 'CLOSE_PANEL'; 31 | //记录 32 | export const RECORD = 'RECORD'; 33 | //撤销 34 | export const REVOKE = 'REVOKE'; 35 | //重做 36 | export const REDO = 'REDO'; 37 | //更新otherInfo 38 | export const OTHERINFO = 'OTHERINFO'; 39 | -------------------------------------------------------------------------------- /backstage/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | devServer: { 3 | proxy: { 4 | '/aj': { 5 | target: 'http://localhost:3000', // 你接口的域名 6 | secure: true, 7 | changeOrigin: true 8 | }, 9 | '/store': { 10 | target: 'http://localhost:3000', // 你接口的域名 11 | secure: true, 12 | changeOrigin: true 13 | }, 14 | // '/svg': { 15 | // target: 'http://p7d4z759a.bkt.clouddn.com', // 你接口的域名 16 | // secure: true, 17 | // changeOrigin: true 18 | // } 19 | } 20 | }, 21 | configureWebpack: { 22 | module: { 23 | rules: [{ 24 | test: /\.art$/, 25 | loader: "art-template-loader", 26 | options: { 27 | // art-template options (if necessary) 28 | // @see https://github.com/aui/art-template 29 | } 30 | }] 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backstage/src/css/reset.css: -------------------------------------------------------------------------------- 1 | /*! minireset.css v0.0.3 | MIT License | github.com/jgthms/minireset.css */ 2 | 3 | html, 4 | body, 5 | p, 6 | ol, 7 | ul, 8 | li, 9 | dl, 10 | dt, 11 | dd, 12 | blockquote, 13 | figure, 14 | fieldset, 15 | legend, 16 | textarea, 17 | pre, 18 | iframe, 19 | hr, 20 | h1, 21 | h2, 22 | h3, 23 | h4, 24 | h5, 25 | h6 { 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | /* h1, 31 | h2, 32 | h3, 33 | h4, 34 | h5, 35 | h6 { 36 | font-size: 100%; 37 | font-weight: normal; 38 | } */ 39 | 40 | ul { 41 | list-style: none; 42 | } 43 | 44 | button, 45 | input, 46 | select, 47 | textarea { 48 | margin: 0; 49 | } 50 | 51 | html { 52 | box-sizing: border-box; 53 | } 54 | 55 | *, 56 | *:before, 57 | *:after { 58 | box-sizing: inherit; 59 | } 60 | 61 | img, 62 | embed, 63 | iframe, 64 | object, 65 | audio, 66 | video { 67 | height: auto; 68 | max-width: 100%; 69 | } 70 | 71 | iframe { 72 | border: 0; 73 | } 74 | 75 | table { 76 | border-collapse: collapse; 77 | border-spacing: 0; 78 | } 79 | 80 | td, 81 | th { 82 | padding: 0; 83 | text-align: left; 84 | } 85 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 刘少鹏 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /backstage/src/css/element-variables.scss: -------------------------------------------------------------------------------- 1 | /* 改变主题色变量 */ 2 | 3 | // $--color-primary: #fd8527; 4 | /* 改变 icon 字体路径变量,必需 */ 5 | 6 | $--font-path: '../../node_modules/element-ui/lib/theme-chalk/fonts'; 7 | @import "../../node_modules/element-ui/packages/theme-chalk/src/index"; 8 | .el-collapse-item__header { 9 | padding-left: 20px; 10 | background: #f6f9fa; 11 | line-height: 32px; 12 | height: 32px; 13 | } 14 | 15 | .el-collapse-item__arrow { 16 | line-height: 32px !important; 17 | } 18 | 19 | .el-select--mini { 20 | width: 130px; 21 | } 22 | 23 | .el-input--mini { 24 | width: 130px; 25 | } 26 | 27 | .el-radio-group { 28 | width: 130px; 29 | .el-radio+.el-radio { 30 | margin-left: 0; 31 | } 32 | .el-radio__label { 33 | font-size: 12px; 34 | } 35 | } 36 | 37 | .el-upload-dragger { 38 | width: 160px; 39 | height: 50px; 40 | border-style: none; 41 | background-color: inherit; 42 | } 43 | 44 | .el-notification { 45 | p { 46 | word-break: break-all; 47 | } 48 | } 49 | 50 | .my-el-dialog { 51 | .el-dialog__header, 52 | .el-dialog__body { 53 | padding: 0 !important; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /h5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "h4", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "minireset.css": "^0.0.3", 12 | "vue": "^2.5.16" 13 | }, 14 | "devDependencies": { 15 | "@vue/cli-plugin-babel": "^3.0.0-rc.5", 16 | "@vue/cli-plugin-eslint": "^3.0.0-rc.5", 17 | "@vue/cli-service": "^3.0.0-rc.5", 18 | "vue-template-compiler": "^2.5.16" 19 | }, 20 | "eslintConfig": { 21 | "root": true, 22 | "env": { 23 | "node": true 24 | }, 25 | "extends": [ 26 | "plugin:vue/essential", 27 | "eslint:recommended" 28 | ], 29 | "rules": { 30 | "no-console": "off" 31 | }, 32 | "parserOptions": { 33 | "parser": "babel-eslint" 34 | } 35 | }, 36 | "postcss": { 37 | "plugins": { 38 | "autoprefixer": {} 39 | } 40 | }, 41 | "browserslist": [ 42 | "> 1%", 43 | "last 2 versions", 44 | "not ie <= 8" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /backstage/src/img/show.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/upload/update_music.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const glob = require('glob') 3 | var request = require('request'); 4 | var path = require('path') 5 | 6 | function downloadFile(uri, filename, callback) { 7 | var stream = fs.createWriteStream(filename); 8 | return new Promise((resolve, reject) => { 9 | request(uri).pipe(stream).on('close', function () { 10 | let timer = parseInt(Math.random() * 1000) + 500; 11 | setTimeout(() => { 12 | resolve(); 13 | }, timer) 14 | }); 15 | }) 16 | } 17 | 18 | 19 | let fileList = glob.sync(`/Users/BraisedCakes/Desktop/2018/myh5-store/musics/*.json`); 20 | let allNum = 0; 21 | (async function () { 22 | for (let i = 0; i < fileList.length; i++) { 23 | let docs = JSON.parse(fs.readFileSync(fileList[i], 'utf-8')); 24 | for (let j = 0; j < docs.length; j++) { 25 | console.log(`${path.basename(fileList[i])}类型共需下载${docs.length}张, 已下载${j}张`) 26 | if (!docs[j].path) { 27 | console.log('path不存在') 28 | continue; 29 | } 30 | if (glob.sync(`/Users/BraisedCakes/Desktop/2018/myh5-store/music/${path.basename(docs[j].path)}`).length != 0) { 31 | continue; 32 | } 33 | let fileUrl = `http://res1.eqh5.com/${docs[j].path}`; 34 | let filename = `/Users/BraisedCakes/Desktop/2018/myh5-store/music/${path.basename(docs[j].path)}`; 35 | await downloadFile(fileUrl, filename) 36 | } 37 | } 38 | })() 39 | -------------------------------------------------------------------------------- /backstage/src/img/music_btn.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /h5/src/css/index.scss: -------------------------------------------------------------------------------- 1 | .loading { 2 | background: #48424f; 3 | width: 100%; 4 | height: 100%; 5 | position: absolute; 6 | z-index: 9999; 7 | top: 0; 8 | } 9 | 10 | .loadbox { 11 | position: relative; 12 | height: 70px; 13 | margin: 0 auto; 14 | margin-top: 50%; 15 | } 16 | 17 | .loadbg, 18 | .loadbox { 19 | display: block; 20 | width: 70px; 21 | } 22 | 23 | .loadbg { 24 | position: absolute; 25 | z-index: 1000; 26 | height: 42px; 27 | background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjQwIj48cmVjdCBmaWxsPSIjMDhhMWVmIiB4PSI0IiB5PSI0IiB3aWR0aD0iMzkyIiBoZWlnaHQ9IjIzMiIvPjxjaXJjbGUgaWQ9ImFjdG9yXzMiIGN4PSIwIiBjeT0iMCIgcj0iMzAiIGZpbGw9IiM5QUQ2NEIiPjxhbmltYXRlTW90aW9uIHBhdGg9Ik0zOSwxMjBMNzgsNTBIMTYxTDIzOSwxOTBIMzIyTDM2MSwxMjBMMzIyLDUwSDIzOUwxNjEsMTkwSDc4TDM5LDEyMCIgZHVyPSIxcyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiLz48L2NpcmNsZT48cGF0aCBkPSJNMCwyNDBWMGg0MDB2MjQwSDB6IE0zMzAuNzY5LDM0aC05OC40NjJsLTgxLjUzOCwxNDEuMzQ1SDg2LjE1NEw1My44NDYsMTE5LjVsMzIuMzA4LTU1Ljg0NWg2NC42MTVsMjMuODQ2LDQwLjgyNGwxNi45MjMtMjkuMjdMMTY3LjY5MiwzNEg2OS4yMzFMMjAsMTE5LjVMNjkuMjMxLDIwNWg5OC40NjFsODEuNTM4LTE0MS4zNDVoNjQuNjE1bDMyLjMwOCw1NS44NDVsLTMyLjMwOCw1NS44NDVoLTY0LjYxNWwtMjMuODQ2LTQwLjgyNGwtMTYuOTIzLDI5LjI3TDIzMi4zMDgsMjA1aDk4LjQ2MkwzODAsMTE5LjVMMzMwLjc2OSwzNHoiIGZpbGw9IiM0ODQyNEYiLz48L3N2Zz4=) 28 | } 29 | 30 | .loading-tip { 31 | display: none; 32 | position: absolute; 33 | bottom: 40px; 34 | font-size: 16px; 35 | z-index: 9999; 36 | width: 200px; 37 | left: 50%; 38 | margin-left: -100px; 39 | text-align: center; 40 | color: #7986cb; 41 | } 42 | -------------------------------------------------------------------------------- /server/qiniu/move.js: -------------------------------------------------------------------------------- 1 | var qiniu = require("qiniu") 2 | const { 3 | AccessKey, 4 | SecretKey 5 | } = require('../../.key.js') 6 | const dbHandel = require('../db/handel.js') 7 | 8 | var mac = new qiniu.auth.digest.Mac(AccessKey, SecretKey); 9 | var config = new qiniu.conf.Config(); 10 | var bucketManager = new qiniu.rs.BucketManager(mac, config); 11 | // var srcBucket = 'public'; 12 | // var destBucket = 'store'; 13 | //每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送 14 | var moveOperations = [ 15 | // qiniu.rs.moveOp(srcBucket, '00105d08-3252-47cf-b5c5-7f48f430a46c.svg', destBucket, '00105d08-3252-47cf-b5c5-7f48f430a46c.svg') 16 | ]; 17 | 18 | (async () => { 19 | const collection = dbHandel.getModel('musics'); 20 | let obj = await collection.find().skip(12000).limit(1000); 21 | obj.forEach((item) => { 22 | moveOperations.push( 23 | qiniu.rs.moveOp('music', item.path, 'store', item.path) 24 | ) 25 | }) 26 | console.log(moveOperations) 27 | bucketManager.batch(moveOperations, function (err, respBody, respInfo) { 28 | if (err) { 29 | console.log(err); 30 | //throw err; 31 | } else { 32 | // 200 is success, 298 is part success 33 | if (parseInt(respInfo.statusCode / 100) == 2) { 34 | respBody.forEach(function (item) { 35 | if (item.code == 200) { 36 | console.log(item.code + "\tsuccess"); 37 | } else { 38 | console.log(item.code + "\t" + item.data.error); 39 | } 40 | }); 41 | } else { 42 | console.log(respInfo.deleteusCode); 43 | console.log(respBody); 44 | } 45 | } 46 | }); 47 | 48 | })() 49 | -------------------------------------------------------------------------------- /backstage/src/keycode/index.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | import store from '@/store/index.js' 3 | 4 | const keyCode = { 5 | up: 38, 6 | down: 40, 7 | left: 37, 8 | right: 39, 9 | delete: 8 10 | } 11 | 12 | $(window).keydown(function (ev) { 13 | if (document.activeElement != document.querySelector('body')) { 14 | return; 15 | } 16 | /** 17 | * up 38 18 | * down 40 19 | * left 37 20 | * right 39 21 | */ 22 | if (!store.getters.hasSelectedItems) { 23 | return; 24 | } 25 | switch (ev.keyCode) { 26 | case keyCode.up: 27 | store.dispatch('updateItem', { 28 | key: 'style', 29 | val: { 30 | 'top': parseInt(store.getters.curItem.style.top) - 1 + 'px' 31 | } 32 | }); 33 | break; 34 | case keyCode.down: 35 | store.dispatch('updateItem', { 36 | key: 'style', 37 | val: { 38 | 'top': parseInt(store.getters.curItem.style.top) + 1 + 'px' 39 | } 40 | }); 41 | break; 42 | case keyCode.left: 43 | store.dispatch('updateItem', { 44 | key: 'style', 45 | val: { 46 | 'left': parseInt(store.getters.curItem.style.left) - 1 + 'px' 47 | } 48 | }); 49 | break; 50 | case keyCode.right: 51 | store.dispatch('updateItem', { 52 | key: 'style', 53 | val: { 54 | 'left': parseInt(store.getters.curItem.style.left) + 1 + 'px' 55 | } 56 | }); 57 | break; 58 | case keyCode.delete: 59 | store.dispatch('delItem') 60 | break; 61 | } 62 | }); 63 | -------------------------------------------------------------------------------- /backstage/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Cookies from 'js-cookie' 4 | import store from '@/store/index.js' 5 | 6 | Vue.use(Router); 7 | 8 | let routes = new Router({ 9 | linkActiveClass: 'active', 10 | mode: 'history', 11 | routes: [{ 12 | path: '/list', 13 | name: 'list', 14 | meta: { 15 | requiresAuth: true 16 | }, 17 | component: () => 18 | import ( /* webpackChunkName: "list" */ '@/page/list/list.vue') 19 | }, { 20 | path: '/edit/:id', 21 | name: 'edit', 22 | meta: { 23 | requiresAuth: true 24 | }, 25 | component: () => 26 | import ( /* webpackChunkName: "edit" */ '@/page/edit/edit.vue') 27 | }, { 28 | path: '/detail/:id', 29 | name: 'detail', 30 | meta: { 31 | requiresAuth: true 32 | }, 33 | component: () => 34 | import ( /* webpackChunkName: "detail" */ '@/page/detail/detail.vue') 35 | }, { 36 | path: '/login', 37 | name: 'login', 38 | component: () => 39 | import ( /* webpackChunkName: "login" */ '@/page/login/login.vue') 40 | }] 41 | }); 42 | 43 | routes.beforeEach((to, from, next) => { 44 | let token = Cookies('username'); 45 | if (!to.name) { 46 | next({ 47 | name: 'list' 48 | }); 49 | } else if (to.name == 'login') { 50 | if (!token || !store.state.auth.username) { 51 | next(); 52 | } else { 53 | next({ 54 | name: 'list' 55 | }); 56 | } 57 | } else if (to.params.status == 2 || (to.matched.some(record => record.meta.requiresAuth) && (!token || token === null))) { 58 | next({ 59 | path: '/login' 60 | }); 61 | } else { 62 | next(); 63 | } 64 | }); 65 | 66 | export default routes 67 | -------------------------------------------------------------------------------- /backstage/src/components/dialog/dialog.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 38 | 45 | 46 | 80 | -------------------------------------------------------------------------------- /backstage/src/page/edit/event.vue: -------------------------------------------------------------------------------- 1 | 21 | 45 | 46 | 61 | -------------------------------------------------------------------------------- /documents/changelog.md: -------------------------------------------------------------------------------- 1 | 2 | # Changelog 3 | 4 | ## 0.3.0 / 2018-07-05 5 | - 新增 `背景` 组件 6 | 7 | ## 0.2.9 / 2018-06-26 8 | 9 | - 新增 `场景图` 上传功能 10 | 11 | ## 0.2.8 / 2018-06-12 12 | 13 | - 新增 `图片` 裁切功能 14 | 15 | ## 0.2.7 / 2018-05-31 16 | 17 | - 新增 `编辑页` 预览功能 18 | 19 | ## 0.2.6 / 2018-05-20 20 | - 新增 `列表页` 增加回收站 21 | 22 | ## 0.2.5 / 2018-05-19 23 | - 新增 `列表页` 支持复制场景 24 | 25 | ## 0.2.4 / 2018-05-18 26 | - 新增 `列表页` 创建场景需先填表 27 | 28 | ## 0.2.3 / 2018-05-08 29 | - 新增 登录功能 30 | 31 | ## 0.2.2 / 2018-05-07 32 | - 修复 `Tooltip` 超长字母没有换行问题 33 | - 新增 `元素` 支持多选操作 34 | 35 | ## 0.2.1 / 2018-05-06 36 | - 新增 `音乐库` 支持用户上传音乐 37 | 38 | ## 0.2.0 / 2018-05-04 39 | - 新增 `图片库` 的所有图片托管到七牛云 40 | - 新增 `图片库` 支持用户上传图片 41 | 42 | ## 0.1.10 / 2018-04-23 43 | - 新增 `二维码` 组件 44 | - 新增 对`元素`的撤销和重做操作 45 | 46 | ## 0.1.9 / 2018-04-21 47 | - 新增 `图片库`支持记录最近使用过的`图片` 48 | 49 | ## 0.1.8 / 2018-04-20 50 | - 新增 `图片` 组件 51 | - 修复 `音乐库` 无法取消选中的问题 52 | 53 | ## 0.1.7 / 2018-04-19 54 | - 新增 `形状库`支持记录最近使用过的`形状` 55 | 56 | ## 0.1.6 / 2018-04-17 57 | - 新增 `形状库` 新增多种官方类型 58 | 59 | ## 0.1.5 / 2018-04-16 60 | - 新增 点击事件 61 | 62 | ## 0.1.4 / 2018-04-12 63 | - 新增 `音乐` 组件 64 | 65 | ## 0.1.3 / 2018-04-09 66 | - 新增 `形状` 组件 67 | 68 | ## 0.1.2 / 2018-04-05 69 | - 新增 重新播放动画效果的按钮 70 | - 修复 被复制的页面动画失效的问题 71 | 72 | ## 0.1.1 / 2018-04-03 73 | - 新增 `元素`以唯一id进行区分 74 | 75 | ## 0.1.0 / 2018-04-1 76 | - 新增 动画效果 77 | 78 | ## 0.0.11 / 2018-03-28 79 | - 新增 `元素`可调整层级关系,置顶,上移一层,下移一层,置底 80 | 81 | ## 0.0.10 / 2018-03-27 82 | - 新增 `编辑页` 可删除元素 83 | 84 | ## 0.0.9 / 2018-03-26 85 | - 新增 ⬆️⬇️⬅️➡️调整元素位置 86 | 87 | ## 0.0.8 / 2018-03-23 88 | - 新增 `编辑页` 复制页面的功能 89 | 90 | ## 0.0.7 / 2018-03-22 91 | - 新增 `编辑页` 对背景颜色进行编辑 92 | 93 | ## 0.0.6 / 2018-03-21 94 | - 新增 保存功能 95 | 96 | ## 0.0.5 / 2018-03-20 97 | - 新增 `元素`缩放功能 98 | 99 | ## 0.0.4 / 2018-03-19 100 | - 新增 对`文字`的内容,以及字号,行高,位置,大小,透明度等进行编辑的功能 101 | 102 | ## 0.0.3 / 2018-03-17 103 | - 新增 `编辑页` 的 `页码区域` 支持拖拽排序 104 | - 新增 `编辑页` 增加右侧操作栏 105 | 106 | ## 0.0.2 / 2018-03-15 107 | - 新增 `列表页` 和 `编辑页` 的布局搭建完成 108 | 109 | ## 0.0.1 / 2018-03-08 110 | - 立项 111 | -------------------------------------------------------------------------------- /backstage/src/page/list/list.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 47 | 48 | 76 | -------------------------------------------------------------------------------- /backstage/src/page/list/header.vue: -------------------------------------------------------------------------------- 1 | 16 | 28 | 29 | 79 | -------------------------------------------------------------------------------- /backstage/src/page/edit/edit.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 51 | -------------------------------------------------------------------------------- /server/api/login.js: -------------------------------------------------------------------------------- 1 | let uuid = require('uuid/v1') 2 | let router = require('../app.js') 3 | let userModel = require('../models/login') 4 | let { AJ_STATUS, AJ_MESSAGE } = require('../const/index') 5 | let types = require('./types') 6 | 7 | // 用户注册 8 | router.post(types.userRegister, async (req, res) => { 9 | let user = req.body.username 10 | let pass = req.body.password 11 | try { 12 | let data = await userModel.getUser(req.body.username) 13 | if (!data) { 14 | await userModel.login({ user: user, pass: pass, uid: uuid() }) 15 | res.send({ status: AJ_STATUS.success, message: '注册成功' }) 16 | } else { 17 | throw new Error('用户已经存在') 18 | } 19 | } catch (e) { 20 | res.send({ status: AJ_STATUS.error, message: e.message }) 21 | } 22 | }) 23 | 24 | // 用户登陆 25 | router.post(types.userLogin, async (req, res) => { 26 | let user = req.body.username; 27 | let pass = req.body.password; 28 | try { 29 | let data = await userModel.goLogin(user, pass) 30 | if (!data) { 31 | throw new Error('用户名或密码不正确') 32 | } else { 33 | req.session.isLogin = true 34 | req.session.username = req.body.username 35 | req.session.uid = data.uid 36 | res.cookie('username', req.body.username, { 37 | expires: new Date(Date.now() + 10000 * 60 * 60 * 24 * 7) 38 | }) 39 | res.send({ status: AJ_STATUS.success, message: '登录成功' }) 40 | } 41 | } catch (e) { 42 | res.send({ status: AJ_STATUS.error, message: e.message }) 43 | } 44 | }) 45 | 46 | // 注销 47 | router.post(types.userLogout, (req, res) => { 48 | req.session.isLogin = null 49 | req.session.uid = null 50 | req.session.username = null 51 | res.clearCookie('username') 52 | res.send({ status: AJ_STATUS.success, message: '注销成功' }) 53 | }) 54 | 55 | // 获取用户信息 56 | router.get(types.getUserInfo, async (req, res) => { 57 | try { 58 | if (req.session.isLogin) { 59 | let data = await userModel.getUser(req.session.uid) 60 | res.send({ status: AJ_STATUS.success, message: '获取成功', result: data }) 61 | } else { 62 | throw new Error('没有拿到用户信息') 63 | } 64 | } catch (e) { 65 | res.send({ status: AJ_STATUS.error, message: e.message }) 66 | } 67 | }) -------------------------------------------------------------------------------- /backstage/src/utils/index.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | import store from '@/store/index' 3 | 4 | /** 5 | * 小数点后保留几位 6 | * @param {*} num 数字 7 | * @param {*} n 小数点后保留几位 8 | */ 9 | export const round = function (num, n = 2) { 10 | return Number(parseFloat(num).toFixed(n)); 11 | } 12 | /** 13 | * 执行当前页动画 14 | */ 15 | export const runCurPhoneAni = function (delay) { 16 | store.getters.currentPhone.data.forEach((item) => { 17 | if (delay) { 18 | runAni(item.id, item.animation); 19 | } else { 20 | runAni(item.id, item.animation, null, 1); 21 | } 22 | 23 | }); 24 | } 25 | /** 26 | * 执行动画 27 | * @param {*} id 元素id 28 | * @param {*} ani 元素animation列表 29 | * @param {*} aniIndex 取animation中第几个? 30 | * @param {*} time 是否存在setTimeout 31 | */ 32 | export const runAni = function (id, ani, aniIndex, time) { 33 | let str = ''; 34 | let delay = 0; 35 | ani.forEach(($item, $index) => { 36 | let aniName = $item['animation-name']; 37 | let aniDuration = $item['animation-duration']; 38 | let aniDelay = $item['animation-delay']; 39 | if (typeof aniIndex == 'number' && aniIndex != $index) { 40 | return; 41 | } else if (typeof aniIndex != 'number' && $index > 0) { 42 | str += ',' 43 | } 44 | str = str + 45 | `${aniName} ${aniDuration}s ${round(delay + round(aniDelay, 1), 1)}s 1 normal`; 46 | delay += round(parseFloat(aniDelay) + parseFloat(aniDuration), 1); 47 | }); 48 | if (time) { 49 | $('#' + id + ' .item-body').css({ 50 | 'animation': str 51 | }) 52 | } else { 53 | $('#' + id + ' .item-body').css({ 54 | 'animation': '', 55 | 'animation-play-state': 'paused' 56 | }); 57 | setTimeout(() => { 58 | $('#' + id + ' .item-body').css({ 59 | 'animation': str 60 | }); 61 | }, 30); 62 | } 63 | }; 64 | 65 | 66 | export const openMask = function () { 67 | if (!$('.mask').length) { 68 | $('body').append('
'); 69 | } 70 | } 71 | export const removeMask = function () { 72 | $('.my-mask').remove(); 73 | } 74 | -------------------------------------------------------------------------------- /backstage/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import 'animate.css' 3 | import { 4 | Pagination, 5 | Scrollbar, 6 | Form, 7 | Tooltip, 8 | Loading, 9 | Button, 10 | ColorPicker, 11 | Collapse, 12 | CollapseItem, 13 | InputNumber, 14 | Input, 15 | Select, 16 | Option, 17 | Radio, 18 | RadioGroup, 19 | OptionGroup, 20 | MessageBox, 21 | Notification, 22 | Upload, 23 | FormItem, 24 | Message, 25 | Dialog, 26 | Checkbox 27 | } from 'element-ui'; 28 | import App from '@/App' 29 | import router from '@/router' 30 | import Crop from '@/components/crop' 31 | import Image from '@/components/image' 32 | import Shape from '@/components/shape' 33 | import Music from '@/components/music' 34 | import store from '@/store' 35 | import '@/css/reset.css' 36 | import '@/css/index.css' 37 | import '@/css/index.scss' 38 | import '@/css/element-variables.scss' 39 | import '@/iconfont/iconfont.css' 40 | import '@/directive/select' 41 | import '@/directive/drag' 42 | import "@/directive/changesize"; 43 | import '@/keycode/index' 44 | 45 | Vue.config.productionTip = process.env.NODE_ENV !== 'production'; 46 | Vue.prototype.$ELEMENT = { 47 | size: 'mini', 48 | // zIndex: 3000 49 | }; 50 | Vue.use(Pagination); 51 | Vue.use(Scrollbar); 52 | Vue.use(Form); 53 | Vue.use(Tooltip); 54 | Vue.use(Button); 55 | Vue.use(ColorPicker); 56 | Vue.use(Collapse); 57 | Vue.use(CollapseItem); 58 | Vue.use(InputNumber); 59 | Vue.use(Input); 60 | Vue.use(Select); 61 | Vue.use(Radio); 62 | Vue.use(RadioGroup); 63 | Vue.use(Option); 64 | Vue.use(OptionGroup); 65 | Vue.use(Upload); 66 | Vue.use(FormItem); 67 | Vue.use(Dialog); 68 | Vue.use(Checkbox); 69 | Vue.use(Loading.directive); 70 | Vue.prototype.$loading = Loading.service; 71 | Vue.prototype.$msgbox = MessageBox; 72 | Vue.prototype.$alert = MessageBox.alert; 73 | Vue.prototype.$confirm = MessageBox.confirm; 74 | Vue.prototype.$prompt = MessageBox.prompt; 75 | Vue.prototype.$notify = Notification; 76 | Vue.prototype.$message = Message; 77 | Vue.use(Crop); 78 | Vue.use(Image); 79 | Vue.use(Shape); 80 | Vue.use(Music); 81 | export default new Vue({ 82 | router: router, 83 | store: store, 84 | render: h => h(App) 85 | }).$mount('#app') -------------------------------------------------------------------------------- /server/upload/b.js: -------------------------------------------------------------------------------- 1 | const dbHandel = require('../db/handel.js') 2 | const glob = require('glob') 3 | const fs = require('fs') 4 | const path = require('path') 5 | let docs1 = glob.sync(`/Users/BraisedCakes/Desktop/2018/myh5-store/svg-json/*`); 6 | let id = 400000000; 7 | let shape2 = dbHandel.getModel('shape') 8 | let desc = dbHandel.getModel('desc') 9 | 10 | // shape2.find({}, (err, docs) => { 11 | // for (let i = 0; i < docs.length; i++) { 12 | // if (docs[i].tagId.indexOf(',') != -1) { 13 | // console.log(docs[i].tagId) 14 | // } 15 | // } 16 | // }) 17 | 18 | ; 19 | (async () => { 20 | let descDocs = await desc.findOne({}); 21 | for (let i = 0; i < docs1.length; i++) { 22 | let docs2 = glob.sync(`${docs1[i]}/*.json`); 23 | for (let j = 0; j < docs2.length; j++) { 24 | let data = JSON.parse(fs.readFileSync(docs2[j], 'utf-8')); 25 | //data 当前文件的json数据 26 | for (let z = 0; z < data.length; z++) { 27 | let shapePath = path.basename(data[z].path) 28 | 29 | let itemData = await shape2.findOne({ 30 | path: shapePath 31 | }) 32 | if (!itemData) { 33 | await new shape2({ 34 | path: shapePath, 35 | id: id++, 36 | name: data[z].name, 37 | typeId: String(descDocs.shape.type[i].typeId), 38 | tagId: String(descDocs.shape.type[i].children[j].tagId) 39 | }).save(); 40 | } else { 41 | let typeId = itemData.typeId.split(','); 42 | let tagId = itemData.tagId.split(','); 43 | if (typeId.indexOf(String(descDocs.shape.type[i].typeId)) == -1) { 44 | typeId.push(String(descDocs.shape.type[i].typeId)); 45 | } 46 | if (tagId.indexOf(String(descDocs.shape.type[i].children[j].tagId)) == -1) { 47 | tagId.push(String(descDocs.shape.type[i].children[j].tagId)); 48 | console.log(tagId) 49 | console.log(shapePath) 50 | console.log(typeId.join(',')) 51 | } 52 | 53 | await shape2.update({ 54 | path: shapePath 55 | }, { 56 | tagId: tagId.join(','), 57 | typeId: typeId.join(',') 58 | }) 59 | } 60 | // console.log(`${id}`) 61 | } 62 | } 63 | } 64 | console.log('done') 65 | })(); 66 | -------------------------------------------------------------------------------- /backstage/src/store/edit/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | 3 | /** 4 | * 总页码 5 | * @return {Number} 6 | */ 7 | 8 | pageLength: state => state.phone.data.length, 9 | 10 | /** 11 | * 当前页码, 从0开始 12 | * @return {Number} 13 | */ 14 | 15 | currentPage: state => state.currentPage, 16 | 17 | /** 18 | * phone数据 19 | * @return {Object} 20 | */ 21 | 22 | phoneData: state => state.phone, 23 | 24 | /** 25 | * 当前页数据 26 | * @return {Object} 27 | */ 28 | 29 | currentPhone: (state, getters) => state.phone.data[getters.currentPage], 30 | 31 | /** 32 | * 当前页元素个数 33 | * @return {Object} 34 | */ 35 | 36 | curPageItemLen: state => state.phone.data[state.currentPage].data.length, 37 | 38 | /** 39 | * 当前被选中元素 40 | * @return {Object} 41 | */ 42 | 43 | curItem: (state, getters) => getters.currentPhone.data[state.curItemId] || false, 44 | 45 | /** 46 | * 是否有被选中元素 47 | * @return {Boolean} 48 | */ 49 | 50 | hasSelectedItems: state => (state.curItemId >= 0 || state.curItemIds.length > 0) ? true : false, 51 | 52 | /** 53 | * 是否是单选 54 | * @return {Boolean} 55 | */ 56 | 57 | hasSelectedOneItem: state => state.curItemId >= 0 ? true : false, 58 | 59 | /** 60 | * 是否是多选 61 | * @return {Boolean} 62 | */ 63 | 64 | hasSelectedMultiItems: state => state.curItemIds.length > 0 ? true : false, 65 | 66 | /** 67 | * 被选中元素的id(以数组形式返回,即使是单选) 68 | * @return {Array} 69 | */ 70 | 71 | selectedItemsForArray: state => state.curItemId >= 0 ? [state.curItemId] : state.curItemIds, 72 | 73 | /** 74 | * 被选中元素的id 75 | * @return {Number} 76 | */ 77 | 78 | curItemId: state => state.curItemId, 79 | 80 | /** 81 | * 被选中元素的id集合 82 | * @return {Array} 83 | */ 84 | 85 | curItemIds: state => state.curItemIds, 86 | 87 | /** 88 | * 当前页的cache数据 89 | * @return {Object} 90 | */ 91 | 92 | curCache: state => state.cacheData[state.currentPage], 93 | 94 | /** 95 | * 其他信息 96 | * @return {Object} 97 | */ 98 | 99 | otherInfo: state => state.otherInfo, 100 | }; 101 | 102 | export default getters; 103 | -------------------------------------------------------------------------------- /server/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('myh5-b:server'); 9 | var http = require('http'); 10 | const db = require('../db/index.js') 11 | require('../api') 12 | /** 13 | * Get port from environment and store in Express. 14 | */ 15 | 16 | var port = normalizePort(process.env.PORT || '3000'); 17 | app.set('port', port); 18 | 19 | /** 20 | * Create HTTP server. 21 | */ 22 | 23 | var server = http.createServer(app); 24 | 25 | /** 26 | * Listen on provided port, on all network interfaces. 27 | */ 28 | 29 | 30 | db.on('error', console.error.bind(console, '连接数据库失败')) 31 | db.once('open', function () { 32 | // console.log('连接数据库成功') 33 | // let myh5 = dbHandel.getModel('myh5'); 34 | // myh5.find({}, (err, docs) => { 35 | // console.log(docs) 36 | // }); 37 | server.listen(port); 38 | server.on('error', onError); 39 | server.on('listening', onListening); 40 | }); 41 | 42 | 43 | /** 44 | * Normalize a port into a number, string, or false. 45 | */ 46 | 47 | function normalizePort(val) { 48 | var port = parseInt(val, 10); 49 | 50 | if (isNaN(port)) { 51 | // named pipe 52 | return val; 53 | } 54 | 55 | if (port >= 0) { 56 | // port number 57 | return port; 58 | } 59 | 60 | return false; 61 | } 62 | 63 | /** 64 | * Event listener for HTTP server "error" event. 65 | */ 66 | 67 | function onError(error) { 68 | if (error.syscall !== 'listen') { 69 | throw error; 70 | } 71 | 72 | var bind = typeof port === 'string' ? 73 | 'Pipe ' + port : 74 | 'Port ' + port; 75 | 76 | // handle specific listen errors with friendly messages 77 | switch (error.code) { 78 | case 'EACCES': 79 | console.error(bind + ' requires elevated privileges'); 80 | process.exit(1); 81 | break; 82 | case 'EADDRINUSE': 83 | console.error(bind + ' is already in use'); 84 | process.exit(1); 85 | break; 86 | default: 87 | throw error; 88 | } 89 | } 90 | 91 | /** 92 | * Event listener for HTTP server "listening" event. 93 | */ 94 | 95 | function onListening() { 96 | var addr = server.address(); 97 | var bind = typeof addr === 'string' ? 98 | 'pipe ' + addr : 99 | 'port ' + addr.port; 100 | debug('Listening on ' + bind); 101 | } 102 | -------------------------------------------------------------------------------- /server/upload/index.js: -------------------------------------------------------------------------------- 1 | // // var qiniu = require("qiniu") 2 | // const dbHandel = require('../db/handel.js') 3 | 4 | // // ; 5 | // // (async (res) => { 6 | // // let collection = dbHandel.getModel('shape') 7 | // // let docs = await collection.find({ 8 | // // path : '8fbd0751-b8d3-4cc3-8798-a68673437654.svg' 9 | // // }) 10 | // // console.log(docs) 11 | // // })(); 12 | 13 | 14 | 15 | // const glob 16 | 17 | 18 | const { 19 | AccessKey, 20 | SecretKey 21 | } = require('../../.key.js') 22 | var mac = new qiniu.auth.digest.Mac(AccessKey, SecretKey); 23 | 24 | var options = { 25 | scope: 'store', 26 | }; 27 | var putPolicy = new qiniu.rs.PutPolicy(options); 28 | var uploadToken = putPolicy.uploadToken(mac); 29 | 30 | 31 | var config = new qiniu.conf.Config(); 32 | // 空间对应的机房 33 | config.zone = qiniu.zone.Zone_z2; 34 | 35 | let glob = require('glob') 36 | // let b = glob.sync(`/Users/BraisedCakes/Desktop/2018/myh5-store/svg/*.svg`) 37 | // console.log(b.length) 38 | // return; 39 | var formUploader = new qiniu.form_up.FormUploader(config); 40 | var putExtra = new qiniu.form_up.PutExtra(); 41 | 42 | 43 | 44 | var localFile = "/Users/BraisedCakes/Desktop/2018/myh5-store/svg/yq0KZVbqS6mAaReOAABLqGlUGoM448.svg"; 45 | var key = 'yq0KX1bg6PCAalnlAAAK3WnoBek138.svg'; 46 | // 文件上传 47 | 48 | function upload(localFile, key) { 49 | return new Promise((resolve) => { 50 | formUploader.putFile(uploadToken, key, localFile, putExtra, function (respErr, 51 | respBody, respInfo) { 52 | if (respErr) { 53 | throw respErr; 54 | } 55 | if (respInfo.statusCode == 200) { 56 | console.log(respBody); 57 | } else { 58 | console.log(respInfo.statusCode); 59 | console.log(respBody); 60 | } 61 | resolve(true) 62 | }); 63 | }) 64 | } 65 | 66 | (async (req, res) => { 67 | let collection = dbHandel.getModel('shape') 68 | let data = await collection.find({}) 69 | let count = await collection.count({}) 70 | for (let i = 0; i < data.length; i++) { 71 | console.log(`共${count}个, 已经处理${i}个`); 72 | let one = await collection.findOne({ 73 | path: data[i].path, 74 | space: 'qiniu' 75 | }) 76 | if (one) { 77 | continue; 78 | } 79 | let localFile = `/Users/BraisedCakes/Desktop/2018/myh5-store/svg/${data[i].path}`; 80 | let key = data[i].path; 81 | await upload(localFile, key) 82 | await collection.update({ 83 | path: key 84 | }, { 85 | space: 'qiniu' 86 | }) 87 | } 88 | })() 89 | -------------------------------------------------------------------------------- /backstage/src/page/list/feed.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 68 | 69 | 93 | -------------------------------------------------------------------------------- /backstage/src/directive/drag.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import $ from 'jquery' 3 | import store from '@/store/index.js' 4 | 5 | Vue.directive('my-drag', { 6 | bind: function (el) { 7 | $(el).on('mousedown', function (ev) { 8 | if ($(ev.target).hasClass('circle')) { 9 | return; 10 | } 11 | let isMove = false; 12 | let old = []; 13 | let pos = []; 14 | for (let i = 0; i < store.getters.selectedItemsForArray.length; i++) { 15 | let item = store.getters.selectedItemsForArray[i]; 16 | old.push({ 17 | 18 | x: parseInt($('#' + store.getters.currentPhone.data[item].id).css('left')), 19 | y: parseInt($('#' + store.getters.currentPhone.data[item].id).css('top')) 20 | }) 21 | pos.push({ 22 | x: 0, 23 | y: 0 24 | }) 25 | } 26 | let downLeft = ev.clientX; 27 | let downTop = ev.clientY; 28 | $(document).on('mousemove', function (ev) { 29 | isMove = true; 30 | for (let i = 0; i < store.getters.selectedItemsForArray.length; i++) { 31 | let item = store.getters.selectedItemsForArray[i]; 32 | pos[i].x = ev.clientX - downLeft + old[i].x; 33 | pos[i].y = ev.clientY - downTop + old[i].y; 34 | $('.phone-area .phone-item').eq(item).css({ 35 | 'left': pos[i].x + 'px', 36 | 'top': pos[i].y + 'px' 37 | }) 38 | } 39 | return false; 40 | }); 41 | $(document).on('mouseup', function () { 42 | $(document).off('mousemove'); 43 | $(document).off('mouseup'); 44 | if (isMove) { 45 | for (let i = 0; i < store.getters.selectedItemsForArray.length; i++) { 46 | let item = store.getters.selectedItemsForArray[i]; 47 | store.dispatch('updateItem', { 48 | key: 'style', 49 | item: store.getters.currentPhone.data[item], 50 | val: { 51 | left: pos[i].x + 'px', 52 | top: pos[i].y + 'px' 53 | } 54 | }); 55 | } 56 | } 57 | }) 58 | }) 59 | } 60 | }) 61 | -------------------------------------------------------------------------------- /backstage/src/page/list/create.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 73 | 74 | 76 | -------------------------------------------------------------------------------- /h5/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | web 9 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | 48 |
49 |
50 |
欢迎制作易企秀
51 |
52 | 62 | 77 |
78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /backstage/src/api/index.js: -------------------------------------------------------------------------------- 1 | import fetch from './fetch' 2 | 3 | /** 4 | * 获取场景列表 5 | */ 6 | 7 | export const getSceneList = data => fetch('/aj/scene/list', data); 8 | 9 | /** 10 | * 创建场景 11 | */ 12 | 13 | export const addScene = data => fetch('/aj/scene/add', data, 'POST'); 14 | 15 | /** 16 | * 删除场景 17 | */ 18 | 19 | export const delScene = data => fetch('/aj/scene/del', data); 20 | 21 | /** 22 | * 发布场景 23 | */ 24 | 25 | export const publishScene = data => fetch('/aj/scene/publish', data, 'POST'); 26 | 27 | /** 28 | * 更新场景 29 | */ 30 | 31 | export const updateScene = data => fetch('/aj/scene/update', data, 'POST'); 32 | 33 | /** 34 | * 获取指定场景 35 | */ 36 | 37 | export const getScene = data => fetch('/aj/scene/get', data); 38 | 39 | /** 40 | * 保存场景 41 | */ 42 | 43 | export const saveScene = data => fetch('/aj/scene/save', data, 'POST'); 44 | 45 | /** 46 | * 获取形状nav 47 | */ 48 | 49 | export const getShapeNav = () => fetch('/aj/shape/nav'); 50 | 51 | /** 52 | * 获取形状list 53 | */ 54 | 55 | export const getShape = data => fetch('/aj/shape/get', data); 56 | 57 | /** 58 | * 获取指定形状的content 59 | */ 60 | export const getShapeContent = data => fetch('/aj/shape/getContent', data); 61 | 62 | /** 63 | * 获取音乐nav 64 | */ 65 | 66 | export const getMusicNav = () => fetch('/aj/music/nav'); 67 | 68 | /** 69 | * 获取音乐list 70 | */ 71 | 72 | export const getMusic = data => fetch('/aj/music/get', data); 73 | 74 | /** 75 | * 选择音乐 76 | */ 77 | 78 | export const choiceMusic = data => fetch('/aj/music/choice', data); 79 | 80 | /** 81 | * 获取图片nav 82 | */ 83 | 84 | export const getImageNav = () => fetch('/aj/image/nav'); 85 | 86 | /** 87 | * 获取图片list 88 | */ 89 | 90 | export const getImage = data => fetch('/aj/image/get', data); 91 | 92 | /** 93 | * 选择图片 94 | */ 95 | 96 | export const choiceImage = data => fetch('/aj/image/choice', data); 97 | 98 | /** 99 | * 创建二维码 100 | */ 101 | 102 | export const createQRCode = data => fetch('/aj/qrcode/create', data); 103 | 104 | /** 105 | * 用户注册 106 | */ 107 | 108 | export const userRegister = data => fetch('/aj/user/register', data, 'POST'); 109 | 110 | /** 111 | * 用户登录 112 | */ 113 | 114 | export const userLogin = data => fetch('/aj/user/login', data, 'POST'); 115 | 116 | /** 117 | * 用户登出 118 | */ 119 | 120 | export const userLogout = data => fetch('/aj/user/logout', data, 'POST'); 121 | 122 | /** 123 | * 获取用户信息 124 | */ 125 | 126 | export const getUserInfo = data => fetch('/aj/user/info', data); 127 | 128 | /** 129 | * 获取上传token 130 | */ 131 | 132 | export const getToken = data => fetch('/aj/upload/token', data); 133 | 134 | /** 135 | * 上传图片 136 | */ 137 | 138 | export const userUpload = data => fetch('/aj/image/user_upload', data, 'POST'); 139 | 140 | /** 141 | * 上传音乐 142 | */ 143 | 144 | export const userUploadMusic = data => fetch('/aj/music/user_upload', data, 'POST'); 145 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myh5", 3 | "version": "0.3.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vue-cli-service serve --open", 7 | "server": "node ./server/bin/www", 8 | "build": "vue-cli-service build", 9 | "start": "concurrently \"yarn dev\" \"yarn server\"", 10 | "lint": "vue-cli-service lint" 11 | }, 12 | "dependencies": { 13 | "animate.css": "^3.6.1", 14 | "art-template": "^4.12.2", 15 | "clipboard": "^2.0.1", 16 | "cookie-parser": "^1.4.3", 17 | "cropper": "^4.0.0", 18 | "element-ui": "^2.4.1", 19 | "express-art-template": "^1.0.1", 20 | "express-session": "^1.15.6", 21 | "jquery": "^3.3.1", 22 | "jquery.cookie": "^1.4.1", 23 | "js-cookie": "^2.2.0", 24 | "lodash": "^4.17.10", 25 | "md5": "^2.2.1", 26 | "moment": "^2.22.1", 27 | "mongoose": "^5.0.14", 28 | "morgan": "^1.9.0", 29 | "particles.js": "^2.0.0", 30 | "qaep-push": "^1.0.3", 31 | "qrcode": "^1.2.0", 32 | "serve-favicon": "^2.5.0", 33 | "sha1": "^1.1.1", 34 | "superagent": "^3.8.2", 35 | "swiper": "^4.3.0", 36 | "uuid": "^3.2.1", 37 | "vue": "^2.5.13", 38 | "vue-resource": "^1.5.0", 39 | "vue-router": "^3.0.1", 40 | "vuedraggable": "^2.16.0", 41 | "vuex": "^3.0.1" 42 | }, 43 | "devDependencies": { 44 | "@vue/cli-plugin-babel": "^3.0.0-beta.6", 45 | "@vue/cli-plugin-eslint": "^3.0.0-beta.6", 46 | "@vue/cli-service": "^3.0.0-beta.6", 47 | "art-template-loader": "^1.4.3", 48 | "babel-plugin-component": "^1.1.0", 49 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 50 | "connect-history-api-fallback": "^1.5.0", 51 | "node-sass": "^4.8.3", 52 | "qiniu": "^7.1.3", 53 | "rimraf": "^2.6.2", 54 | "sass-loader": "^6.0.7", 55 | "vue-template-compiler": "^2.5.13" 56 | }, 57 | "babel": { 58 | "presets": [ 59 | "@vue/app" 60 | ], 61 | "plugins": [ 62 | [ 63 | "component", 64 | { 65 | "libraryName": "element-ui", 66 | "styleLibraryName": "theme-chalk" 67 | } 68 | ] 69 | ] 70 | }, 71 | "eslintConfig": { 72 | "root": true, 73 | "env": { 74 | "node": true 75 | }, 76 | "extends": [ 77 | "plugin:vue/essential", 78 | "eslint:recommended" 79 | ], 80 | "rules": { 81 | "no-console": "off" 82 | }, 83 | "parserOptions": { 84 | "parser": "babel-eslint" 85 | } 86 | }, 87 | "postcss": { 88 | "plugins": { 89 | "autoprefixer": {} 90 | } 91 | }, 92 | "browserslist": [ 93 | "> 1%", 94 | "last 2 versions", 95 | "not ie <= 8" 96 | ] 97 | } 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myh5", 3 | "version": "0.3.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vue-cli-service serve --open", 7 | "server": "node ./server/bin/www", 8 | "build": "vue-cli-service build", 9 | "start": "concurrently \"yarn dev\" \"yarn server\"", 10 | "lint": "vue-cli-service lint" 11 | }, 12 | "dependencies": { 13 | "animate.css": "^3.6.1", 14 | "art-template": "^4.12.2", 15 | "clipboard": "^2.0.1", 16 | "cookie-parser": "^1.4.3", 17 | "cropper": "^4.0.0", 18 | "element-ui": "^2.4.1", 19 | "express-art-template": "^1.0.1", 20 | "express-session": "^1.15.6", 21 | "jade": "^1.11.0", 22 | "jquery": "^3.3.1", 23 | "jquery.cookie": "^1.4.1", 24 | "js-cookie": "^2.2.0", 25 | "lodash": "^4.17.10", 26 | "md5": "^2.2.1", 27 | "moment": "^2.22.1", 28 | "mongoose": "^5.0.14", 29 | "morgan": "^1.9.0", 30 | "particles.js": "^2.0.0", 31 | "qaep-push": "^1.0.3", 32 | "qrcode": "^1.2.0", 33 | "serve-favicon": "^2.5.0", 34 | "sha1": "^1.1.1", 35 | "superagent": "^3.8.2", 36 | "swiper": "^4.3.0", 37 | "uuid": "^3.2.1", 38 | "vue": "^2.5.13", 39 | "vue-resource": "^1.5.0", 40 | "vue-router": "^3.0.1", 41 | "vuedraggable": "^2.16.0", 42 | "vuex": "^3.0.1" 43 | }, 44 | "devDependencies": { 45 | "@vue/cli-plugin-babel": "^3.0.0-beta.6", 46 | "@vue/cli-plugin-eslint": "^3.0.0-beta.6", 47 | "@vue/cli-service": "^3.0.0-beta.6", 48 | "art-template-loader": "^1.4.3", 49 | "babel-plugin-component": "^1.1.0", 50 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 51 | "connect-history-api-fallback": "^1.5.0", 52 | "node-sass": "^4.8.3", 53 | "qiniu": "^7.1.3", 54 | "rimraf": "^2.6.2", 55 | "sass-loader": "^6.0.7", 56 | "vue-template-compiler": "^2.5.13" 57 | }, 58 | "babel": { 59 | "presets": [ 60 | "@vue/app" 61 | ], 62 | "plugins": [ 63 | [ 64 | "component", 65 | { 66 | "libraryName": "element-ui", 67 | "styleLibraryName": "theme-chalk" 68 | } 69 | ] 70 | ] 71 | }, 72 | "eslintConfig": { 73 | "root": true, 74 | "env": { 75 | "node": true 76 | }, 77 | "extends": [ 78 | "plugin:vue/essential", 79 | "eslint:recommended" 80 | ], 81 | "rules": { 82 | "no-console": "off" 83 | }, 84 | "parserOptions": { 85 | "parser": "babel-eslint" 86 | } 87 | }, 88 | "postcss": { 89 | "plugins": { 90 | "autoprefixer": {} 91 | } 92 | }, 93 | "browserslist": [ 94 | "> 1%", 95 | "last 2 versions", 96 | "not ie <= 8" 97 | ] 98 | } 99 | -------------------------------------------------------------------------------- /backstage/src/directive/select.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import $ from 'jquery' 3 | import store from '../store/index.js' 4 | 5 | Vue.directive('my-select', { 6 | inserted() { 7 | $('body').append($('
')) 8 | $('#j-choice-box').css({ 9 | position: 'absolute', 10 | left: 0, 11 | top: 0, 12 | width: 0, 13 | height: 0, 14 | border: '1px solid #000', 15 | display: 'none', 16 | 'z-index': 9999, 17 | background: 'rgba(0,0,0,0.3)' 18 | }) 19 | }, 20 | bind: function (el) { 21 | $(el).on('mousedown', (ev) => { 22 | let arr = store.getters.currentPhone.data; 23 | let oldX = ev.clientX; 24 | let oldY = ev.clientY; 25 | let x, y, w, h; 26 | $('#j-choice-box').show(); 27 | const { 28 | left: phoneLeft, 29 | top: phoneTop 30 | } = $('.workspace').offset(); 31 | $(document).on('mousemove', (ev) => { 32 | let nowX = ev.clientX; 33 | let nowY = ev.clientY; 34 | if (nowX > oldX) { 35 | x = oldX; 36 | w = nowX - oldX; 37 | } else { 38 | x = nowX; 39 | w = oldX - nowX; 40 | } 41 | if (nowY > oldY) { 42 | y = oldY; 43 | h = nowY - oldY; 44 | } else { 45 | y = nowY; 46 | h = oldY - nowY; 47 | } 48 | $('#j-choice-box').css({ 49 | left: x + 'px', 50 | top: y + 'px', 51 | width: w + 'px', 52 | height: h + 'px', 53 | }); 54 | var list = []; 55 | arr.forEach((item, index) => { 56 | var { 57 | left, 58 | top, 59 | width, 60 | height 61 | } = item.style; 62 | left = $('#' + item.id).offset().left - phoneLeft; 63 | top = $('#' + item.id).offset().top - phoneTop; 64 | width = parseFloat(width); 65 | height = parseFloat(height); 66 | 67 | if (phoneLeft + left > x + w || phoneLeft + left + width < x || phoneTop + top > y + h || phoneTop + top + height < y) { 68 | // console.log(';') 69 | } else { 70 | list.push(index); 71 | } 72 | }) 73 | store.dispatch('selectItem', list) 74 | }) 75 | $(document).on('mouseup', () => { 76 | $(document).off('mousemove'); 77 | $(document).off('mouseup'); 78 | $('#j-choice-box').css({ 79 | 'display': 'none', 80 | 'width': 0, 81 | 'height': 0, 82 | 'left': 0, 83 | 'top': 0 84 | }) 85 | }) 86 | }); 87 | } 88 | }) 89 | -------------------------------------------------------------------------------- /backstage/src/img/bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /backstage/src/components/shape/main.vue: -------------------------------------------------------------------------------- 1 | 2 | 24 | 89 | 112 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | let session = require('express-session') 6 | var cookieParser = require('cookie-parser'); 7 | var bodyParser = require('body-parser'); 8 | let url = require('url') 9 | var history = require('connect-history-api-fallback'); 10 | const { 11 | AJ_STATUS, 12 | AJ_MESSAGE 13 | } = require('./const/index') 14 | // var index = require('./routes/index'); 15 | // var users = require('./routes/users'); 16 | const types = require('./api/types') 17 | let dbHandel = require('./db/handel.js') 18 | 19 | 20 | var app = express(); 21 | // app.use(history({ 22 | // index: '/Users/BraisedCakes/Desktop/2018/myh5-project/backstage/public/index.html' 23 | // })); 24 | // view engine setup 25 | // app.set('views', path.resolve(process.cwd(), 'server/views')); 26 | // app.set('view engine', 'jade'); 27 | app.engine('art', require('express-art-template')); 28 | var runtime = require('art-template/lib/runtime'); 29 | runtime.parseStyle = function (date, format) { 30 | let str = '' 31 | for (let attr in date) { 32 | str += attr + ':' + date[attr] + ';' 33 | } 34 | return str 35 | }; 36 | app.set('view options', { 37 | debug: process.env.NODE_ENV !== 'production' 38 | }); 39 | // uncomment after placing your favicon in /public 40 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 41 | app.use(logger('dev')); 42 | app.use(bodyParser.json({ 43 | limit: '50mb' 44 | })); 45 | app.use(bodyParser.urlencoded({ 46 | extended: false 47 | })); 48 | app.use(cookieParser()); 49 | app.use(session({ 50 | secret: 'who am i ?', 51 | cookie: { 52 | maxAge: 1000 * 60 * 60 * 24 * 7 53 | }, 54 | saveUninitialized: true, 55 | resave: true 56 | })) 57 | 58 | app.use('/store', express.static('/Users/BraisedCakes/Desktop/2018/myh5-store/svg')); 59 | 60 | app.get('/show', function (req, res) { 61 | let myh5 = dbHandel.getModel('myh5') 62 | let id = url.parse(req.url, true).query['id'] 63 | console.log(id) 64 | myh5.findOne({ 65 | id: id 66 | }, (err, docs) => { 67 | res.render(path.resolve(process.cwd(), 'web/public/index.art'), { 68 | info: docs.data 69 | }); 70 | }); 71 | }); 72 | 73 | app.all('/aj/*', function (req, res, next) { 74 | // res.header('Access-Control-Allow-Origin', 'http://localhost:8082'); 75 | // res.header('Access-Control-Allow-Headers', 'Content-Type'); 76 | // res.header('Access-Control-Allow-Credentials', 'true'); 77 | if (req.session.username || req.path == '/app.js' || req.path == types.getPublishScene || req.path == types.getUserInfo || req.path == types.userLogin || req.path == types.userRegister) { 78 | next() 79 | } else { 80 | res.send({ 81 | status: AJ_STATUS.notlogin, 82 | data: {}, 83 | message: AJ_MESSAGE.error 84 | }) 85 | } 86 | // next(); 87 | }); 88 | 89 | app.use(function (err, req, res, next) { 90 | // set locals, only providing error in development 91 | res.locals.message = err.message; 92 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 93 | 94 | // render the error page 95 | res.status(err.status || 500); 96 | res.render('error'); 97 | // res.end(JSON.stringify(req.body, null, 2)) 98 | }); 99 | 100 | 101 | module.exports = app; 102 | -------------------------------------------------------------------------------- /backstage/src/page/edit/page-dom.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 62 | 112 | -------------------------------------------------------------------------------- /backstage/src/css/index.css: -------------------------------------------------------------------------------- 1 | ul { 2 | list-style: none; 3 | } 4 | 5 | a { 6 | text-decoration: none; 7 | cursor: pointer; 8 | } 9 | 10 | .icon { 11 | width: 1em; 12 | height: 1em; 13 | vertical-align: -0.15em; 14 | fill: currentColor; 15 | overflow: hidden; 16 | } 17 | 18 | em, 19 | i { 20 | font-style: normal; 21 | font-weight: 400; 22 | } 23 | 24 | html { 25 | min-height: 100vh; 26 | } 27 | 28 | body { 29 | /* -webkit-font-smoothing: antialiased; 30 | -moz-osx-font-smoothing: grayscale; */ 31 | font-size: 12px; 32 | user-select: none; 33 | /* overflow: hidden; */ 34 | min-height: 100vh; 35 | zoom: 1; 36 | color: #76838f; 37 | min-width: 1200px; 38 | } 39 | 40 | body>div { 41 | /* height: 100%; */ 42 | } 43 | 44 | #app { 45 | min-height: 100vh; 46 | width: 100%; 47 | position: relative; 48 | left: 0; 49 | top: 0; 50 | } 51 | 52 | .el-scrollbar__view { 53 | height: 100%; 54 | } 55 | 56 | .el-scrollbar__bar { 57 | z-index: 999999 !important; 58 | } 59 | 60 | .drag-handle { 61 | cursor: move; 62 | cursor: -webkit-grabbing; 63 | } 64 | 65 | div { 66 | border-width: 0; 67 | } 68 | 69 | @keyframes fadeIn { 70 | 0% { 71 | opacity: 0 72 | } 73 | to { 74 | opacity: 1 75 | } 76 | } 77 | 78 | @keyframes fadeInLeft { 79 | 0% { 80 | opacity: 0; 81 | -webkit-transform: translate3d(-200px, 0, 0); 82 | transform: translate3d(-200px, 0, 0) 83 | } 84 | to { 85 | opacity: 1; 86 | -webkit-transform: none; 87 | transform: none 88 | } 89 | } 90 | 91 | @keyframes opacityFadeInLeft { 92 | 0% { 93 | opacity: 1; 94 | -webkit-transform: translate3d(-200px, 0, 0); 95 | transform: translate3d(-200px, 0, 0) 96 | } 97 | to { 98 | opacity: 1; 99 | -webkit-transform: none; 100 | transform: none 101 | } 102 | } 103 | 104 | .pause { 105 | animation-play-state: paused; 106 | } 107 | 108 | @-webkit-keyframes fade-scale-02 { 109 | 0% { 110 | opacity: 0; 111 | -webkit-transform: scale(.2); 112 | transform: scale(.2) 113 | } 114 | to { 115 | opacity: 1; 116 | -webkit-transform: scale(1); 117 | transform: scale(1) 118 | } 119 | } 120 | 121 | @keyframes fade-scale-02 { 122 | 0% { 123 | opacity: 0; 124 | -webkit-transform: scale(.2); 125 | -o-transform: scale(.2); 126 | transform: scale(.2) 127 | } 128 | to { 129 | opacity: 1; 130 | -webkit-transform: scale(1); 131 | -o-transform: scale(1); 132 | transform: scale(1) 133 | } 134 | } 135 | 136 | @keyframes zoomIn { 137 | 0% { 138 | opacity: 0; 139 | -webkit-transform: scale3d(0.3, 0.3, 0.3); 140 | transform: scale3d(0.3, 0.3, 0.3); 141 | } 142 | 50% { 143 | opacity: 1; 144 | } 145 | } 146 | 147 | @keyframes rotating { 148 | 0% { 149 | transform: rotate(0deg) 150 | } 151 | to { 152 | transform: rotate(1turn) 153 | } 154 | } 155 | 156 | .rotate { 157 | animation: rotating 1.2s linear infinite; 158 | } 159 | 160 | #wrapAll { 161 | width: 318px; 162 | height: 486px; 163 | overflow: hidden; 164 | position: absolute; 165 | left: 50%; 166 | top: 82px; 167 | margin-left: -159px; 168 | } 169 | 170 | .swiper-container { 171 | width: 100%; 172 | height: 100%; 173 | position: relative; 174 | } 175 | 176 | .swiper-container .swiper-wrapper { 177 | width: 100%; 178 | height: 100%; 179 | position: absolute; 180 | } 181 | 182 | .swiper-slide { 183 | overflow: hidden; 184 | } 185 | -------------------------------------------------------------------------------- /backstage/src/img/notfound.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /h5/public/index.art: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | web 9 | 40 | 84 | 85 | 86 | 87 | 88 | 91 |
92 |
93 | 94 |
95 |
96 |
欢迎制作易企秀
97 |
98 |
99 |
100 | {{ each info.data }} 101 |
102 |
103 | {{each $value.data}} 104 |
105 | {{@ $value.content }} 106 |
107 | {{/each}} 108 |
109 |
110 | {{/each}} 111 |
112 |
113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /server/views/index.art: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | web 9 | 40 | 84 | 85 | 86 | 87 | 88 | 91 |
92 |
93 | 94 |
95 |
96 |
欢迎制作易企秀
97 |
98 |
99 |
100 | {{ each info.data }} 101 |
102 |
103 | {{each $value.data}} 104 |
105 | {{@ $value.content }} 106 |
107 | {{/each}} 108 |
109 |
110 | {{/each}} 111 |
112 |
113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /backstage/src/store/edit/action/page.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import $ from 'jquery' 3 | import * as types from '../mutation-types.js' 4 | import app from '@/main' 5 | import * as utils from '@/utils' 6 | export default { 7 | /** 8 | * 复制指定页 9 | * @param from 被复制的页码 10 | * @param to 复制到哪页 11 | */ 12 | async copyPage({ 13 | commit, 14 | state, 15 | dispatch, 16 | getters 17 | }, { 18 | from = getters.currentPage, 19 | to = getters.currentPage + 1 20 | } = {}) { 21 | if (from < 0 || from >= getters.phoneData.data.length) { 22 | return; 23 | } 24 | await dispatch('addPage', { 25 | go: false, 26 | record: false 27 | }); 28 | Vue.nextTick().then(async () => { 29 | let data = $.extend(true, {}, state.phone.data[from]); 30 | data.data.forEach((item) => { 31 | commit(types.ADD_CREATED_ID); 32 | item.id = `item_${state.phone.main.createdDomId}` 33 | }); 34 | commit(types.CHANGE_DATA, { 35 | page: to, 36 | data: data 37 | }); 38 | await dispatch('selectPage', { 39 | page: to 40 | }); 41 | dispatch('record', { 42 | type: 'page', 43 | data: data 44 | }) 45 | }) 46 | }, 47 | /** 48 | * 改变活跃页 49 | * @param {Number} page 页码 50 | */ 51 | async selectPage({ 52 | commit, 53 | dispatch 54 | }, { 55 | page, 56 | ani = true 57 | }) { 58 | await dispatch('cancelSelect'); 59 | await commit(types.SELECT_PAGE, page); 60 | Vue.nextTick().then(() => { 61 | ani && utils.runCurPhoneAni(); 62 | }) 63 | }, 64 | /** 65 | * 增加一页 66 | * @param {Boolean} go 是否选中新页面 67 | * @param {Boolean} record 是否记录cache 68 | */ 69 | async addPage({ 70 | commit, 71 | dispatch, 72 | getters 73 | }, { 74 | go = true, 75 | record = true 76 | } = {}) { 77 | commit(types.ADD_PAGE, { 78 | index: getters.currentPage, 79 | phoneData: getters.phoneData 80 | }) 81 | go && await dispatch('selectPage', { 82 | page: getters.currentPage + 1 83 | }); 84 | record && dispatch('record', { 85 | type: 'page', 86 | data: getters.currentPhone 87 | }) 88 | }, 89 | /** 90 | * 排序 91 | */ 92 | async sortPage({ 93 | commit, 94 | state, 95 | getters, 96 | dispatch 97 | }, data) { 98 | await dispatch('selectPage', { 99 | page: data.futureIndex, 100 | ani: false 101 | }); 102 | let arr = []; 103 | console.log(data) 104 | data.value.forEach((item) => { 105 | for (let i = 0; i < getters.phoneData.data.length; i++) { 106 | if (getters.phoneData.data[i] == item) { 107 | arr.push(state.cacheData[i]) 108 | } 109 | } 110 | }) 111 | commit(types.CHANGE_DATA, { 112 | data: data.value 113 | }); 114 | dispatch('record', { 115 | type: 'init', 116 | data: arr 117 | }) 118 | }, 119 | /** 120 | * 删除指定页 121 | * @param {Number} page 页码 122 | */ 123 | async delPage({ 124 | commit, 125 | dispatch, 126 | getters 127 | }, page) { 128 | if (getters.pageLength > 1) { 129 | commit(types.DEL_PAGE, { 130 | phoneData: getters.phoneData, 131 | page: page 132 | }); 133 | if (getters.currentPage > getters.phoneData.data.length - 1) { 134 | await dispatch('selectPage', { 135 | page: getters.phoneData.data.length - 1 136 | }); 137 | } 138 | dispatch('record', { 139 | type: 'delPage', 140 | page: page 141 | }) 142 | } else { 143 | app.$alert('最少保留一页内容', { 144 | closeOnClickModal: true, 145 | callback: () => {} 146 | }); 147 | } 148 | }, 149 | } 150 | -------------------------------------------------------------------------------- /backstage/src/page/edit/qrcode.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | 80 | -------------------------------------------------------------------------------- /server/api/scene.js: -------------------------------------------------------------------------------- 1 | let router = require('../app.js') 2 | let utils = require('../utils') 3 | let api = require('./types') 4 | let sceneModel = require('../models/scene') 5 | let { 6 | AJ_STATUS, 7 | AJ_MESSAGE 8 | } = require('../const/index') 9 | 10 | // 获取指定场景 11 | router.get(api.getSceneData, async (req, res) => { 12 | let id = req.query.id 13 | try { 14 | let data = await sceneModel.getSceneById(id) 15 | if (!data) { 16 | throw new Error('场景不存在') 17 | } else { 18 | data.portrait = data.domain + data.portrait 19 | res.send({ status: AJ_STATUS.success, message: AJ_MESSAGE.success, result: data }) 20 | } 21 | } catch (e) { 22 | res.send({ status: AJ_STATUS.error, message: e.message }) 23 | } 24 | }) 25 | 26 | // 保存场景 27 | router.post(api.saveScene, async (req, res) => { 28 | let id = req.body.id 29 | try { 30 | let data = await sceneModel.getSceneById(id) 31 | if (!data) { 32 | throw new Error('场景不存在') 33 | } else { 34 | await sceneModel.updateSceneById(id, { 35 | data: req.body.data, 36 | updateTime: utils.getTime(), 37 | status: data.status != 0 ? 2 : 0 38 | }) 39 | res.send({ status: AJ_STATUS.success, message: AJ_MESSAGE.success }) 40 | } 41 | } catch (e) { 42 | res.send({ status: AJ_STATUS.error, message: e.message }) 43 | } 44 | }) 45 | 46 | // 更新场景信息 47 | router.post(api.updateScene, async (req, res) => { 48 | let id = req.body.id 49 | try { 50 | let data = await sceneModel.getSceneById(id) 51 | if (!data) { 52 | throw new Error('场景不存在') 53 | } else { 54 | let title = req.body.title || data.title 55 | let desc = req.body.desc || data.desc 56 | let portrait = req.body.portrait || data.portrait 57 | portrait = portrait.match(/.com\/(.+)/)[1] 58 | await sceneModel.updateSceneById(id, { 59 | title: title, 60 | desc: desc, 61 | portrait: portrait 62 | }) 63 | res.send({ status: AJ_STATUS.success, message: '更新成功' }) 64 | } 65 | } catch (e) { 66 | res.send({ status: AJ_STATUS.error, message: e.message }) 67 | } 68 | }) 69 | 70 | // 获取场景列表 71 | router.get('/aj/scene/list', async (req, res) => { 72 | // page,limit,uid,status 73 | let search = { 74 | uid: req.session.uid, 75 | status: { 76 | // 默认情况获取所有非回收站作品 77 | $in: req.query.status ? req.query.status.split(',') : [0, 1, 2] 78 | } 79 | } 80 | let page = Number(req.query.page) || 1 81 | let limit = Number(req.query.limit) || 10 82 | let [total, data] = await Promise.all([ 83 | sceneModel.getSceneCount(search), 84 | sceneModel.getSceneList(search, { page, limit }) 85 | ]) 86 | data.forEach((item) => { 87 | item.portrait = item.domain + item.portrait 88 | }) 89 | res.send({ 90 | status: AJ_STATUS.success, 91 | message: AJ_MESSAGE.success, 92 | result: { 93 | info: { 94 | page: page, 95 | limit: limit, 96 | total: total, 97 | }, 98 | data: data 99 | } 100 | }) 101 | }) 102 | 103 | // 添加一个场景 104 | router.post('/aj/scene/add', async (req, res) => { 105 | let title = req.body.title 106 | let desc = req.body.desc 107 | let uid = req.session.uid 108 | 109 | try { 110 | if (!title.length) { 111 | throw new Error('title不能为空') 112 | } 113 | let count = await sceneModel.getSceneCount() 114 | let copyItem = {} 115 | if (req.body.id) { 116 | copyItem = await sceneModel.getSceneById(req.body.id) 117 | } 118 | let post = { 119 | id: count + 1, 120 | title: title, 121 | desc: desc, 122 | uid: uid, 123 | portrait: copyItem.portrait, 124 | data: copyItem.data, 125 | createTime: utils.getTime(), 126 | updateTime: utils.getTime() 127 | } 128 | await sceneModel.addScene(post) 129 | res.send({ status: AJ_STATUS.success, message: AJ_MESSAGE.success }) 130 | } catch (e) { 131 | res.send({ status: AJ_STATUS.error, message: e.message }) 132 | return 133 | } 134 | }) 135 | 136 | // 删除一个场景 137 | router.get('/aj/scene/del', async (req, res, next) => { 138 | let id = req.query.id 139 | try { 140 | let data = await sceneModel.getSceneById(id) 141 | if (!data) { 142 | throw new Error('该场景不存在') 143 | } else { 144 | await sceneModel.delSceneById(id) 145 | res.send({ status: AJ_STATUS.success, message: '删除成功' }) 146 | } 147 | } catch (e) { 148 | res.send({ status: AJ_STATUS.error, message: e.message }) 149 | } 150 | }) 151 | 152 | // 发布一个场景 153 | router.post('/aj/scene/publish', async (req, res, next) => { 154 | let id = req.body.id 155 | try { 156 | let data = await sceneModel.getSceneById(id) 157 | if (!data) { 158 | throw new Error('该场景不存在') 159 | } else { 160 | await sceneModel.updateSceneById(id, { 161 | updateTime: utils.getTime(), 162 | status: 1, 163 | publishData: data.data 164 | }) 165 | res.send({ status: AJ_STATUS.success, message: '发布成功' }) 166 | } 167 | } catch (e) { 168 | res.send({ status: AJ_STATUS.error, message: e.message }) 169 | } 170 | }) -------------------------------------------------------------------------------- /backstage/src/tpl/index.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery' 2 | import store from '@/store/index.js' 3 | import * as types from './types.js' 4 | 5 | export default { 6 | /* 7 | 文本 8 | */ 9 | [types.TXT]: { 10 | create() { 11 | return { 12 | type: types.TXT, 13 | content: '空白文本', 14 | class: types.TXT.toLowerCase(), 15 | animation: [], 16 | style: { 17 | position: 'absolute', 18 | left: '0', 19 | top: '50px', 20 | color: '#666666', 21 | width: '200px', 22 | height: '100px', 23 | padding: '5px', 24 | 'border-style': 'solid' 25 | } 26 | } 27 | } 28 | }, 29 | /* 30 | 形状 31 | */ 32 | [types.SHAPE]: { 33 | 34 | create({ 35 | content, 36 | path 37 | }) { 38 | content = $(content); 39 | let width = Math.round(parseFloat(content.attr('width'))); 40 | let height = Math.round(parseFloat(content.attr('height'))); 41 | if (!content.attr('viewbox')) { 42 | content.attr('viewbox', `0 0 ${width} ${height}`); 43 | } 44 | content.attr('preserveAspectRatio', 'none'); 45 | if (width >= height) { 46 | let radio = 100 / height; 47 | width = radio * width; 48 | height = 100; 49 | } else { 50 | let radio = 100 / width; 51 | height = radio * width; 52 | width = 100; 53 | } 54 | width = Math.round(width); 55 | height = Math.round(height); 56 | content.attr('width', '100%'); 57 | content.attr('height', '100%'); 58 | content = content.prop('outerHTML'); 59 | return { 60 | type: types.SHAPE, 61 | content: content, 62 | class: types.SHAPE.toLowerCase(), 63 | animation: [], 64 | fill: {}, 65 | width: width, 66 | height: height, 67 | path: path, 68 | style: { 69 | position: 'absolute', 70 | left: '0', 71 | top: '50px', 72 | width: `${width}px`, 73 | height: `${height}px`, 74 | 'border-style': 'solid' 75 | } 76 | } 77 | }, 78 | afterCreate(itemTpl) { 79 | let arr = []; 80 | $(itemTpl.content) 81 | .find("*") 82 | .each((index, item) => { 83 | if ( 84 | $(item).attr("fill") && 85 | arr.every(item2 => { 86 | return item2.fill != $(item).attr("fill"); 87 | }) 88 | ) { 89 | arr.push({ 90 | fill: $(item).attr("fill"), 91 | css: $(item).css("fill") 92 | }); 93 | } 94 | }); 95 | for (let i = 0; i < arr.length; i++) { 96 | store.dispatch('updateItem', { 97 | key: "content", 98 | val: arr[i].css, 99 | fill: arr[i].fill 100 | }); 101 | } 102 | } 103 | }, 104 | /* 105 | 图片 106 | */ 107 | [types.IMAGE]: { 108 | create({ 109 | path, 110 | width, 111 | height 112 | }) { 113 | return { 114 | type: types.IMAGE, 115 | path: path, 116 | originPath: path, 117 | crop: {}, 118 | content: ``, 119 | class: types.IMAGE.toLowerCase(), 120 | animation: [], 121 | style: { 122 | position: 'absolute', 123 | left: '0', 124 | top: '50px', 125 | width: `${width}px`, 126 | height: `${height}px`, 127 | 'border-style': 'solid' 128 | } 129 | } 130 | } 131 | }, 132 | /* 133 | 二维码 134 | */ 135 | [types.QRCODE]: { 136 | create({ 137 | url 138 | }) { 139 | let width = 200; 140 | let height = 200; 141 | return { 142 | type: types.QRCODE, 143 | content: ``, 144 | class: types.IMAGE.toLowerCase(), 145 | animation: [], 146 | style: { 147 | position: 'absolute', 148 | left: '0', 149 | top: '50px', 150 | width: `${width}px`, 151 | height: `${height}px`, 152 | 'border-style': 'solid' 153 | } 154 | } 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /backstage/src/components/music/main.vue: -------------------------------------------------------------------------------- 1 | 42 | 151 | -------------------------------------------------------------------------------- /backstage/src/page/login/login.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 72 | 164 | -------------------------------------------------------------------------------- /backstage/src/components/image/main.vue: -------------------------------------------------------------------------------- 1 | 27 | 110 | 133 | -------------------------------------------------------------------------------- /backstage/src/components/background/main.vue: -------------------------------------------------------------------------------- 1 | 27 | 110 | 133 | -------------------------------------------------------------------------------- /server/upload/app.js: -------------------------------------------------------------------------------- 1 | // const superagent = require('superagent') 2 | // const fs = require('fs') 3 | // superagent.post('http://store.eqxiu.com/api/category/getCategoryListById?id=889875').set("Cookie", '_ga=GA1.2.1844039418.1523173010; gr_user_id=856b28d3-7797-4ad3-bf0a-e970d2d29855; _gid=GA1.2.1183794952.1523282172; _tracker_distinct_id_=5d3727a1-f0a8-457d-9fbe-47317c4011b7; _tracker_session_id_=6915a5f5-2f33-4892-afca-d78fa7b039f7; _tracker_user_id_=4a2d8af950851f490150ef39910e18f1; UM_distinctid=162be364ec91054-02381f8078fd39-33697b04-1fa400-162be364eca1bb; rememberMe=DLcTdZGZXkAzMHU5jjaPN1v2Z6yk2xDAZWR4mm5v8zCdN91USpyM8T0IGwdxQTgxjjFv43XARdAD/hDJO9G1jiIHpv8TowKQlY4E9+yh5+AVPew0MzTHiqFdAnUhElwhcAgjaecr6Lvi49oJhxc7hBg7Nj0yJc5a6wdZyBPVJCpReJ8NrDlzEpTiuzmh0n+/4nbSMEV8wshWBPOxXtMA6lON+OgitiGHFLNVc28x6oqn/yYYWrPA/VjqJU/IRkI5mtlB4Lvtba+ZblokqnQ5QGLOxTknUZ2/VyPH+2IXomiOx56gjSQ/GO8ZO4oenIBmp3kCFh0+wZqwDHkwcojLQxuPv1w9B96gqwFJUO3IU5NJpC2kf/2vCbb/a4TmpA31GmwCQpaX5XHxHaTKfO6bN20FEvTo0wy9Q/4mcSVJoihzySyOGAVYElXKqvqcYAQpyUT8XQ/FwL7STpcHxoHkGuP91fOVbXw0l7WbjtG6dADEZ3qV24TkNgqulqUdFi1tXk46dB54EEbToGoa33eZbwK1qtFRtujaZVY9088ImXQkrZV0K2dyJJkDfhTbyteFIG3MnZfjxBm4IwagzOE0uhR39bSRBQaZZGLJ2dnRgHhMowPDnLcGNWCHTBIISLw0Rjj7qY9lLHyfprfN3uet/iPvVoIHMzXxDf4l0w3m5Wp7cw/akurO4JTZMl+9iAuJY0gmvGHImXfz7W3XuEfNoP9nKPCtNVj4ewLE8FoDIjKlejDaUsR3R+2peNRJ4XGzAUysUU0JklxzlBfq3pKzWRtb4RGmFTz8+CtJQnTdocQ6V5ZC96BCSQ1gljLjzfG8JDxQ7Wj0KxUmMwC8+6L495Xg8iowmWyYdv7AHI8ippkAdkjeJ5oPsVxG4KdhD7gf2TACM5KgcezXx89tR1pMo0GDhJOWWO3PoEOCZPWbaLFEHd6vh5jyLDraUWi8KqW5MvB7pAuqqhe2Ftq2/qFwVU+xVCTpWYSUfAfNHDfGp2yomKcMckkdh5IJALDmRKvGiBSRfo/v2Q9GOGlb9PaT4gqudN2FWFWORhk0Y4Q3WW9EP++7sGOyIhjfBXf1amphc2BZG0YumdJIQoghz9FPcopQsa2HCtHF+kWuTDutVQOWAc5TcAApAMwakrYEeyyUqVFKJwZlVvu6rvpwA3+Ji3VvW2pDWsREHo5cYGLrb2KY5yffRfEUvANargC79qzamx4e5Zn3vJBsa5DjNrv46lWSEbOwj+edsYYUBHRi8GFFGA02MCpimqWls76witVUwHE5BLYA379ZP35HMr11wwmIgvIVvr76tRbMydVFKsoO5qbSbkfnD1YNtfjoqSB84OCwlrUkNU3TKXnhjnFvOg==; _tracker_launch_=1; JSESSIONID=87f8dac50f5443b3a643addcbc60dc84; gr_session_id_a17a66c1107ba80b=6aaada5d-7096-4d1a-82db-c5b19f30cf06; _gat=1').set({ 4 | // "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36", 5 | // 'Content-Type': 'application/x-www-form-urlencoded' 6 | // }).end(async function (err, res) { 7 | // // console.log(res.body.list) 8 | // let list = res.body.list; 9 | // for (let i = 0; i < list.length; i++) { 10 | // fs.appendFileSync(`/Users/BraisedCakes/Desktop/2018/myh5-store/musics/${list[i].name}.json`, ''); 11 | // // break; 12 | // for (let j = 0; j < 1000; j++) { 13 | // let data = await fn(j + 1, list[i].id); 14 | // if (!data || data.length == 0) { 15 | // break; 16 | // } else { 17 | // let originData = []; 18 | // if (fs.existsSync(`/Users/BraisedCakes/Desktop/2018/myh5-store/musics/${list[i].name}.json`)) { 19 | // let docs = fs.readFileSync(`/Users/BraisedCakes/Desktop/2018/myh5-store/musics/${list[i].name}.json`, 'utf-8'); 20 | // if (docs.trim() == '') { 21 | // docs = '[]' 22 | // } 23 | // originData = JSON.parse(docs); 24 | // } 25 | // originData.push(...data) 26 | 27 | // fs.writeFileSync(`/Users/BraisedCakes/Desktop/2018/myh5-store/musics/${list[i].name}.json`, JSON.stringify(originData, null, 4)); 28 | // console.log(`当前type = ${list[i].name}, page = ${j+1}`) 29 | // } 30 | // } 31 | // } 32 | // }); 33 | 34 | 35 | // function fn(page, id) { 36 | // return new Promise((resolve) => { 37 | // superagent.get(`http://store.eqxiu.com/api/product/cat/listProdByCate?attrGroupId=3&category=${id}&pageNo=${page}&pageSize=10`).set("Cookie", '_ga=GA1.2.1844039418.1523173010; gr_user_id=856b28d3-7797-4ad3-bf0a-e970d2d29855; _gid=GA1.2.1183794952.1523282172; _tracker_distinct_id_=5d3727a1-f0a8-457d-9fbe-47317c4011b7; _tracker_session_id_=6915a5f5-2f33-4892-afca-d78fa7b039f7; _tracker_user_id_=4a2d8af950851f490150ef39910e18f1; UM_distinctid=162be364ec91054-02381f8078fd39-33697b04-1fa400-162be364eca1bb; rememberMe=DLcTdZGZXkAzMHU5jjaPN1v2Z6yk2xDAZWR4mm5v8zCdN91USpyM8T0IGwdxQTgxjjFv43XARdAD/hDJO9G1jiIHpv8TowKQlY4E9+yh5+AVPew0MzTHiqFdAnUhElwhcAgjaecr6Lvi49oJhxc7hBg7Nj0yJc5a6wdZyBPVJCpReJ8NrDlzEpTiuzmh0n+/4nbSMEV8wshWBPOxXtMA6lON+OgitiGHFLNVc28x6oqn/yYYWrPA/VjqJU/IRkI5mtlB4Lvtba+ZblokqnQ5QGLOxTknUZ2/VyPH+2IXomiOx56gjSQ/GO8ZO4oenIBmp3kCFh0+wZqwDHkwcojLQxuPv1w9B96gqwFJUO3IU5NJpC2kf/2vCbb/a4TmpA31GmwCQpaX5XHxHaTKfO6bN20FEvTo0wy9Q/4mcSVJoihzySyOGAVYElXKqvqcYAQpyUT8XQ/FwL7STpcHxoHkGuP91fOVbXw0l7WbjtG6dADEZ3qV24TkNgqulqUdFi1tXk46dB54EEbToGoa33eZbwK1qtFRtujaZVY9088ImXQkrZV0K2dyJJkDfhTbyteFIG3MnZfjxBm4IwagzOE0uhR39bSRBQaZZGLJ2dnRgHhMowPDnLcGNWCHTBIISLw0Rjj7qY9lLHyfprfN3uet/iPvVoIHMzXxDf4l0w3m5Wp7cw/akurO4JTZMl+9iAuJY0gmvGHImXfz7W3XuEfNoP9nKPCtNVj4ewLE8FoDIjKlejDaUsR3R+2peNRJ4XGzAUysUU0JklxzlBfq3pKzWRtb4RGmFTz8+CtJQnTdocQ6V5ZC96BCSQ1gljLjzfG8JDxQ7Wj0KxUmMwC8+6L495Xg8iowmWyYdv7AHI8ippkAdkjeJ5oPsVxG4KdhD7gf2TACM5KgcezXx89tR1pMo0GDhJOWWO3PoEOCZPWbaLFEHd6vh5jyLDraUWi8KqW5MvB7pAuqqhe2Ftq2/qFwVU+xVCTpWYSUfAfNHDfGp2yomKcMckkdh5IJALDmRKvGiBSRfo/v2Q9GOGlb9PaT4gqudN2FWFWORhk0Y4Q3WW9EP++7sGOyIhjfBXf1amphc2BZG0YumdJIQoghz9FPcopQsa2HCtHF+kWuTDutVQOWAc5TcAApAMwakrYEeyyUqVFKJwZlVvu6rvpwA3+Ji3VvW2pDWsREHo5cYGLrb2KY5yffRfEUvANargC79qzamx4e5Zn3vJBsa5DjNrv46lWSEbOwj+edsYYUBHRi8GFFGA02MCpimqWls76witVUwHE5BLYA379ZP35HMr11wwmIgvIVvr76tRbMydVFKsoO5qbSbkfnD1YNtfjoqSB84OCwlrUkNU3TKXnhjnFvOg==; _tracker_launch_=1; JSESSIONID=87f8dac50f5443b3a643addcbc60dc84; gr_session_id_a17a66c1107ba80b=6aaada5d-7096-4d1a-82db-c5b19f30cf06; _gat=1').set({ 38 | // "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36", 39 | // 'Content-Type': 'application/x-www-form-urlencoded' 40 | // }).end(function (err, res) { 41 | // setTimeout(() => { 42 | // resolve(res.body.list) 43 | // }, 500) 44 | // }); 45 | // }) 46 | // } 47 | -------------------------------------------------------------------------------- /backstage/src/page/edit/tool.vue: -------------------------------------------------------------------------------- 1 | 12 | 157 | 158 | 179 | -------------------------------------------------------------------------------- /backstage/src/page/edit/page.vue: -------------------------------------------------------------------------------- 1 | 38 | 92 | -------------------------------------------------------------------------------- /backstage/src/page/edit/ani.vue: -------------------------------------------------------------------------------- 1 | 49 | 117 | 118 | 181 | -------------------------------------------------------------------------------- /backstage/src/page/edit/phone.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 72 | 73 | 173 | -------------------------------------------------------------------------------- /backstage/src/store/edit/modules/page.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import $ from 'jquery' 3 | import * as types from '../mutation-types.js' 4 | import * as constant from '@/constant' 5 | import app from '@/main' 6 | import * as utils from '@/utils' 7 | 8 | const page = { 9 | namespaced: true, 10 | state: { 11 | currentPage: 0, 12 | }, 13 | getters: { 14 | 15 | /** 16 | * 总页码 17 | * @return {Number} 18 | */ 19 | 20 | pageLength: (state, getters, rootState) => rootState.edit.phone.data.length, 21 | 22 | /** 23 | * 当前页码, 从0开始 24 | * @return {Number} 25 | */ 26 | 27 | currentPage: state => state.currentPage, 28 | }, 29 | actions: { 30 | /** 31 | * 复制指定页 32 | * @param from 被复制的页码 33 | * @param to 复制到哪页 34 | */ 35 | async copyPage({ 36 | commit, 37 | state, 38 | dispatch, 39 | getters 40 | }, { 41 | from = getters.currentPage, 42 | to = getters.currentPage + 1 43 | } = {}) { 44 | if (from < 0 || from >= getters.phoneData.data.length) { 45 | return; 46 | } 47 | await dispatch('addPage', { 48 | go: false, 49 | record: false 50 | }); 51 | Vue.nextTick().then(async () => { 52 | let data = $.extend(true, {}, state.phone.data[from]); 53 | data.data.forEach((item) => { 54 | commit(types.ADD_CREATED_ID); 55 | item.id = `item_${state.phone.main.createdDomId}` 56 | }); 57 | commit(types.CHANGE_DATA, { 58 | page: to, 59 | data: data 60 | }); 61 | await dispatch('selectPage', { 62 | page: to 63 | }); 64 | // dispatch('record', { 65 | // type: 'page', 66 | // data: data 67 | // }) 68 | }) 69 | }, 70 | /** 71 | * 改变活跃页 72 | * @param {Number} page 页码 73 | */ 74 | async selectPage({ 75 | commit, 76 | dispatch, 77 | getters 78 | }, { 79 | page, 80 | ani = true 81 | }) { 82 | /* 取消所有选中元素 */ 83 | await dispatch('edit/cancelSelect', null, { 84 | root: true 85 | }); 86 | /* 取消所有选中元素 */ 87 | await commit(types.SELECT_PAGE, { 88 | page: page 89 | }); 90 | /* 执行当前页动画 */ 91 | Vue.nextTick().then(() => { 92 | ani && utils.runCurPhoneAni(); 93 | }) 94 | }, 95 | /** 96 | * 增加一页 97 | * @param {Boolean} go 是否选中新页面 98 | * @param {Boolean} record 是否记录cache 99 | */ 100 | async addPage({ 101 | commit, 102 | dispatch, 103 | getters, 104 | rootGetters 105 | }, { 106 | go = true, 107 | record = true 108 | } = {}) { 109 | commit(types.ADD_PAGE, { 110 | index: getters.currentPage, 111 | phoneData: rootGetters['edit/phoneData'] 112 | }) 113 | go && await dispatch('selectPage', { 114 | page: getters.currentPage + 1 115 | }); 116 | // record && dispatch('record', { 117 | // type: 'page', 118 | // data: rootGetters.currentPhone 119 | // }) 120 | }, 121 | /** 122 | * 排序 123 | */ 124 | async sortPage({ 125 | commit, 126 | state, 127 | getters, 128 | dispatch 129 | }, data) { 130 | await dispatch('selectPage', { 131 | page: data.futureIndex, 132 | ani: false 133 | }); 134 | let arr = []; 135 | data.value.forEach((item) => { 136 | for (let i = 0; i < getters.phoneData.data.length; i++) { 137 | if (getters.phoneData.data[i] == item) { 138 | arr.push(state.cacheData[i]) 139 | } 140 | } 141 | }) 142 | commit(types.CHANGE_DATA, { 143 | data: data.value 144 | }); 145 | // dispatch('record', { 146 | // type: 'init', 147 | // data: arr 148 | // }) 149 | }, 150 | /** 151 | * 删除指定页 152 | * @param {Number} page 页码 153 | */ 154 | async delPage({ 155 | commit, 156 | dispatch, 157 | getters, 158 | rootGetters 159 | }, page) { 160 | if (getters.pageLength > 1) { 161 | commit(types.DEL_PAGE, { 162 | phoneData: rootGetters['edit/phoneData'], 163 | page: page 164 | }); 165 | if (getters.currentPage > rootGetters['edit/phoneData'].data.length - 1) { 166 | await dispatch('selectPage', { 167 | page: rootGetters['edit/phoneData'].data.length - 1 168 | }); 169 | } 170 | // dispatch('record', { 171 | // type: 'delPage', 172 | // page: page 173 | // }) 174 | } else { 175 | app.$alert('最少保留一页内容', { 176 | closeOnClickModal: true, 177 | callback: () => {} 178 | }); 179 | } 180 | }, 181 | }, 182 | mutations: { 183 | 184 | /** 185 | * 选择一页 186 | * @param {Number} page 页码 187 | */ 188 | 189 | [types.SELECT_PAGE](state, { 190 | page, 191 | }) { 192 | state.currentPage = page; 193 | }, 194 | 195 | /** 196 | * 添加一页 197 | * @param {Object} phoneData 数据列表 198 | * @param {Number} index 第index页后新增空白页 199 | */ 200 | 201 | [types.ADD_PAGE](state, { 202 | index, 203 | phoneData 204 | }) { 205 | phoneData.data.splice(index + 1, 0, $.extend(true, {}, constant.BASE_BLANK)); 206 | }, 207 | 208 | /** 209 | * 删除指定页 210 | * @param {Number} phoneData 数据列表 211 | * @param {Number} page 要删除的页码 212 | */ 213 | 214 | [types.DEL_PAGE](state, { 215 | phoneData, 216 | page 217 | }) { 218 | phoneData.data.splice(page, 1); 219 | }, 220 | }, 221 | }; 222 | 223 | export default page; 224 | -------------------------------------------------------------------------------- /server/db/model.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "myh5": { 3 | "id": { 4 | type: Number, 5 | required: true, 6 | description: '作品id' 7 | }, 8 | "title": { 9 | type: String, 10 | required: true, 11 | description: '作品title' 12 | }, 13 | 'domain': { 14 | type: String, 15 | required: false, 16 | default: 'http://p7d4z759a.bkt.clouddn.com/' 17 | }, 18 | "portrait": { 19 | type: String, 20 | required: false, 21 | description: '背景图', 22 | default: 'myh5_logo.png' 23 | }, 24 | "desc": { 25 | type: String, 26 | required: false, 27 | description: '作品描述' 28 | }, 29 | "createTime": { 30 | type: Number, 31 | required: true, 32 | description: '创建时间' 33 | }, 34 | "updateTime": { 35 | type: Number, 36 | required: true, 37 | description: '编辑时间' 38 | }, 39 | "data": { 40 | type: Object, 41 | required: false, 42 | default: {}, 43 | description: '作品数据' 44 | }, 45 | "publishData": { 46 | type: Object, 47 | required: false, 48 | default: {}, 49 | description: '发布时的数据' 50 | }, 51 | "status": { 52 | type: Number, 53 | default: 0, 54 | description: `状态 0:未发布,1:已发布且无修改,2:已发布且有修改, 3:回收站, 4:彻底删除` 55 | }, 56 | "uid": { 57 | type: Number, 58 | required: true, 59 | description: '创建者的uid' 60 | } 61 | }, 62 | "shape": { 63 | "path": { 64 | type: String, 65 | required: true 66 | }, 67 | 'domain': { 68 | type: String, 69 | required: false, 70 | default: 'http://p7d4z759a.bkt.clouddn.com/' 71 | }, 72 | "space": { 73 | type: String, 74 | required: false 75 | }, 76 | "content": { 77 | type: String, 78 | required: false 79 | }, 80 | "name": { 81 | type: String, 82 | required: false 83 | }, 84 | "id": { 85 | type: Number, 86 | required: true 87 | }, 88 | "typeId": { 89 | type: String, 90 | required: false 91 | }, 92 | "tagId": { 93 | type: String, 94 | required: false 95 | } 96 | }, 97 | "desc": { 98 | "id": { 99 | type: Number, 100 | required: true 101 | }, 102 | "shape": { 103 | type: Array, 104 | required: false 105 | }, 106 | "music": { 107 | type: Array, 108 | required: false 109 | }, 110 | "image": { 111 | type: Array, 112 | required: false 113 | } 114 | }, 115 | "pic_admin": { 116 | types: { 117 | type: Array, 118 | required: false, 119 | default: [] 120 | } 121 | }, 122 | 'user': { 123 | 'password': { 124 | type: String, 125 | required: true 126 | }, 127 | 'username': { 128 | type: String, 129 | required: true 130 | }, 131 | 'uid': { 132 | type: String, 133 | required: true 134 | } 135 | }, 136 | 'used_shapes': { 137 | 'uid': { 138 | type: Number, 139 | required: true 140 | }, 141 | 'shapeId': { 142 | type: Number, 143 | required: true 144 | }, 145 | 'usedTime': { 146 | type: Number, 147 | required: true 148 | } 149 | }, 150 | 'used_musics': { 151 | 'uid': { 152 | type: Number, 153 | required: true 154 | }, 155 | 'musicId': { 156 | type: Number, 157 | required: true 158 | }, 159 | 'usedTime': { 160 | type: Number, 161 | required: true 162 | } 163 | }, 164 | 'used_images': { 165 | 'uid': { 166 | type: Number, 167 | required: true 168 | }, 169 | 'imageId': { 170 | type: Number, 171 | required: true 172 | }, 173 | 'usedTime': { 174 | type: Number, 175 | required: true 176 | } 177 | }, 178 | 'images': { 179 | 'uid': { 180 | type: Number, 181 | required: false 182 | }, 183 | 'domain': { 184 | type: String, 185 | required: false, 186 | default: 'http://p7d4z759a.bkt.clouddn.com/' 187 | }, 188 | 'path': { 189 | type: String, 190 | required: true 191 | }, 192 | 'id': { 193 | type: Number, 194 | required: true 195 | }, 196 | 'createTime': { 197 | type: Number, 198 | required: true 199 | }, 200 | 'size': { 201 | type: Number, 202 | required: false 203 | }, 204 | 'name': { 205 | type: String, 206 | required: false 207 | }, 208 | 'typeId': { 209 | type: String, 210 | required: false 211 | }, 212 | 'width': { 213 | type: Number, 214 | required: false 215 | }, 216 | 'height': { 217 | type: Number, 218 | required: false 219 | }, 220 | 'isPublic': { 221 | type: Boolean, 222 | required: false, 223 | default: false 224 | } 225 | }, 226 | 'musics': { 227 | 'domain': { 228 | type: String, 229 | required: false, 230 | default: 'http://p7d4z759a.bkt.clouddn.com/' 231 | }, 232 | 'src': { 233 | type: String, 234 | required: false 235 | }, 236 | 'path': { 237 | type: String, 238 | required: true 239 | }, 240 | 'name': { 241 | type: String, 242 | required: false 243 | }, 244 | 'typeId': { 245 | type: String, 246 | required: false 247 | }, 248 | 'id': { 249 | type: Number, 250 | required: true 251 | }, 252 | 'createTime': { 253 | type: Number, 254 | required: true 255 | }, 256 | 'uid': { 257 | type: Number, 258 | required: false 259 | } 260 | } 261 | } 262 | --------------------------------------------------------------------------------