├── vue-form ├── static │ └── .gitkeep ├── build │ ├── logo.png │ ├── vue-loader.conf.js │ ├── build.js │ ├── check-versions.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ ├── utils.js │ └── webpack.prod.conf.js ├── config │ ├── prod.env.js │ ├── dev.env.js │ └── index.js ├── src │ ├── assets │ │ └── logo.png │ ├── common │ │ ├── images │ │ │ ├── logo.png │ │ │ ├── menu.png │ │ │ ├── home_bg.jpg │ │ │ ├── login_bg.jpg │ │ │ ├── logo_transparent.png │ │ │ └── form_model │ │ │ │ ├── model_1.jpg │ │ │ │ └── model_1_title.png │ │ ├── less │ │ │ ├── variable.less │ │ │ ├── base.less │ │ │ ├── index.less │ │ │ ├── reset.less │ │ │ └── customForm_h5.less │ │ └── js │ │ │ └── customForm_h5.js │ ├── components │ │ ├── form │ │ │ ├── success.vue │ │ │ ├── data_form.vue │ │ │ ├── PreviewForm.vue │ │ │ ├── view.vue │ │ │ ├── model_form.vue │ │ │ └── custom_form.vue │ │ ├── m-footer │ │ │ └── footer.vue │ │ ├── person │ │ │ ├── person_desc.vue │ │ │ ├── person_review.vue │ │ │ └── person.vue │ │ ├── home.vue │ │ ├── m-header │ │ │ └── header.vue │ │ ├── signup │ │ │ └── signup.vue │ │ ├── login │ │ │ └── login.vue │ │ ├── main.vue │ │ └── help │ │ │ └── help.vue │ ├── App.vue │ ├── main.js │ ├── api │ │ ├── user.js │ │ └── form.js │ └── router │ │ └── index.js ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── .babelrc ├── index.html └── package.json ├── .DS_Store ├── img ├── demo.png ├── drag.png ├── main.png ├── view.png └── login.png ├── vue-node ├── config │ └── db.js ├── router │ ├── index.js │ ├── movie.js │ └── form.js ├── models │ ├── form.js │ ├── models.js │ ├── form_data.js │ ├── user.js │ └── movie.js ├── package.json └── app.js └── readme.md /vue-form/static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/.DS_Store -------------------------------------------------------------------------------- /img/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/img/demo.png -------------------------------------------------------------------------------- /img/drag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/img/drag.png -------------------------------------------------------------------------------- /img/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/img/main.png -------------------------------------------------------------------------------- /img/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/img/view.png -------------------------------------------------------------------------------- /img/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/img/login.png -------------------------------------------------------------------------------- /vue-form/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/vue-form/build/logo.png -------------------------------------------------------------------------------- /vue-form/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /vue-form/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/vue-form/src/assets/logo.png -------------------------------------------------------------------------------- /vue-form/src/common/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/vue-form/src/common/images/logo.png -------------------------------------------------------------------------------- /vue-form/src/common/images/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/vue-form/src/common/images/menu.png -------------------------------------------------------------------------------- /vue-node/config/db.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | mongodb: 'mongodb://localhost:27017/form' 4 | } 5 | console.log('数据库连接成功') -------------------------------------------------------------------------------- /vue-form/src/common/images/home_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/vue-form/src/common/images/home_bg.jpg -------------------------------------------------------------------------------- /vue-form/src/common/images/login_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/vue-form/src/common/images/login_bg.jpg -------------------------------------------------------------------------------- /vue-form/src/common/images/logo_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/vue-form/src/common/images/logo_transparent.png -------------------------------------------------------------------------------- /vue-form/src/common/images/form_model/model_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/vue-form/src/common/images/form_model/model_1.jpg -------------------------------------------------------------------------------- /vue-form/src/common/images/form_model/model_1_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkxiaojun/VueForm/HEAD/vue-form/src/common/images/form_model/model_1_title.png -------------------------------------------------------------------------------- /vue-node/router/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | 4 | router.get('/', (req,res) => { 5 | res.send('Hello Express!') 6 | }) 7 | 8 | module.exports = router -------------------------------------------------------------------------------- /vue-form/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /vue-form/.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 | -------------------------------------------------------------------------------- /vue-node/models/form.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const formSchema = mongoose.Schema({ 4 | title: String, 5 | form: Array 6 | }) 7 | 8 | const form = module.exports = mongoose.model('form', formSchema) -------------------------------------------------------------------------------- /vue-node/models/models.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const modelsSchema = mongoose.Schema({ 3 | title: String, 4 | form: Array 5 | }) 6 | const models = module.exports = mongoose.model('models', modelsSchema) 7 | -------------------------------------------------------------------------------- /vue-form/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /vue-form/src/common/less/variable.less: -------------------------------------------------------------------------------- 1 | // 颜色定义规范 2 | @body-color: #333; 3 | @color-a:#5c5c5c; 4 | @color-btn-blue:#00a0e9; 5 | 6 | // 字体定义规范 7 | @font-size-ss:10px; 8 | @font-size-s:12px; 9 | @font-size-m:14px; 10 | @font-size-l:16px; 11 | @font-size-l-x:20px; -------------------------------------------------------------------------------- /vue-form/src/components/form/success.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /vue-node/models/form_data.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const formDataSchema = mongoose.Schema({ 3 | id: String, 4 | title: String, 5 | time: String, 6 | form: Array 7 | }) 8 | const formData = module.exports = mongoose.model('formData', formDataSchema) -------------------------------------------------------------------------------- /vue-form/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "postcss-import": {}, 7 | "autoprefixer": {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vue-form/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /vue-node/models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | // var ObjectId = mongoose.Schema.Types.ObjectId; 3 | 4 | const UserSchema = mongoose.Schema({ 5 | id: String, 6 | username: { type: String, required: true }, 7 | password: { type: String, required: true } 8 | }) 9 | 10 | const User = module.exports = mongoose.model('user', UserSchema) -------------------------------------------------------------------------------- /vue-form/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /vue-form/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | vue-form 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /vue-node/models/movie.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const movieSchema = mongoose.Schema({ 4 | title: { type: String, required: true }, 5 | poster: {}, 6 | rating: String, 7 | instroduction: String, 8 | created_at: { type: Date, default: Date.now }, 9 | update_at: { type: Date, default: Date.now } 10 | }) 11 | 12 | const Movie = module.exports = mongoose.model('Movie', movieSchema) 13 | -------------------------------------------------------------------------------- /vue-form/src/common/less/base.less: -------------------------------------------------------------------------------- 1 | @import "./variable.less"; 2 | 3 | body { 4 | background: #fff; 5 | min-width: 1000px; 6 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; 7 | font-size: 14px; 8 | line-height: 1.42857143; 9 | color: #545454; 10 | } 11 | .clearfix:after { 12 | content: " "; 13 | display: block; 14 | clear: both; 15 | height: 0; 16 | } 17 | .clearfix { 18 | zoom: 1; 19 | } 20 | .left{ 21 | float: left; 22 | } 23 | .right{ 24 | float: right; 25 | } -------------------------------------------------------------------------------- /vue-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-node", 3 | "version": "1.0.0", 4 | "description": "a server for vue-form", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "node" 11 | ], 12 | "author": "hope", 13 | "license": "ISC", 14 | "dependencies": { 15 | "body-parser": "^1.18.2", 16 | "express": "^4.16.3", 17 | "mongodb": "^3.0.4", 18 | "mongoose": "^5.0.11" 19 | }, 20 | "devDependencies": { 21 | "nodemon": "^1.17.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /vue-form/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | import '@/common/less/index.less' 7 | import ElementUI from 'element-ui' 8 | import 'element-ui/lib/theme-chalk/index.css' 9 | 10 | Vue.config.productionTip = false 11 | Vue.use(ElementUI) 12 | 13 | /* eslint-disable no-new */ 14 | new Vue({ 15 | el: '#app', 16 | router, 17 | template: '', 18 | components: { App } 19 | }) 20 | -------------------------------------------------------------------------------- /vue-node/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const app = express() 3 | const config = require('./config/db') 4 | const mongoose = require('mongoose') 5 | const bodyParser = require('body-parser') 6 | mongoose.connect(config.mongodb) 7 | 8 | const index = require('./router/index') 9 | const movie = require('./router/movie') 10 | const form = require('./router/form') 11 | 12 | app.use(bodyParser.json()) 13 | app.use(bodyParser.urlencoded({ extended: true })) 14 | app.use('/',index) 15 | app.use('/api',movie) 16 | app.use('/api',form) 17 | 18 | app.listen(3000,() => { 19 | console.log('app listening on port 3000.') 20 | }) -------------------------------------------------------------------------------- /vue-form/src/components/m-footer/footer.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 29 | -------------------------------------------------------------------------------- /vue-form/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vue-form/src/common/less/index.less: -------------------------------------------------------------------------------- 1 | @import "./base.less"; 2 | @import "./reset.less"; 3 | @import "./variable.less"; 4 | .btn { 5 | display: inline-block; 6 | padding: 2px 12px; 7 | margin-bottom: 0; 8 | font-size: 14px; 9 | font-weight: 400; 10 | line-height: 1.42857143; 11 | text-align: center; 12 | white-space: nowrap; 13 | vertical-align: middle; 14 | -ms-touch-action: manipulation; 15 | touch-action: manipulation; 16 | cursor: pointer; 17 | -webkit-user-select: none; 18 | -moz-user-select: none; 19 | -ms-user-select: none; 20 | user-select: none; 21 | background-image: none; 22 | border-radius: 4px; 23 | } 24 | .b-blue { 25 | background-color: #00a0e9; 26 | } 27 | .b-white{ 28 | background-color: #fff; 29 | } 30 | .b-transparent{ 31 | background-color: transparent; 32 | } 33 | .f-white{ 34 | color: #fff; 35 | } 36 | -------------------------------------------------------------------------------- /vue-form/src/api/user.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | 3 | export function getUser(user, callback) { 4 | axios({ 5 | method: 'post', 6 | url: 'api/user/find', 7 | data: { 8 | username: user.username, 9 | password: user.password 10 | } 11 | }) 12 | .then(function (res) { 13 | callback(res) 14 | }) 15 | .catch(function (error) { 16 | console.log(error); 17 | }); 18 | } 19 | 20 | export function addUser(user, callback) { 21 | axios({ 22 | method: 'post', 23 | url: '/api/user/add', 24 | data: { 25 | username: user.username, 26 | password: user.password 27 | } 28 | }) 29 | .then(res => { 30 | callback(res) 31 | }) 32 | } 33 | 34 | export function updateUser(user, callback){ 35 | axios({ 36 | method: 'post', 37 | url: '/api/user/update', 38 | data: { 39 | password: user.password 40 | } 41 | }) 42 | .then(res => { 43 | callback(res) 44 | }) 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /vue-form/src/common/less/reset.less: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | ol, ul { 32 | list-style: none; 33 | } 34 | blockquote, q { 35 | quotes: none; 36 | } 37 | blockquote:before, blockquote:after, 38 | q:before, q:after { 39 | content: ''; 40 | content: none; 41 | } 42 | table { 43 | border-collapse: collapse; 44 | border-spacing: 0; 45 | } 46 | -------------------------------------------------------------------------------- /vue-form/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // if you are using ts-loader, setting this to true will make tyescript errors show up during build 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /vue-form/src/components/person/person_desc.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 44 | 45 | -------------------------------------------------------------------------------- /vue-form/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vue-form/src/components/home.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 33 | 67 | 68 | -------------------------------------------------------------------------------- /vue-form/src/components/person/person_review.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 53 | 54 | -------------------------------------------------------------------------------- /vue-form/src/components/m-header/header.vue: -------------------------------------------------------------------------------- 1 | 20 | 23 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /vue-form/src/common/less/customForm_h5.less: -------------------------------------------------------------------------------- 1 | .el-button{ 2 | padding: 6px 10px; 3 | } 4 | .customForm { 5 | width: 100%; 6 | } 7 | 8 | .customForm > * { 9 | float: left; 10 | padding: 20px 11 | } 12 | 13 | .leftForm { 14 | width: 50%; 15 | } 16 | 17 | .rightForm { 18 | background-color: #F6F7F8; 19 | width: 40%; 20 | height: 100%; 21 | position: fixed; 22 | right: 0; 23 | } 24 | 25 | .rightForm form { 26 | height: 100%; 27 | } 28 | 29 | .box { 30 | /*padding-left:10px;*/ 31 | /*width:80%;*/ 32 | /*border-left:1px solid #4483EB;*/ 33 | /*min-height:300px;*/ 34 | height: auto; 35 | position: relative; 36 | } 37 | 38 | .box >* { 39 | /*float:left;*/ 40 | } 41 | 42 | .box form { 43 | width: 90%; 44 | border-left: 2px solid; 45 | height: auto; 46 | min-height: 300px; 47 | /*position: absolute;*/ 48 | /*right: 0;*/ 49 | float: right; 50 | } 51 | 52 | .circle { 53 | width: 10px; 54 | height: 10px; 55 | border-radius: 10px; 56 | border: 2px solid; 57 | right: 0; 58 | margin-right: -6px; 59 | margin-top: 5px; 60 | position: absolute; 61 | background-color: white; 62 | z-index: 99; 63 | } 64 | 65 | .block { 66 | width: 10%; 67 | position: absolute; 68 | top: 50%; 69 | margin-top: -10px; 70 | height: 20px; 71 | } 72 | 73 | .block .name { 74 | float: left; 75 | } 76 | 77 | .block .circle { 78 | float: right; 79 | } 80 | 81 | .blue { 82 | border-color: #4483EB !important; 83 | } 84 | 85 | .lightblue { 86 | border-color: #66CCFF !important; 87 | } 88 | 89 | .operate { 90 | /*padding-left: 10px;*/ 91 | position: relative; 92 | height: 50px; 93 | } 94 | 95 | .operate .button { 96 | width: 90%; 97 | border-left: 2px solid; 98 | height: 100%; 99 | position: absolute; 100 | right: 0; 101 | } 102 | 103 | .button .center { 104 | width: 200px; 105 | margin: 0 auto; 106 | line-height: 25px 107 | } 108 | 109 | .clearfix:after { 110 | content: " "; 111 | display: block; 112 | height: 0; 113 | clear: both; 114 | visibility: hidden; 115 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 整个项目最终实现的是一个自定义表单系统,用户可以用鼠标拖拽生成自己的表单,并填写和发布表单 3 | 4 | # 目录 5 | **PS:** 整个project是包含前端vue-form和后端vue-node项目的 6 | 1. 前端项目 7 | ``` 8 | ├─vue-form 9 | │ ├─build ------------------------------------------ webpack配置 10 | │ ├─config ------------------------------------------ 项目配置 11 | │ ├─src ------------------------------------------ 代码目录 12 | │ │ ├─api ------------------------------------------ 接口文件 13 | │ │ ├─common --------------------------------------- 公共文件夹 14 | │ │ │ ├─images 15 | │ │ │ ├─js 16 | │ │ │ └─less 17 | │ │ ├─components ----------------------------------- 组件 18 | │ │ │ ├─form 19 | │ │ │ ├─help 20 | │ │ │ ├─login 21 | │ │ │ ├─m-footer 22 | │ │ │ ├─m-header 23 | │ │ │ ├─person 24 | │ │ │ └─signup 25 | │ │ └─router ------------------------------------------- 路由 26 | │ └─static 27 | ``` 28 | 29 | 2. 后端项目 30 | ``` 31 | └─vue-node 32 | ├─config ------------------------------------------- 配置文件 33 | ├─models ------------------------------------------- 数据model文件 34 | └─router ------------------------------------------- api接口路由文件 35 | ├─app.js ------------------------------------------- 入口 36 | ├─package.json -------------------------------------- 依赖 37 | ``` 38 | 39 | # 技术栈 40 | 1. 前端 41 | vue + vue-router + axios + vuedraggable + element-ui 42 | 43 | 2. 后端 44 | node + express + mongodb + mongoose 45 | 46 | # 功能列表 47 | 1. 登录、注册模块 48 | 2. 表单模块 49 | 3. 表单模板模块 50 | 4. 个人信息模块 51 | 5. 管理员模块 52 | 53 | 功能结构图 54 | ![系统功能结构图](./img/demo.png) 55 | 56 | ## 界面预览 57 | ![系统功能结构图](./img/login.png) 58 | ![系统功能结构图](./img/main.png) 59 | ![系统功能结构图](./img/drag.png) 60 | ![系统功能结构图](./img/view.png) 61 | 62 | ## 项目启动 63 | 1. 前端项目 64 | ``` bash 65 | # install dependencies 66 | npm install 67 | 68 | # serve with hot reload at localhost:8080 69 | npm run dev 70 | 71 | # build for production with minification 72 | npm run build 73 | 74 | # build for production and view the bundle analyzer report 75 | npm run build --report 76 | 77 | # run unit tests 78 | npm run unit 79 | 80 | # run all tests 81 | npm test 82 | ``` 83 | 84 | 2. 后端项目 85 | 先启动数据库mongodb 86 | 再启动项目 87 | ``` 88 | # install dependencies 89 | npm install 90 | 91 | # serve 92 | node app.js 93 | ``` 94 | 95 | -------------------------------------------------------------------------------- /vue-form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-form", 3 | "version": "1.0.0", 4 | "description": "a project with vue and node.js", 5 | "author": "kkxiaojun <985531883@qq.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.18.0", 14 | "element-ui": "^2.0.11", 15 | "sortablejs": "^1.7.0", 16 | "vue": "^2.5.2", 17 | "vue-router": "^3.0.1", 18 | "vuedraggable": "^2.16.0" 19 | }, 20 | "devDependencies": { 21 | "autoprefixer": "^7.1.2", 22 | "axios": "^0.18.0", 23 | "babel-core": "^6.22.1", 24 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 25 | "babel-loader": "^7.1.1", 26 | "babel-plugin-syntax-jsx": "^6.18.0", 27 | "babel-plugin-transform-runtime": "^6.22.0", 28 | "babel-plugin-transform-vue-jsx": "^3.5.0", 29 | "babel-preset-env": "^1.3.2", 30 | "babel-preset-stage-2": "^6.22.0", 31 | "chalk": "^2.0.1", 32 | "copy-webpack-plugin": "^4.0.1", 33 | "css-loader": "^0.28.7", 34 | "extract-text-webpack-plugin": "^3.0.0", 35 | "file-loader": "^1.1.4", 36 | "friendly-errors-webpack-plugin": "^1.6.1", 37 | "html-webpack-plugin": "^2.30.1", 38 | "less": "^2.7.3", 39 | "less-loader": "^4.0.5", 40 | "node-notifier": "^5.1.2", 41 | "optimize-css-assets-webpack-plugin": "^3.2.0", 42 | "ora": "^1.2.0", 43 | "portfinder": "^1.0.13", 44 | "postcss-import": "^11.0.0", 45 | "postcss-loader": "^2.0.8", 46 | "rimraf": "^2.6.0", 47 | "semver": "^5.3.0", 48 | "shelljs": "^0.7.6", 49 | "uglifyjs-webpack-plugin": "^1.1.1", 50 | "url-loader": "^0.5.8", 51 | "vue-loader": "^13.3.0", 52 | "vue-style-loader": "^3.0.1", 53 | "vue-template-compiler": "^2.5.2", 54 | "webpack": "^3.6.0", 55 | "webpack-bundle-analyzer": "^2.9.0", 56 | "webpack-dev-server": "^2.9.1", 57 | "webpack-merge": "^4.1.0" 58 | }, 59 | "engines": { 60 | "node": ">= 4.0.0", 61 | "npm": ">= 3.0.0" 62 | }, 63 | "browserslist": [ 64 | "> 1%", 65 | "last 2 versions", 66 | "not ie <= 8" 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /vue-form/src/api/form.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | 3 | export function addForm(form, callback) { 4 | console.log(form) 5 | axios({ 6 | method: 'post', 7 | url: 'api/form/add', 8 | data: { 9 | id: form.id, 10 | from: form.from, 11 | title: form.title, 12 | form: form.formItems 13 | } 14 | }) 15 | .then(res => { 16 | callback(res) 17 | }) 18 | .catch(err => { 19 | console.log(err) 20 | }) 21 | } 22 | 23 | export function getForm(callback) { 24 | axios({ 25 | method: 'get', 26 | url: 'api/form/find', 27 | }) 28 | .then(res => { 29 | callback(res) 30 | }) 31 | .catch(err => { 32 | callback(err) 33 | }) 34 | } 35 | 36 | export function getFormById(id, callback) { 37 | axios.get('api/form/id', { 38 | params: { 39 | id: id 40 | } 41 | }) 42 | .then(res => { 43 | callback(res) 44 | }) 45 | .catch(err => { 46 | callback(err) 47 | }) 48 | } 49 | 50 | export function removeForm(id, callback) { 51 | axios.get('api/form/delete', { 52 | params: { 53 | id: id 54 | } 55 | }) 56 | .then(res => { 57 | callback(res) 58 | }) 59 | .catch(err => { 60 | callback(err) 61 | }) 62 | } 63 | 64 | export function addFormData(data, callback) { 65 | axios({ 66 | method: 'post', 67 | url: 'api/form/data/add', 68 | data: { 69 | data: data 70 | } 71 | }) 72 | .then(res => { 73 | callback(res) 74 | }) 75 | .catch(err => { 76 | callback(err) 77 | }) 78 | } 79 | 80 | export function findFormData(id, callback){ 81 | axios.get('api/form/data/find', { 82 | params: { 83 | id: id 84 | } 85 | }) 86 | .then(res => { 87 | callback(res) 88 | }) 89 | .catch(err => { 90 | callback(err) 91 | }) 92 | } 93 | /** 94 | * models 95 | */ 96 | export function findModels(callback){ 97 | axios.get('api/models/find') 98 | .then(res => { 99 | callback(res) 100 | }) 101 | .catch(err => { 102 | callback(err) 103 | }) 104 | } 105 | 106 | export function findModelById(id, callback){ 107 | axios.get('api/models/find/id',{ 108 | params:{ 109 | id: id 110 | } 111 | }) 112 | .then(res => { 113 | callback(res) 114 | }) 115 | .catch(err => { 116 | callback(err) 117 | }) 118 | } -------------------------------------------------------------------------------- /vue-form/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | 12 | 13 | module.exports = { 14 | context: path.resolve(__dirname, '../'), 15 | entry: { 16 | app: './src/main.js' 17 | }, 18 | output: { 19 | path: config.build.assetsRoot, 20 | filename: '[name].js', 21 | publicPath: process.env.NODE_ENV === 'production' 22 | ? config.build.assetsPublicPath 23 | : config.dev.assetsPublicPath 24 | }, 25 | resolve: { 26 | extensions: ['.js', '.vue', '.json', '.less'], 27 | alias: { 28 | 'api': resolve('src/api'), 29 | 'vue$': 'vue/dist/vue.esm.js', 30 | '@': resolve('src'), 31 | 'common': resolve('src/common'), 32 | 'base': resolve('src/base') 33 | } 34 | }, 35 | module: { 36 | rules: [ 37 | { 38 | test: /\.vue$/, 39 | loader: 'vue-loader', 40 | options: vueLoaderConfig 41 | }, 42 | { 43 | test: /\.js$/, 44 | loader: 'babel-loader', 45 | include: [resolve('src'), resolve('test')] 46 | }, 47 | { 48 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 49 | loader: 'url-loader', 50 | options: { 51 | limit: 10000, 52 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 53 | } 54 | }, 55 | { 56 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 57 | loader: 'url-loader', 58 | options: { 59 | limit: 10000, 60 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 61 | } 62 | }, 63 | { 64 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 65 | loader: 'url-loader', 66 | options: { 67 | limit: 10000, 68 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 69 | } 70 | } 71 | ] 72 | }, 73 | node: { 74 | // prevent webpack from injecting useless setImmediate polyfill because Vue 75 | // source contains it (although only uses it if it's native). 76 | setImmediate: false, 77 | // prevent webpack from injecting mocks to Node native modules 78 | // that does not make sense for the client 79 | dgram: 'empty', 80 | fs: 'empty', 81 | net: 'empty', 82 | tls: 'empty', 83 | child_process: 'empty' 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /vue-form/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Home from '@/components/home' 4 | import Login from '@/components/login/login' 5 | import Register from '@/components/signup/signup' 6 | import CustomForm from '@/components/form/custom_form' 7 | import dataForm from '@/components/form/data_form' 8 | import ModelForm from '@/components/form/model_form' 9 | import Help from '@/components/help/help' 10 | import Person from '@/components/person/person' 11 | import Desc from '@/components/person/person_desc' 12 | import Review from '@/components/person/person_review' 13 | import Main from '@/components/main' 14 | import Preview from '@/components/form/PreviewForm' 15 | import View from '@/components/form/view' 16 | import Success from '@/components/form/view' 17 | 18 | Vue.use(Router) 19 | 20 | export default new Router({ 21 | routes: [ 22 | { 23 | path: '/', 24 | name: 'Home', 25 | component: Home 26 | }, 27 | { 28 | path: '/home', 29 | name: 'home', 30 | component: Home 31 | }, 32 | { 33 | path: '/login', 34 | name: 'Login', 35 | component: Login 36 | }, 37 | { 38 | path: '/register', 39 | name: 'register', 40 | component: Register 41 | }, 42 | { 43 | path: '/customform', 44 | name: 'customform', 45 | component: CustomForm 46 | }, 47 | { 48 | path: '/modelform', 49 | name: 'ModelForm', 50 | component: ModelForm 51 | }, 52 | { 53 | path: '/view', 54 | name:'View', 55 | component: View 56 | }, 57 | { 58 | path: '/preview/:id', 59 | name: 'preview', 60 | component: Preview 61 | }, 62 | { 63 | path: 'success', 64 | name: 'success', 65 | component: Success 66 | }, 67 | { 68 | path: '/main', 69 | name: 'main', 70 | component: Main 71 | }, 72 | { 73 | path: '/dataform/:id', 74 | name: 'dataform', 75 | component: dataForm 76 | }, 77 | { 78 | path: '/person', 79 | name: 'person', 80 | component: Person, 81 | children:[ 82 | { 83 | path:'desc', 84 | name:'desc', 85 | component:Desc 86 | }, 87 | { 88 | path:'review', 89 | name:'review', 90 | component:Review 91 | } 92 | ] 93 | }, 94 | { 95 | path: '/help', 96 | name: 'help', 97 | component: Help 98 | } 99 | ] 100 | }) 101 | -------------------------------------------------------------------------------- /vue-form/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.2.7 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: { 14 | '/api': { 15 | target: 'http://localhost:3000', 16 | changeOrigin: true 17 | } 18 | }, 19 | 20 | // Various Dev Server settings 21 | host: 'localhost', // can be overwritten by process.env.HOST 22 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 23 | autoOpenBrowser: false, 24 | errorOverlay: true, 25 | notifyOnErrors: true, 26 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 27 | 28 | 29 | /** 30 | * Source Maps 31 | */ 32 | 33 | // https://webpack.js.org/configuration/devtool/#development 34 | devtool: 'eval-source-map', 35 | 36 | // If you have problems debugging vue-files in devtools, 37 | // set this to false - it *may* help 38 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 39 | cacheBusting: true, 40 | 41 | // CSS Sourcemaps off by default because relative paths are "buggy" 42 | // with this option, according to the CSS-Loader README 43 | // (https://github.com/webpack/css-loader#sourcemaps) 44 | // In our experience, they generally work as expected, 45 | // just be aware of this issue when enabling this option. 46 | cssSourceMap: false, 47 | }, 48 | 49 | build: { 50 | // Template for index.html 51 | index: path.resolve(__dirname, '../dist/index.html'), 52 | 53 | // Paths 54 | assetsRoot: path.resolve(__dirname, '../dist'), 55 | assetsSubDirectory: 'static', 56 | assetsPublicPath: '/', 57 | 58 | /** 59 | * Source Maps 60 | */ 61 | 62 | productionSourceMap: true, 63 | // https://webpack.js.org/configuration/devtool/#production 64 | devtool: '#source-map', 65 | 66 | // Gzip off by default as many popular static hosts such as 67 | // Surge or Netlify already gzip all static assets for you. 68 | // Before setting to `true`, make sure to: 69 | // npm install --save-dev compression-webpack-plugin 70 | productionGzip: false, 71 | productionGzipExtensions: ['js', 'css'], 72 | 73 | // Run the build command with an extra argument to 74 | // View the bundle analyzer report after build finishes: 75 | // `npm run build --report` 76 | // Set to `true` or `false` to always turn it on or off 77 | bundleAnalyzerReport: process.env.npm_config_report 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /vue-node/router/movie.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const Movie = require('../models/movie') 4 | const User = require('../models/user') 5 | 6 | router.post('/user/find', (req, res) => { 7 | User.find({ 8 | username: req.body.username, 9 | password: req.body.password 10 | }) 11 | .then(movies => { 12 | res.json(movies) 13 | }) 14 | .catch(err => { 15 | res.json(err) 16 | }) 17 | }) 18 | router.post('/user/add', (req, res) => { 19 | User.create({ 20 | username: req.body.username, 21 | password: req.body.password 22 | }) 23 | .then(movies => { 24 | res.json(movies) 25 | }) 26 | .catch(err => { 27 | res.json(err) 28 | }) 29 | }) 30 | 31 | router.get('/user/remove', (req, res) => { 32 | User.remove({}) 33 | }) 34 | 35 | // 查询所有电影 36 | router.get('/movie', (req, res) => { 37 | Movie.find({}) 38 | .sort({ update_at: -1 }) 39 | .then(movies => { 40 | res.json(movies) 41 | }) 42 | .catch(err => { 43 | res.json(err) 44 | }) 45 | }) 46 | // 通过ObjectId查询单个电影 47 | router.get('/movie/:id', (req, res) => { 48 | Movie.findById(req.params.id) 49 | .then(movie => { 50 | res.json(movie) 51 | }) 52 | .catch(err => { 53 | res.json(err) 54 | }) 55 | }) 56 | // 添加一部电影 57 | router.post('/movie', (req, res) => { 58 | //使用Movie model上的create方法储存数据 59 | Movie.create(req.body, (err, movie) => { 60 | if (err) { 61 | res.json(err) 62 | } else { 63 | res.json(movie) 64 | } 65 | }) 66 | //使用实例的save方法存储数据 67 | // let movie = new Movie({ 68 | // title : req.body.title, 69 | // year : req.body.year, 70 | // poster : req.body.poster, 71 | // introduction : req.body.introduction 72 | // }) 73 | // movie.save( (err,movie) => { 74 | // if (err) { 75 | // res.json(err) 76 | // } else { 77 | // res.json(movie) 78 | // } 79 | // }) 80 | }) 81 | //更新一部电影 82 | router.put('/movie/:id', (req, res) => { 83 | Movie.findOneAndUpdate({ _id: req.params.id } 84 | , { 85 | $set: { 86 | title: req.body.title, 87 | rating: req.body.rating, 88 | poster: req.body.poster, 89 | introduction: req.body.introduction 90 | } 91 | }, { 92 | new: true 93 | }) 94 | .then(movie => res.json(movie)) 95 | .catch(err => res.json(err)) 96 | }) 97 | //删除一部电影 98 | router.delete('/movie/:id', (req, res) => { 99 | Movie.findOneAndRemove({ 100 | _id: req.params.id 101 | }) 102 | .then(movie => res.send(`${movie.title}删除成功`)) 103 | .catch(err => res.json(err)) 104 | }) 105 | 106 | module.exports = router 107 | -------------------------------------------------------------------------------- /vue-form/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const baseWebpackConfig = require('./webpack.base.conf') 7 | const HtmlWebpackPlugin = require('html-webpack-plugin') 8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 9 | const portfinder = require('portfinder') 10 | 11 | const HOST = process.env.HOST 12 | const PORT = process.env.PORT && Number(process.env.PORT) 13 | 14 | const devWebpackConfig = merge(baseWebpackConfig, { 15 | module: { 16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 17 | }, 18 | // cheap-module-eval-source-map is faster for development 19 | devtool: config.dev.devtool, 20 | 21 | // these devServer options should be customized in /config/index.js 22 | devServer: { 23 | clientLogLevel: 'warning', 24 | historyApiFallback: true, 25 | hot: true, 26 | compress: true, 27 | host: HOST || config.dev.host, 28 | port: PORT || config.dev.port, 29 | open: config.dev.autoOpenBrowser, 30 | overlay: config.dev.errorOverlay 31 | ? { warnings: false, errors: true } 32 | : false, 33 | publicPath: config.dev.assetsPublicPath, 34 | proxy: config.dev.proxyTable, 35 | quiet: true, // necessary for FriendlyErrorsPlugin 36 | watchOptions: { 37 | poll: config.dev.poll, 38 | } 39 | }, 40 | plugins: [ 41 | new webpack.DefinePlugin({ 42 | 'process.env': require('../config/dev.env') 43 | }), 44 | new webpack.HotModuleReplacementPlugin(), 45 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 46 | new webpack.NoEmitOnErrorsPlugin(), 47 | // https://github.com/ampedandwired/html-webpack-plugin 48 | new HtmlWebpackPlugin({ 49 | filename: 'index.html', 50 | template: 'index.html', 51 | inject: true 52 | }), 53 | ] 54 | }) 55 | 56 | module.exports = new Promise((resolve, reject) => { 57 | portfinder.basePort = process.env.PORT || config.dev.port 58 | portfinder.getPort((err, port) => { 59 | if (err) { 60 | reject(err) 61 | } else { 62 | // publish the new Port, necessary for e2e tests 63 | process.env.PORT = port 64 | // add port to devServer config 65 | devWebpackConfig.devServer.port = port 66 | 67 | // Add FriendlyErrorsPlugin 68 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 69 | compilationSuccessInfo: { 70 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 71 | }, 72 | onErrors: config.dev.notifyOnErrors 73 | ? utils.createNotifierCallback() 74 | : undefined 75 | })) 76 | 77 | resolve(devWebpackConfig) 78 | } 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /vue-node/router/form.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const mongoose = require('mongoose') 4 | const ObjectId = require('mongodb').ObjectId 5 | const form = require('../models/form') 6 | const formData = require('../models/form_data') 7 | const models = require('../models/models') 8 | 9 | router.get('/form/find', (req, res) => { 10 | form.find({}) 11 | .then(form => { 12 | res.json(form) 13 | }) 14 | .catch(err => { 15 | res.json(err) 16 | }) 17 | }) 18 | 19 | router.post('/form/data/add', (req, res) => { 20 | let data = req.body.data; 21 | let params = { 22 | id: ObjectId(data.id), 23 | title: data.title, 24 | time: data.time, 25 | form: data.form, 26 | } 27 | formData.create(params) 28 | .then(form => { 29 | res.json(form) 30 | }) 31 | .catch(err => { 32 | res.json(err) 33 | }) 34 | }) 35 | 36 | router.get('/form/data/find', (req, res) => { 37 | formData.find({ id: req.query.id }) 38 | .then(form => { 39 | res.json(form) 40 | }) 41 | .catch(err => { 42 | res.json(err) 43 | }) 44 | }) 45 | 46 | router.get('/form/id', (req, res) => { 47 | form.findOne({ _id: ObjectId(req.query.id) }) 48 | .then(form => { 49 | res.json(form) 50 | }) 51 | .catch(err => { 52 | res.json(err) 53 | }) 54 | }) 55 | 56 | router.get('/form/delete', (req, res) => { 57 | form.remove({ _id: ObjectId(req.query.id) }) 58 | .then(form => { 59 | res.json(form) 60 | }) 61 | .catch(err => { 62 | res.json(err) 63 | }) 64 | }) 65 | 66 | router.post('/form/add', (req, res) => { 67 | if (req.body.from == 'new' || req.body.from == 'model') { 68 | let params1 = { 69 | title: req.body.title, 70 | form: req.body.form 71 | } 72 | form.create(params1) 73 | .then(form => { 74 | res.json(form) 75 | }) 76 | .catch(err => { 77 | res.json(err) 78 | }) 79 | } else { 80 | let condition = { _id: ObjectId(req.body.id) }; 81 | let params = { 82 | title: req.body.title, 83 | form: req.body.form 84 | } 85 | form.update(condition, params) 86 | .then(form => { 87 | res.json(form) 88 | }) 89 | .catch(err => { 90 | res.json(err) 91 | }) 92 | } 93 | }) 94 | /****************************************** 95 | 模板接口 96 | ******************************************/ 97 | router.get('/models/find', (req, res) => { 98 | models.find() 99 | .then(model => { 100 | res.json(model) 101 | }) 102 | .catch(err => { 103 | res.json(err); 104 | }) 105 | }) 106 | 107 | router.get('/models/find/id', (req, res)=>{ 108 | models.find({_id:ObjectId(req.query.id)}) 109 | .then(model=>{ 110 | res.json(model); 111 | }) 112 | .catch(err=>{ 113 | res.json(err); 114 | }) 115 | }) 116 | 117 | module.exports = router -------------------------------------------------------------------------------- /vue-form/src/components/form/data_form.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 60 | 61 | 114 | -------------------------------------------------------------------------------- /vue-form/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /vue-form/src/components/person/person.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 53 | 54 | -------------------------------------------------------------------------------- /vue-form/src/components/signup/signup.vue: -------------------------------------------------------------------------------- 1 | 39 | 67 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /vue-form/src/components/login/login.vue: -------------------------------------------------------------------------------- 1 | 45 | 76 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /vue-form/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | 14 | const env = require('../config/prod.env') 15 | 16 | const webpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true, 21 | usePostCSS: true 22 | }) 23 | }, 24 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 25 | output: { 26 | path: config.build.assetsRoot, 27 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 29 | }, 30 | plugins: [ 31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 32 | new webpack.DefinePlugin({ 33 | 'process.env': env 34 | }), 35 | new UglifyJsPlugin({ 36 | uglifyOptions: { 37 | compress: { 38 | warnings: false 39 | } 40 | }, 41 | sourceMap: config.build.productionSourceMap, 42 | parallel: true 43 | }), 44 | // extract css into its own file 45 | new ExtractTextPlugin({ 46 | filename: utils.assetsPath('css/[name].[contenthash].css'), 47 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 48 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 49 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 50 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 51 | allChunks: true, 52 | }), 53 | // Compress extracted CSS. We are using this plugin so that possible 54 | // duplicated CSS from different components can be deduped. 55 | new OptimizeCSSPlugin({ 56 | cssProcessorOptions: config.build.productionSourceMap 57 | ? { safe: true, map: { inline: false } } 58 | : { safe: true } 59 | }), 60 | // generate dist index.html with correct asset hash for caching. 61 | // you can customize output by editing /index.html 62 | // see https://github.com/ampedandwired/html-webpack-plugin 63 | new HtmlWebpackPlugin({ 64 | filename: config.build.index, 65 | template: 'index.html', 66 | inject: true, 67 | minify: { 68 | removeComments: true, 69 | collapseWhitespace: true, 70 | removeAttributeQuotes: true 71 | // more options: 72 | // https://github.com/kangax/html-minifier#options-quick-reference 73 | }, 74 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 75 | chunksSortMode: 'dependency' 76 | }), 77 | // keep module.id stable when vender modules does not change 78 | new webpack.HashedModuleIdsPlugin(), 79 | // enable scope hoisting 80 | new webpack.optimize.ModuleConcatenationPlugin(), 81 | // split vendor js into its own file 82 | new webpack.optimize.CommonsChunkPlugin({ 83 | name: 'vendor', 84 | minChunks (module) { 85 | // any required modules inside node_modules are extracted to vendor 86 | return ( 87 | module.resource && 88 | /\.js$/.test(module.resource) && 89 | module.resource.indexOf( 90 | path.join(__dirname, '../node_modules') 91 | ) === 0 92 | ) 93 | } 94 | }), 95 | // extract webpack runtime and module manifest to its own file in order to 96 | // prevent vendor hash from being updated whenever app bundle is updated 97 | new webpack.optimize.CommonsChunkPlugin({ 98 | name: 'manifest', 99 | minChunks: Infinity 100 | }), 101 | // This instance extracts shared chunks from code splitted chunks and bundles them 102 | // in a separate chunk, similar to the vendor chunk 103 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 104 | new webpack.optimize.CommonsChunkPlugin({ 105 | name: 'app', 106 | async: 'vendor-async', 107 | children: true, 108 | minChunks: 3 109 | }), 110 | 111 | // copy custom static assets 112 | new CopyWebpackPlugin([ 113 | { 114 | from: path.resolve(__dirname, '../static'), 115 | to: config.build.assetsSubDirectory, 116 | ignore: ['.*'] 117 | } 118 | ]) 119 | ] 120 | }) 121 | 122 | if (config.build.productionGzip) { 123 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 124 | 125 | webpackConfig.plugins.push( 126 | new CompressionWebpackPlugin({ 127 | asset: '[path].gz[query]', 128 | algorithm: 'gzip', 129 | test: new RegExp( 130 | '\\.(' + 131 | config.build.productionGzipExtensions.join('|') + 132 | ')$' 133 | ), 134 | threshold: 10240, 135 | minRatio: 0.8 136 | }) 137 | ) 138 | } 139 | 140 | if (config.build.bundleAnalyzerReport) { 141 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 142 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 143 | } 144 | 145 | module.exports = webpackConfig 146 | -------------------------------------------------------------------------------- /vue-form/src/components/form/PreviewForm.vue: -------------------------------------------------------------------------------- 1 | 105 | 106 | 130 | 131 | 148 | -------------------------------------------------------------------------------- /vue-form/src/components/form/view.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 136 | 137 | 214 | -------------------------------------------------------------------------------- /vue-form/src/common/js/customForm_h5.js: -------------------------------------------------------------------------------- 1 | function drag(o) { 2 | //默认参数,传入的参数o之后会与之合并 3 | var options = 4 | { 5 | dragArea: "dragArea", 6 | dropArea: "dropArea", 7 | } 8 | //document.getElementById捕捉到的节点 9 | var target = 10 | { 11 | dragArea: null, 12 | dropArea: null, 13 | } 14 | //后面用于判断鼠标移动的时候是把元素底部的边变蓝还是上部的边变蓝 15 | var deraction = 16 | { 17 | index: -1, 18 | deraction: 0, 19 | flag: -1, 20 | } 21 | 22 | //正在拖拽的元素 23 | var dragTarget; 24 | var empty = function (obj) { 25 | if (obj == undefined || obj == null || obj == "") 26 | return true; 27 | 28 | return false; 29 | } 30 | var exception = function (tip) { 31 | console.log("dragErr:" + tip); 32 | throw new Error(tip); 33 | } 34 | var getTarget = function (id) { 35 | var target = document.getElementById(id); 36 | if (empty(target)) 37 | throw new Error("无法找到这个id") 38 | return target; 39 | } 40 | // ========== 41 | // = 获取鼠标所在的坐标位置 = 42 | // ========== 43 | var getPageLocation = function (event) { 44 | var e = event || window.event; 45 | var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; 46 | var scrollY = document.documentElement.scrollTop || document.body.scrollTop; 47 | var x = e.pageX || e.clientX + scrollX; 48 | var y = e.pageY || e.clientY + scrollY; 49 | return { 'x': x, 'y': y }; 50 | } 51 | var insertAfter = function (newElement, targetElement) { 52 | var parent = targetElement.parentNode; 53 | if (parent.lastChild == targetElement) { 54 | // 如果最后的节点是目标元素,则直接添加。因为默认是最后 55 | parent.appendChild(newElement); 56 | } 57 | else { 58 | //如果不是,则插入在目标元素的下一个兄弟节点的前面。也就是目标元素的后面 59 | parent.insertBefore(newElement, targetElement.nextSibling); 60 | } 61 | } 62 | var insertBefore = function (newElement, targetElement) { 63 | targetElement.parentNode.insertBefore(newElement, targetElement) 64 | } 65 | // ========== 66 | // = 将每个表单区的上下边界恢复成原样 = 67 | // ========== 68 | var setBorderDefault = function () { 69 | if (target.dropArea.children.length > 0) 70 | for (var i = 0; i < target.dropArea.children.length; i++) { 71 | target.dropArea.children[i].style.borderBottom = ""; 72 | target.dropArea.children[i].style.borderTop = ""; 73 | } 74 | } 75 | // ========== 76 | // = 获取CSS = 77 | // ========== 78 | var getCss = function (o, key) { 79 | return o.currentStyle ? o.currentStyle[key] : document.defaultView.getComputedStyle(o, false)[key]; 80 | } 81 | var preventDefault = function (e) { 82 | e.preventDefault(); 83 | } 84 | 85 | /// ========== 86 | // = 将表单元素拖拽到右边删掉 = 87 | // ========== 88 | var dragOut = function (e) { 89 | e.preventDefault(); 90 | if (deraction.flag == 2) { 91 | dragTarget.parentNode.removeChild(dragTarget); 92 | setBorderDefault(); 93 | } 94 | 95 | } 96 | // ========== 97 | // = 开始拖动 = 98 | // ========== 99 | var dragStart = function (e) { 100 | dragTarget = e.target; 101 | //区分拖拽的元素是要新增呢还是要交换位置,记录到flag上,1表示要新增,2表示交换位置 102 | if (dragTarget.parentNode.id == options.dragArea) { 103 | deraction.flag = 1; 104 | } 105 | else { 106 | deraction.flag = 2; 107 | target.dragArea.addEventListener("dragover", preventDefault); 108 | } 109 | } 110 | // ========== 111 | // = 拖动经过 = 112 | // ========== 113 | var dragOver = function (e) { 114 | e.preventDefault(); 115 | var pageLocation = getPageLocation(); 116 | var index = -1; 117 | //检测目前鼠标正落在哪个表单元素上面 118 | if (target.dropArea.children.length > 0) { 119 | for (var i = 0; i < target.dropArea.children.length; i++) { 120 | var pos = target.dropArea.children[i].getBoundingClientRect(); 121 | if (pageLocation.y >= pos.bottom) 122 | continue; 123 | index = i; 124 | break; 125 | } 126 | } 127 | else { 128 | index = 0; 129 | deraction["deraction"] = 0; 130 | deraction["index"] = -1; 131 | deraction["flag"] = 1; 132 | return; 133 | } 134 | if (index != -1) { 135 | var pos = target.dropArea.children[index].getBoundingClientRect(); 136 | setBorderDefault(); 137 | //鼠标落在表单元素宽度中间以上的部分,则上边变蓝 138 | if ((pos.bottom + pos.top) / 2 > pageLocation.y)//元素的上边变蓝 139 | { 140 | deraction["deraction"] = -1; 141 | deraction["index"] = index; 142 | target.dropArea.children[index].style.borderTop = "1px solid blue"; 143 | } 144 | else//元素的下边变蓝 145 | { 146 | deraction["deraction"] = 1; 147 | deraction["index"] = index; 148 | target.dropArea.children[index].style.borderBottom = "1px solid blue"; 149 | } 150 | 151 | } 152 | else//当前拖拽的是第一个表单元素 153 | { 154 | deraction["deraction"] = 0; 155 | deraction["index"] = -1; 156 | } 157 | } 158 | // ========== 159 | // = 拖动结束 = 160 | // ========== 161 | var dragEnd = function (e) { 162 | setBorderDefault(); 163 | target.dragArea.removeEventListener("dragover", preventDefault); 164 | } 165 | // ========== 166 | // = 放置 = 167 | // ========== 168 | var drop = function (e) { 169 | e.preventDefault(); 170 | if (deraction.index != -1) { 171 | var index = deraction.index; 172 | if (deraction.deraction > 0) { 173 | var node; 174 | //flag为1,插入表单元素,否则就是换位置 175 | if (deraction.flag == 1) { 176 | node = dragTarget.cloneNode(true); 177 | node.addEventListener("dragstart", dragStart); 178 | node.addEventListener("dragend", dragEnd); 179 | } 180 | else { 181 | node = dragTarget 182 | } 183 | insertAfter(node, target.dropArea.children[index]); 184 | } 185 | else if (deraction.deraction < 0) { 186 | var node; 187 | if (deraction.flag == 1) { 188 | node = dragTarget.cloneNode(true); 189 | node.addEventListener("dragstart", dragStart); 190 | node.addEventListener("dragend", dragEnd); 191 | } 192 | else { 193 | node = dragTarget 194 | } 195 | insertBefore(node, target.dropArea.children[index]); 196 | } 197 | //target.dropArea=document.getElementById("dropArea"); 198 | } 199 | else if (deraction.flag == 1)//第一个插入的表单元素 200 | { 201 | var node = dragTarget.cloneNode(true); 202 | node.addEventListener("dragstart", dragStart); 203 | node.addEventListener("dragend", dragEnd); 204 | target.dropArea.appendChild(node) 205 | } 206 | deraction.index = -1; 207 | // setBorderDefault(); 208 | } 209 | // ========== 210 | // = 初始化 = 211 | // ========== 212 | var init = function () { 213 | extend(arguments) 214 | if (!empty(options.dragArea)) { 215 | target.dragArea = getTarget(options.dragArea); 216 | target.dragArea.addEventListener("drop", dragOut); 217 | if (target.dragArea.children.length > 0) 218 | for (var i = 0; i < target.dragArea.children.length; i++) { 219 | target.dragArea.children[i].setAttribute("draggable", "true"); 220 | target.dragArea.children[i].addEventListener("dragstart", dragStart); 221 | target.dragArea.children[i].addEventListener("dragend", dragEnd); 222 | } 223 | } 224 | else { 225 | exception("请设置拖拽区域"); 226 | } 227 | if (!empty(options.dropArea)) { 228 | target.dropArea = getTarget(options.dropArea); 229 | target.dropArea.addEventListener("drop", drop); 230 | target.dropArea.addEventListener("dragover", dragOver); 231 | } 232 | else { 233 | exception("请设置存取地址"); 234 | } 235 | } 236 | // ========== 237 | // = 合并参数 = 238 | // ========== 239 | var extend = function (n) { 240 | for (var p in n) if (!options.hasOwnProperty(p) || (options.hasOwnProperty(p) && options[p] != n[p])) 241 | options[p] = n[p]; 242 | } 243 | //执行 244 | if (arguments.length > 1) 245 | var args = arguments[0]; 246 | init.apply(this, args); 247 | } 248 | export default drag; 249 | 250 | -------------------------------------------------------------------------------- /vue-form/src/components/form/model_form.vue: -------------------------------------------------------------------------------- 1 | 65 | 155 | 305 | 306 | 307 | -------------------------------------------------------------------------------- /vue-form/src/components/main.vue: -------------------------------------------------------------------------------- 1 | 73 | 211 | 345 | 346 | 347 | -------------------------------------------------------------------------------- /vue-form/src/components/help/help.vue: -------------------------------------------------------------------------------- 1 | 195 | 196 | 389 | 390 | -------------------------------------------------------------------------------- /vue-form/src/components/form/custom_form.vue: -------------------------------------------------------------------------------- 1 | 205 | 206 | 493 | 494 | 720 | 721 | --------------------------------------------------------------------------------