├── .babelrc ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── README.md ├── build ├── build.js ├── check-versions.js ├── dev-client.js ├── dev-server.js ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.dev.conf.js └── webpack.prod.conf.js ├── config ├── dev.env.js ├── index.js └── prod.env.js ├── https ├── index.js ├── rootCA.pem ├── server.crt └── server.key ├── index.html ├── package.json ├── server ├── api │ ├── listmore.js │ └── userApi.js ├── db.js ├── index.js └── sqlMap.js ├── src ├── App.vue ├── assets │ ├── js │ │ └── cookie.js │ └── logo.png ├── components │ ├── Hello.vue │ ├── foot.vue │ ├── head.vue │ ├── login.vue │ ├── mine.vue │ ├── search.vue │ └── user.vue ├── main.js ├── router │ └── index.js └── vuex │ ├── actions.js │ ├── mutation-types.js │ ├── mutations.js │ └── store.js └── static ├── .gitkeep ├── QQ截图20180529230333.png ├── QQ截图20180529230411.png ├── back-info.jpg ├── icon2.png ├── listmore.json └── theme.scss /.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-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["istanbul"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | *.suo 11 | *.ntvs* 12 | *.njsproj 13 | *.sln 14 | package-lock.json 15 | yarn.lock 16 | -------------------------------------------------------------------------------- /.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 | "autoprefixer": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### vue入门级全栈项目 - 拉钩网H5版本 2 | 这个项目是我在17年的时候创建的,vue都不知道更新多少版本了。 3 | 4 | 但我还是推荐大家去体验下这个项目,项目不大不小刚刚好😊 5 | 6 | 多踩踩坑,无论对SPA还是http都会有一个入门级的了解 7 | 8 | 9 | 10 | 11 | ### 1.使用方法 12 | ```bash 13 | $ git clone https://github.com/1uokun/vue-node-mysql.git 14 | $ cd vue-node-mysql 15 | $ cnpm install # 如果能翻墙则无需使用cnpm 16 | 17 | // 开发 18 | $ npm start 19 | 20 | // 等待编译完成,然后访问locahost:8080预览页面 21 | 22 | // 启动express服务 23 | $ npm run server 24 | 25 | // 这里将前后端分离,后段接口地址为 locahost:3000 26 | ``` 27 | ### 2.HTTPS 28 | 这个项目新增https支持 29 | 30 | **教程**: [如何在5分钟内让HTTPS在本地开发环境中运行](https://medium.freecodecamp.org/how-to-get-https-working-on-your-local-development-environment-in-5-minutes-7af615770eec) 31 | 32 | **具体代码**:[commit](https://github.com/1uokun/vue-node-mysql/commit/295c87f5d121f4c3d6dc4745878d1607ea332516) 33 | 34 | **安装证书**:在文件路径*https/rootCA.pem*中双击该文件即可安装证书 35 | 36 | ⚠**注意**:️如果不想使用https可以通过上述commit删除代码 37 | 38 | ### 3.项目说明: 39 | 40 | 本项目你将用到 41 | ``` 42 | |-- 前端 43 | |-- vue 44 | |-- vue-router 45 | |-- vuex 46 | |-- axios 47 | |-- sass 48 | |-- 后端 49 | |-- express 50 | |-- mysql 51 | |-- 其他 52 | |-- cookie 53 | |-- localstrong 54 | |-- nodemon 55 | ``` 56 | 其中项目css使用sass预编译,如果不能翻墙,请使用淘宝镜像或yarn或者直接在GitHub中找。
57 | `cnpm install node-sass sass-loader`
58 | https://segmentfault.com/a/1190000011288053
59 | 这篇文章介绍了后端功能细节
60 | 61 | ### 4.常见问题 62 | 63 | - 接口`404` 64 | 65 | express服务没有开启 66 | ```bash 67 | npm run server 68 | ``` 69 | 70 | - 接口`504` 71 | 72 | 本地数据库没有创建,现在已为您添加自动创建数据库脚本 73 | ```db 74 | create database vue_node_mysql_learning 75 | ``` 76 | 另外数据库的初始密码可能不是123456,有些是12345678。 77 | 记得在`server/db.js`内修改 78 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | var ora = require('ora') 6 | var rm = require('rimraf') 7 | var path = require('path') 8 | var chalk = require('chalk') 9 | var webpack = require('webpack') 10 | var config = require('../config') 11 | var webpackConfig = require('./webpack.prod.conf') 12 | 13 | var spinner = ora('building for production...') 14 | spinner.start() 15 | 16 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 17 | if (err) throw err 18 | webpack(webpackConfig, function (err, stats) { 19 | spinner.stop() 20 | if (err) throw err 21 | process.stdout.write(stats.toString({ 22 | colors: true, 23 | modules: false, 24 | children: false, 25 | chunks: false, 26 | chunkModules: false 27 | }) + '\n\n') 28 | 29 | if (stats.hasErrors()) { 30 | console.log(chalk.red(' Build failed with errors.\n')) 31 | process.exit(1) 32 | } 33 | 34 | console.log(chalk.cyan(' Build complete.\n')) 35 | console.log(chalk.yellow( 36 | ' Tip: built files are meant to be served over an HTTP server.\n' + 37 | ' Opening index.html over file:// won\'t work.\n' 38 | )) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /build/check-versions.js: -------------------------------------------------------------------------------- 1 | var chalk = require('chalk') 2 | var semver = require('semver') 3 | var packageConfig = require('../package.json') 4 | var shell = require('shelljs') 5 | function exec (cmd) { 6 | return require('child_process').execSync(cmd).toString().trim() 7 | } 8 | 9 | var versionRequirements = [ 10 | { 11 | name: 'node', 12 | currentVersion: semver.clean(process.version), 13 | versionRequirement: packageConfig.engines.node 14 | } 15 | ] 16 | 17 | if (shell.which('npm')) { 18 | versionRequirements.push({ 19 | name: 'npm', 20 | currentVersion: exec('npm --version'), 21 | versionRequirement: packageConfig.engines.npm 22 | }) 23 | } 24 | 25 | module.exports = function () { 26 | var warnings = [] 27 | for (var i = 0; i < versionRequirements.length; i++) { 28 | var mod = versionRequirements[i] 29 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 30 | warnings.push(mod.name + ': ' + 31 | chalk.red(mod.currentVersion) + ' should be ' + 32 | chalk.green(mod.versionRequirement) 33 | ) 34 | } 35 | } 36 | 37 | if (warnings.length) { 38 | console.log('') 39 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 40 | console.log() 41 | for (var i = 0; i < warnings.length; i++) { 42 | var warning = warnings[i] 43 | console.log(' ' + warning) 44 | } 45 | console.log() 46 | process.exit(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /build/dev-client.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | require('eventsource-polyfill') 3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 4 | 5 | hotClient.subscribe(function (event) { 6 | if (event.action === 'reload') { 7 | window.location.reload() 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /build/dev-server.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | 3 | var config = require('../config') 4 | if (!process.env.NODE_ENV) { 5 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) 6 | } 7 | 8 | var opn = require('opn') 9 | var path = require('path') 10 | var express = require('express') 11 | var webpack = require('webpack') 12 | var proxyMiddleware = require('http-proxy-middleware') 13 | var webpackConfig = require('./webpack.dev.conf') 14 | 15 | // default port where dev server listens for incoming traffic 16 | var port = process.env.PORT || config.dev.port 17 | // automatically open browser, if not set will be false 18 | var autoOpenBrowser = !!config.dev.autoOpenBrowser 19 | // Define HTTP proxies to your custom API backend 20 | // https://github.com/chimurai/http-proxy-middleware 21 | var proxyTable = config.dev.proxyTable 22 | 23 | var app = express() 24 | var compiler = webpack(webpackConfig) 25 | 26 | var devMiddleware = require('webpack-dev-middleware')(compiler, { 27 | publicPath: webpackConfig.output.publicPath, 28 | quiet: true 29 | }) 30 | 31 | var hotMiddleware = require('webpack-hot-middleware')(compiler, { 32 | log: false, 33 | heartbeat: 2000 34 | }) 35 | // force page reload when html-webpack-plugin template changes 36 | compiler.plugin('compilation', function (compilation) { 37 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 38 | hotMiddleware.publish({ action: 'reload' }) 39 | cb() 40 | }) 41 | }) 42 | 43 | // proxy api requests 44 | Object.keys(proxyTable).forEach(function (context) { 45 | var options = proxyTable[context] 46 | if (typeof options === 'string') { 47 | options = { target: options } 48 | } 49 | app.use(proxyMiddleware(options.filter || context, options)) 50 | }) 51 | 52 | // handle fallback for HTML5 history API 53 | app.use(require('connect-history-api-fallback')()) 54 | 55 | // serve webpack bundle output 56 | app.use(devMiddleware) 57 | 58 | // enable hot-reload and state-preserving 59 | // compilation error display 60 | app.use(hotMiddleware) 61 | 62 | // serve pure static assets 63 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 64 | app.use(staticPath, express.static('./static')) 65 | 66 | var uri = 'https://localhost:' + port 67 | 68 | var _resolve 69 | var readyPromise = new Promise(resolve => { 70 | _resolve = resolve 71 | }) 72 | 73 | console.log('> Starting dev server...') 74 | devMiddleware.waitUntilValid(() => { 75 | console.log('> Listening at ' + uri + '\n') 76 | // when env is testing, don't need open it 77 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { 78 | opn(uri) 79 | } 80 | _resolve() 81 | }) 82 | 83 | // var server = app.listen(port) 84 | 85 | //添加https环境 86 | const certOptions = require('../https') 87 | const https = require('https'); 88 | https.createServer(certOptions, app).listen(port); 89 | 90 | 91 | module.exports = { 92 | ready: readyPromise, 93 | close: () => { 94 | server.close() 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | exports.assetsPath = function (_path) { 6 | var assetsSubDirectory = process.env.NODE_ENV === 'production' 7 | ? config.build.assetsSubDirectory 8 | : config.dev.assetsSubDirectory 9 | return path.posix.join(assetsSubDirectory, _path) 10 | } 11 | 12 | exports.cssLoaders = function (options) { 13 | options = options || {} 14 | 15 | var cssLoader = { 16 | loader: 'css-loader', 17 | options: { 18 | minimize: process.env.NODE_ENV === 'production', 19 | sourceMap: options.sourceMap 20 | } 21 | } 22 | 23 | // generate loader string to be used with extract text plugin 24 | function generateLoaders (loader, loaderOptions) { 25 | var loaders = [cssLoader] 26 | if (loader) { 27 | loaders.push({ 28 | loader: loader + '-loader', 29 | options: Object.assign({}, loaderOptions, { 30 | sourceMap: options.sourceMap 31 | }) 32 | }) 33 | } 34 | 35 | // Extract CSS when that option is specified 36 | // (which is the case during production build) 37 | if (options.extract) { 38 | return ExtractTextPlugin.extract({ 39 | use: loaders, 40 | fallback: 'vue-style-loader' 41 | }) 42 | } else { 43 | return ['vue-style-loader'].concat(loaders) 44 | } 45 | } 46 | 47 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 48 | return { 49 | css: generateLoaders(), 50 | postcss: generateLoaders(), 51 | less: generateLoaders('less'), 52 | sass: generateLoaders('sass', { indentedSyntax: true }), 53 | scss: generateLoaders('sass'), 54 | stylus: generateLoaders('stylus'), 55 | styl: generateLoaders('stylus') 56 | } 57 | } 58 | 59 | // Generate loaders for standalone style files (outside of .vue) 60 | exports.styleLoaders = function (options) { 61 | var output = [] 62 | var loaders = exports.cssLoaders(options) 63 | for (var extension in loaders) { 64 | var loader = loaders[extension] 65 | output.push({ 66 | test: new RegExp('\\.' + extension + '$'), 67 | use: loader 68 | }) 69 | } 70 | return output 71 | } 72 | -------------------------------------------------------------------------------- /build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils') 2 | var config = require('../config') 3 | var isProduction = process.env.NODE_ENV === 'production' 4 | 5 | module.exports = { 6 | loaders: utils.cssLoaders({ 7 | sourceMap: isProduction 8 | ? config.build.productionSourceMap 9 | : config.dev.cssSourceMap, 10 | extract: isProduction 11 | }), 12 | transformToRequire: { 13 | video: 'src', 14 | source: 'src', 15 | img: 'src', 16 | image: 'xlink:href' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var utils = require('./utils') 3 | var config = require('../config') 4 | var vueLoaderConfig = require('./vue-loader.conf') 5 | 6 | function resolve (dir) { 7 | return path.join(__dirname, '..', dir) 8 | } 9 | 10 | module.exports = { 11 | entry: { 12 | app: './src/main.js' 13 | }, 14 | output: { 15 | path: config.build.assetsRoot, 16 | filename: '[name].js', 17 | publicPath: process.env.NODE_ENV === 'production' 18 | ? config.build.assetsPublicPath 19 | : config.dev.assetsPublicPath 20 | }, 21 | resolve: { 22 | extensions: ['.js', '.vue', '.json'], 23 | alias: { 24 | 'vue$': 'vue/dist/vue.esm.js', 25 | '@': resolve('src'), 26 | } 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.vue$/, 32 | loader: 'vue-loader', 33 | options: vueLoaderConfig 34 | }, 35 | { 36 | test: /\.js$/, 37 | loader: 'babel-loader', 38 | include: [resolve('src'), resolve('test')] 39 | }, 40 | { 41 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 42 | loader: 'url-loader', 43 | options: { 44 | limit: 10000, 45 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 46 | } 47 | }, 48 | { 49 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 50 | loader: 'url-loader', 51 | options: { 52 | limit: 10000, 53 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 54 | } 55 | }, 56 | { 57 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 58 | loader: 'url-loader', 59 | options: { 60 | limit: 10000, 61 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 62 | } 63 | } 64 | ] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils') 2 | var webpack = require('webpack') 3 | var config = require('../config') 4 | var merge = require('webpack-merge') 5 | var baseWebpackConfig = require('./webpack.base.conf') 6 | var HtmlWebpackPlugin = require('html-webpack-plugin') 7 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 8 | 9 | // add hot-reload related code to entry chunks 10 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 11 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 12 | }) 13 | 14 | module.exports = merge(baseWebpackConfig, { 15 | module: { 16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 17 | }, 18 | // cheap-module-eval-source-map is faster for development 19 | devtool: '#cheap-module-eval-source-map', 20 | plugins: [ 21 | new webpack.DefinePlugin({ 22 | 'process.env': config.dev.env 23 | }), 24 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 25 | new webpack.HotModuleReplacementPlugin(), 26 | new webpack.NoEmitOnErrorsPlugin(), 27 | // https://github.com/ampedandwired/html-webpack-plugin 28 | new HtmlWebpackPlugin({ 29 | filename: 'index.html', 30 | template: 'index.html', 31 | inject: true 32 | }), 33 | new FriendlyErrorsPlugin() 34 | ] 35 | }) 36 | -------------------------------------------------------------------------------- /build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var utils = require('./utils') 3 | var webpack = require('webpack') 4 | var config = require('../config') 5 | var merge = require('webpack-merge') 6 | var baseWebpackConfig = require('./webpack.base.conf') 7 | var CopyWebpackPlugin = require('copy-webpack-plugin') 8 | var HtmlWebpackPlugin = require('html-webpack-plugin') 9 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 10 | var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 11 | 12 | var env = config.build.env 13 | 14 | var webpackConfig = merge(baseWebpackConfig, { 15 | module: { 16 | rules: utils.styleLoaders({ 17 | sourceMap: config.build.productionSourceMap, 18 | extract: true 19 | }) 20 | }, 21 | devtool: config.build.productionSourceMap ? '#source-map' : false, 22 | output: { 23 | path: config.build.assetsRoot, 24 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 25 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 26 | }, 27 | plugins: [ 28 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 29 | new webpack.DefinePlugin({ 30 | 'process.env': env 31 | }), 32 | new webpack.optimize.UglifyJsPlugin({ 33 | compress: { 34 | warnings: false 35 | }, 36 | sourceMap: true 37 | }), 38 | // extract css into its own file 39 | new ExtractTextPlugin({ 40 | filename: utils.assetsPath('css/[name].[contenthash].css') 41 | }), 42 | // Compress extracted CSS. We are using this plugin so that possible 43 | // duplicated CSS from different components can be deduped. 44 | new OptimizeCSSPlugin({ 45 | cssProcessorOptions: { 46 | safe: true 47 | } 48 | }), 49 | // generate dist index.html with correct asset hash for caching. 50 | // you can customize output by editing /index.html 51 | // see https://github.com/ampedandwired/html-webpack-plugin 52 | new HtmlWebpackPlugin({ 53 | filename: config.build.index, 54 | template: 'index.html', 55 | inject: true, 56 | minify: { 57 | removeComments: true, 58 | collapseWhitespace: true, 59 | removeAttributeQuotes: true 60 | // more options: 61 | // https://github.com/kangax/html-minifier#options-quick-reference 62 | }, 63 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 64 | chunksSortMode: 'dependency' 65 | }), 66 | // keep module.id stable when vender modules does not change 67 | new webpack.HashedModuleIdsPlugin(), 68 | // split vendor js into its own file 69 | new webpack.optimize.CommonsChunkPlugin({ 70 | name: 'vendor', 71 | minChunks: function (module, count) { 72 | // any required modules inside node_modules are extracted to vendor 73 | return ( 74 | module.resource && 75 | /\.js$/.test(module.resource) && 76 | module.resource.indexOf( 77 | path.join(__dirname, '../node_modules') 78 | ) === 0 79 | ) 80 | } 81 | }), 82 | // extract webpack runtime and module manifest to its own file in order to 83 | // prevent vendor hash from being updated whenever app bundle is updated 84 | new webpack.optimize.CommonsChunkPlugin({ 85 | name: 'manifest', 86 | chunks: ['vendor'] 87 | }), 88 | // copy custom static assets 89 | new CopyWebpackPlugin([ 90 | { 91 | from: path.resolve(__dirname, '../static'), 92 | to: config.build.assetsSubDirectory, 93 | ignore: ['.*'] 94 | } 95 | ]) 96 | ] 97 | }) 98 | 99 | if (config.build.productionGzip) { 100 | var CompressionWebpackPlugin = require('compression-webpack-plugin') 101 | 102 | webpackConfig.plugins.push( 103 | new CompressionWebpackPlugin({ 104 | asset: '[path].gz[query]', 105 | algorithm: 'gzip', 106 | test: new RegExp( 107 | '\\.(' + 108 | config.build.productionGzipExtensions.join('|') + 109 | ')$' 110 | ), 111 | threshold: 10240, 112 | minRatio: 0.8 113 | }) 114 | ) 115 | } 116 | 117 | if (config.build.bundleAnalyzerReport) { 118 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 119 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 120 | } 121 | 122 | module.exports = webpackConfig 123 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'], 18 | // Run the build command with an extra argument to 19 | // View the bundle analyzer report after build finishes: 20 | // `npm run build --report` 21 | // Set to `true` or `false` to always turn it on or off 22 | bundleAnalyzerReport: process.env.npm_config_report 23 | }, 24 | dev: { 25 | env: require('./dev.env'), 26 | port: 8080, 27 | autoOpenBrowser: true, 28 | assetsSubDirectory: 'static', 29 | assetsPublicPath: '/', 30 | proxyTable: { 31 | '/api': { 32 | target: 'https://localhost:3000', 33 | secure: false, 34 | changeOrigin: true, 35 | pathRewrite: { 36 | 37 | '^/api': '/api/' 38 | } 39 | } 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 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /https/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const certOptions = { 5 | key: fs.readFileSync(path.resolve('./https/server.key')), 6 | cert: fs.readFileSync(path.resolve('./https/server.crt')) 7 | } 8 | 9 | module.exports = certOptions 10 | -------------------------------------------------------------------------------- /https/rootCA.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICtDCCAZwCCQDWk/vhKyvxpzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFM 3 | b2NhbCBDZXJ0aWZpY2F0ZTAeFw0xODEwMDgwMzQ1NTdaFw0yMTA3MjgwMzQ1NTda 4 | MBwxGjAYBgNVBAMMEUxvY2FsIENlcnRpZmljYXRlMIIBIjANBgkqhkiG9w0BAQEF 5 | AAOCAQ8AMIIBCgKCAQEAuosxsk7vfqDUJS2mWduN+1LVu4uE0jV/N48I4rPQwL6s 6 | VcrewqPdFnvz8gE/D7/Bf6SjTpKrGnbOhWsn8JV1trQ5Mpcch/s1ZoJc9j7GL3bw 7 | EHo79utKflKHvcJHvjTBAm4M30Uze/LUS+v/OrDFlI2uv032uPpm5aGAEeUJxb5n 8 | 9qpnpP3zINpjyRJTPrm27o+U8eO+MEqQuAwEJ2bjmNUMCN+XCoGVQeyLSXoYGxUf 9 | kNbdCOhKgdRGph9p1tYOkMiPH7D+H7BLKsptrpuaN+E2SfJUHcopNiQiwzwaQXdS 10 | sp6l9xVMCm/2XkxxUNpHyuWQSQX3MyRIyWsmjfiX1QIDAQABMA0GCSqGSIb3DQEB 11 | CwUAA4IBAQAa+EtTh/o1DlpNIRMpQ5SmS2wKXrMNHKhmdHDNvXEHrxDRdpcP51rk 12 | NpKPDN8KjSnX+iHMi+KbJ1QB1sIx3o4Hi+JCih9X1FgcOJr1RV5qL5+qEaKP+kKv 13 | XxBehBgfPA9XywsZ4YTiTpGUiUbuHz34XRAP0hbWAk73SRhik0EyfCiJN15KQ6US 14 | GTqz+SqgNOsQ384ScTe+AuoJnQpSVquMSZ8sNNRPTEP6KQa1cjg1SB1DLrEiVDvG 15 | Ioo605Ot5xhmAoaJFeDkv10OnVM3ce+x43NrTo2b9lRJTxKltRiYv1C3Yu/lWHLi 16 | o+4dugUExun3nskOd6N4zK6D+ZTqKo4N 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /https/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDtDCCApygAwIBAgIJAMcuu8m1HWjLMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNV 3 | BAMMEUxvY2FsIENlcnRpZmljYXRlMB4XDTE4MTAwODA1NDM0MloXDTIwMDIyMDA1 4 | NDM0MlowgawxCzAJBgNVBAYTAlVTMRQwEgYDVQQIDAtSYW5kb21TdGF0ZTETMBEG 5 | A1UEBwwKUmFuZG9tQ2l0eTEbMBkGA1UECgwSUmFuZG9tT3JnYW5pemF0aW9uMR8w 6 | HQYDVQQLDBZSYW5kb21Pcmdhbml6YXRpb25Vbml0MSAwHgYJKoZIhvcNAQkBFhEx 7 | NDk4NzEwMDM3QHFxLmNvbTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG 8 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwdpBgznfkXeB75Li0A5Ij4IS94QxNOVjge7P 9 | kbFdnEz1e5eCtc+ZIX/xnWpshOMNqa6WUYQc5QE0qd3MA93JM26UnDVnnyzZyRkh 10 | Zk0rCDqPhjk6ixq52RbDWwo9j426NYuOg/mxYv1glLK0+BxMfXIIh43ZUdIUDhkf 11 | RRHIwdJoXqDWvUrHgXfemaV05XAPbY1mxiW3vxW/fxc06SbKjlcxk/FohfQ9Lkh7 12 | 0QlqH/mIZAj0IpodmX2HLCMlOBYIMiJfiC1wZCTCM9R/CVhO6T+88VVJfC5z1rg6 13 | lQgQGJzf9zi08xrcY8fd7qBCa3CjPj5JmpWbgRoQgc1aIL0nLwIDAQABo2gwZjA2 14 | BgNVHSMELzAtoSCkHjAcMRowGAYDVQQDDBFMb2NhbCBDZXJ0aWZpY2F0ZYIJANaT 15 | ++ErK/GnMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMBQGA1UdEQQNMAuCCWxvY2Fs 16 | aG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAekL0jWYN1oxFbAKb60VAoCHNQHGcpe/n 17 | 4fkzwTyOpA8lcnHk9yMPG+pLKxbWH4YZWhweqAPDM687FHtpxHouPXP5F7VCsmXy 18 | yyUp41EPI3X5qB7IoYKlyojNjyZDug1X9mrbTmDcpOCSpl15DbzULYLOUge+Nt/P 19 | LgcGkpeC3tnsGvR7OkD7ROv26Rtvla1kI48jWahJwrje9L0CvJK4KSRD55COE7TQ 20 | Qic4ifM6Hly4LfSJ2WHAKB0gDKoxIaKPaDDrCeDSDUZeeUT+HapigKJL68MPDG/3 21 | VWMuB4nY88gT2s/AaiAUIg/bEicaEKWMKshFPIVdBbCYR3CLOTb5Gw== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /https/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDB2kGDOd+Rd4Hv 3 | kuLQDkiPghL3hDE05WOB7s+RsV2cTPV7l4K1z5khf/GdamyE4w2prpZRhBzlATSp 4 | 3cwD3ckzbpScNWefLNnJGSFmTSsIOo+GOTqLGrnZFsNbCj2Pjbo1i46D+bFi/WCU 5 | srT4HEx9cgiHjdlR0hQOGR9FEcjB0mheoNa9SseBd96ZpXTlcA9tjWbGJbe/Fb9/ 6 | FzTpJsqOVzGT8WiF9D0uSHvRCWof+YhkCPQimh2ZfYcsIyU4FggyIl+ILXBkJMIz 7 | 1H8JWE7pP7zxVUl8LnPWuDqVCBAYnN/3OLTzGtxjx93uoEJrcKM+PkmalZuBGhCB 8 | zVogvScvAgMBAAECggEAJhnQwIsSbxPvHPhgzgXQqywksDNyPHRNj48xivAydvMF 9 | WMpZrYRlE5mDEq6FL8U0vUIMV29BAp2vDSgD3kqsLgiw+tbgS//TulvPHb1TFOX1 10 | qar737VcC9gWg4FKwjlX/Qg19aiQ/eTRI8jQGufs/Mb5+DKgIR3KRU8Yv0mUjMuH 11 | RSdarkeJf9/si//rVgN8yaRHUYsFToorh8DjGymW4hSuwyzqNzoCTaRNtXBpOmcT 12 | V91aK1clG3v7PuQtaEBFCeJiBrUc/mAR0WAsN7sQCRDvQTzmTNtuVAho5vHnmVAF 13 | ZuTB97pl+Uc4WBVtzV2FBLpJ2tBPCrK7hVJJaaFCyQKBgQDyxOJJf3uh3N38d8sI 14 | b3eYH6OwTGNW/Z/drPd+eA/xTl6dSupPls7Ez6erFvPPGzeVFbZLvHZ4IGC0Xwya 15 | v1sxm270sBTrhVtIag2wUHtGw63aw8+UO/fTXqBcRIW0PxpPWEsy69nS7C3Kr6Az 16 | ZKTlfInfqz5n83gP3OoVvz+LpQKBgQDMauNr9ZoMJ/EGAbopP1wUY0GtTBn3eIM3 17 | YYIXyOZQmekZrxachMZmtVjnRPaRj+k1Auf9+8k1sBpTHJiw+ra38IYlSFKjIQbN 18 | xJHZy0LpayuGWv3RMucqfIfIEXO9jAw9SjNjc71+z6QPPs5V7Anrir8TH0s9bjxr 19 | w+Ah3GY/QwKBgF0iNwWnSJDCIM9YTaXAzYqoqK1bwMiAqDHsx03XVZZnUFjbgqRh 20 | 4JUUbOhv97y+XkZte+/2LyTKlw7bYASF4MBZNuncG0p3cJuJRBMd7Q0qyKmicSIX 21 | F24ZYT/Ecd7CTp+d6ctxO7F1NeQKGFVfCPuZGXObSGesuaSsRYluQzyZAoGBALy3 22 | RUOK1u+4BAxulcW0p7Zut9hcNHDBwinjjC2XHAF3RbqfaLmQ3nU9oFjJ0vQpLrvw 23 | 9eoFhAlbjcIC2XgxzXPcyosqzJerStO2UIwH2bsSZHlCEacLXlxb4SKz8e0gmMAQ 24 | e4ap+FxVJ3mnmue55KKeUa4+qsCIv6EhkQ7RoVdvAoGBANdqEIBujywMp3E71vLi 25 | PH4XIWSkYWiiq42CwD1GPfUhqzp3ikhSrUVHkaBdvcU0PUsU+sSqko5PcYFjpDQp 26 | Z6HmU5ve+SHmLSO4WRgfXKAewagt06m5IutLikWb5yrFCTrcMy9+4Filkx5Bsv1T 27 | 4EQv6NFv5VXfRUHnaVtSaLHE 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | lagi 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lagou", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "", 6 | "private": true, 7 | "scripts": { 8 | "linux": "nodejs build/dev-server.js", 9 | "start": "node build/dev-server.js", 10 | "build": "node build/build.js", 11 | "server": "nodemon server/index.js" 12 | }, 13 | "dependencies": { 14 | "cookie-parser": "^1.4.3", 15 | "nodemon": "1.18.3", 16 | "vue": "2.4.2", 17 | "vue-router": "2.7.0" 18 | }, 19 | "devDependencies": { 20 | "autoprefixer": "7.1.2", 21 | "axios": "0.16.2", 22 | "babel-core": "6.22.1", 23 | "babel-loader": "7.1.1", 24 | "babel-plugin-transform-runtime": "6.22.0", 25 | "babel-preset-env": "1.3.2", 26 | "babel-preset-stage-2": "6.22.0", 27 | "babel-register": "6.22.0", 28 | "body-parser": "1.18.1", 29 | "chalk": "2.0.1", 30 | "connect-history-api-fallback": "1.3.0", 31 | "copy-webpack-plugin": "4.0.1", 32 | "css-loader": "0.28.0", 33 | "cssnano": "3.10.0", 34 | "eventsource-polyfill": "0.9.6", 35 | "express": "4.14.1", 36 | "extract-text-webpack-plugin": "2.0.0", 37 | "file-loader": "0.11.1", 38 | "friendly-errors-webpack-plugin": "1.1.3", 39 | "html-webpack-plugin": "2.28.0", 40 | "http-proxy-middleware": "0.17.3", 41 | "mysql": "2.14.1", 42 | "node-sass": "4.5.3", 43 | "opn": "5.1.0", 44 | "optimize-css-assets-webpack-plugin": "2.0.0", 45 | "ora": "1.2.0", 46 | "rimraf": "2.6.0", 47 | "sass-loader": "6.0.6", 48 | "semver": "5.3.0", 49 | "shelljs": "0.7.6", 50 | "url-loader": "0.5.8", 51 | "vue-loader": "13.0.4", 52 | "vue-style-loader": "3.0.1", 53 | "vue-template-compiler": "2.4.2", 54 | "vuex": "2.4.0", 55 | "webpack": "2.6.1", 56 | "webpack-bundle-analyzer": "2.2.1", 57 | "webpack-dev-middleware": "1.10.0", 58 | "webpack-hot-middleware": "2.18.0", 59 | "webpack-merge": "4.1.0" 60 | }, 61 | "engines": { 62 | "node": ">= 4.0.0", 63 | "npm": ">= 3.0.0" 64 | }, 65 | "browserslist": [ 66 | "> 1%", 67 | "last 2 versions", 68 | "not ie <= 8" 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /server/api/listmore.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var https = require('https'); 4 | 5 | 6 | //中间件 7 | var jsonWrite = function(res, ret) { 8 | if(typeof ret === 'undefined') { 9 | res.send('err') 10 | } else { 11 | res.send(ret) 12 | } 13 | }; 14 | // hello页面列表数据 15 | router.get('/more', (req, res) => { 16 | var request = https.request({ 17 | hostname:'m.lagou.com', 18 | port:'443', 19 | path:'/listmore.json?'+req._parsedUrl.query, 20 | method:'GET' 21 | },(resp)=>{ 22 | let results = ''; 23 | //监听接受数据 24 | resp.on('data',(dataObj)=>{ 25 | results += dataObj; 26 | }) 27 | //监听关闭 28 | resp.on('end',()=>{ 29 | jsonWrite(res, results) 30 | }) 31 | }) 32 | // 发送请求 33 | request.end(); 34 | 35 | }); 36 | 37 | module.exports = router; 38 | -------------------------------------------------------------------------------- /server/api/userApi.js: -------------------------------------------------------------------------------- 1 | var models = require('../db'); 2 | var express = require('express'); 3 | var router = express.Router(); 4 | var mysql = require('mysql'); 5 | var $sql = require('../sqlMap'); 6 | // 连接数据库 7 | var conn = mysql.createConnection(models.mysql); 8 | conn.connect(); 9 | 10 | //中间件 11 | var jsonWrite = function(res, ret) { 12 | if(typeof ret === 'undefined') { 13 | res.send('err') 14 | } else { 15 | res.json(ret); 16 | // res.send('ok') 17 | } 18 | }; 19 | // 增加用户接口 20 | router.post('/addUser', (req, res) => { 21 | var sql_name = $sql.user.select_name 22 | var sql = $sql.user.add; 23 | var params = req.body; 24 | console.log(params); 25 | conn.query(sql_name,params.username,function(err,result) { 26 | if(err) { 27 | console.log(err) 28 | } 29 | if(result[0]===undefined) { 30 | conn.query(sql,[params.username,params.password], function(err, result) { 31 | if(err) { 32 | console.log(err) 33 | } 34 | if(result) { 35 | jsonWrite(res, result) 36 | } 37 | }) 38 | }else { 39 | res.send('-1') 40 | } 41 | }) 42 | 43 | }); 44 | 45 | //查找用户接口 46 | router.post('/selectUser', (req,res) => { 47 | var sql_name = $sql.user.select_name; 48 | var sql_password = $sql.user.select_password; 49 | var params = req.body; 50 | conn.query(sql_name,params.username,function(err, result) { 51 | if(err) { 52 | console.log(err) 53 | } 54 | if(result[0]===undefined) { 55 | res.send('-1') 56 | }else { 57 | conn.query(sql_password,params.password, function(err, result) { 58 | if(err) { 59 | console.log(err) 60 | } 61 | if(result[0]===undefined) { 62 | res.send('0') 63 | }else { 64 | // Set-Cookie 65 | // 如果是https环境必须添加 { secure: true } 66 | res.cookie('isAuth', 1, {maxAge: 7 * 24 * 60 * 1000, secure: true, signed: true}); 67 | jsonWrite(res, result); 68 | } 69 | }) 70 | } 71 | }) 72 | }); 73 | 74 | //修改密码 目前未提供页面,可以用接口测试工具测试 75 | router.put('/updateUser', (req,res) => { 76 | var sql_update = $sql.user.update_password; 77 | var params = req.body; 78 | if(req.cookies.isAuth){ 79 | conn.query(sql_update,[params.password,params.id], function(err, result) { 80 | if(err) { 81 | console.log(err) 82 | } 83 | if(result[0]===undefined) { 84 | res.send('0') 85 | }else { 86 | jsonWrite(res, result); 87 | } 88 | }) 89 | }else { 90 | res.send('no auth') 91 | } 92 | }); 93 | 94 | module.exports = router; 95 | -------------------------------------------------------------------------------- /server/db.js: -------------------------------------------------------------------------------- 1 | var MySQL = require('mysql'); 2 | var $sql = require('./sqlMap'); 3 | 4 | //数据库连接配置 5 | var create_mysql={host:'localhost', user:'root', password:'123456',}; 6 | var mysql = {...create_mysql, database:'vue_node_mysql_learning', port: '3306'}; 7 | 8 | process.on('uncaughtException', function(err){ 9 | // 如果数据库vue_node_mysql_learning不存在时,创建数据库 10 | if(err.errno === 1049){ 11 | var conn = MySQL.createConnection(create_mysql); 12 | MySQL.createConnection(mysql).query($sql.base.create,function(err, result){ 13 | console.error('创建数据库',err,result); 14 | conn.query($sql.user.create_user,'',function(err, result){ 15 | if(!err){ 16 | conn.query($sql.user.add,["admin","123456"], function(err, result){ 17 | }) 18 | } 19 | }); 20 | 21 | }) 22 | 23 | 24 | } 25 | }); 26 | 27 | module.exports = { 28 | mysql 29 | }; 30 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | // node 后端服务器 2 | const userApi = require('./api/userApi'); 3 | const listApi = require('./api/listmore'); 4 | 5 | const bodyParser = require('body-parser'); 6 | const cookieParser = require('cookie-parser'); 7 | const express = require('express'); 8 | const app = express(); 9 | app.use(bodyParser.json()); 10 | app.use(bodyParser.urlencoded({extended: false})); 11 | app.use(cookieParser("secret_password")); 12 | 13 | //cookie验证 14 | app.use(function (req, res, next) { 15 | console.log("多个cookies",req.cookies); //Cookie: name1=value1; name2=value2 16 | console.log("单个cookie",req.cookie); //Cookie: name=value 17 | console.log("加密后的cookie",req.signedCookies); 18 | if(req.signedCookies.isAuth === "1"){ 19 | // 验证拦截 20 | } 21 | return next(); 22 | }); 23 | 24 | // 后端api路由 25 | app.use('/api/user', userApi); 26 | app.use('/api/list', listApi); 27 | // 监听端口 28 | // app.listen(3000); 29 | // 添加https环境 30 | const certOptions = require('../https') 31 | const https = require('https'); 32 | https.createServer(certOptions, app).listen(3000); 33 | 34 | 35 | console.log('success listen at port:3000......'); 36 | -------------------------------------------------------------------------------- /server/sqlMap.js: -------------------------------------------------------------------------------- 1 | // sql语句 2 | var sqlMap = { 3 | // 自动创建数据表 4 | base: { 5 | create: 'create database vue_node_mysql_learning' 6 | }, 7 | // 用户 8 | user: { 9 | add: 'insert into user( username , password) values ( ?, ?)', 10 | select_name: 'SELECT * from user where username = ?', 11 | select_password : 'SELECT * from user where password = ?', 12 | update_password : 'update user set password = ? where id = ?', 13 | //脚步创建数据库表 14 | create_user:`CREATE TABLE user( 15 | id INT NOT NULL AUTO_INCREMENT, 16 | username VARCHAR(100) NOT NULL, 17 | password VARCHAR(40) NOT NULL, 18 | PRIMARY KEY ( id ) 19 | )ENGINE=InnoDB DEFAULT CHARSET=utf8;`, 20 | } 21 | }; 22 | module.exports = sqlMap; 23 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 48 | 49 | 50 | 88 | -------------------------------------------------------------------------------- /src/assets/js/cookie.js: -------------------------------------------------------------------------------- 1 | /*用export把方法暴露出来*/ 2 | /*设置cookie*/ 3 | export function setCookie(c_name,value,expire) { 4 | var date=new Date() 5 | date.setSeconds(date.getSeconds()+expire) 6 | document.cookie=c_name+ "="+escape(value)+"; expires="+date.toGMTString() 7 | //console.log(document.cookie) 8 | } 9 | 10 | /*获取cookie*/ 11 | export function getCookie(c_name){ 12 | if (document.cookie.length>0){ 13 | let c_start=document.cookie.indexOf(c_name + "=") 14 | if (c_start!=-1){ 15 | c_start=c_start + c_name.length+1 16 | let c_end=document.cookie.indexOf(";",c_start) 17 | if (c_end==-1) c_end=document.cookie.length 18 | return unescape(document.cookie.substring(c_start,c_end)) 19 | } 20 | } 21 | return "" 22 | } 23 | 24 | /*删除cookie*/ 25 | export function delCookie(c_name){ 26 | setCookie(c_name, "", -1) 27 | } -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1uokun/vue-node-mysql/3015d946046b9cd7aefbcd8d577750687175294e/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/Hello.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 105 | 106 | 107 | 108 | 166 | -------------------------------------------------------------------------------- /src/components/foot.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 32 | 33 | -------------------------------------------------------------------------------- /src/components/head.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/login.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 115 | 147 | -------------------------------------------------------------------------------- /src/components/mine.vue: -------------------------------------------------------------------------------- 1 | 163 | 164 | 200 | 201 | -------------------------------------------------------------------------------- /src/components/search.vue: -------------------------------------------------------------------------------- 1 | 170 | 171 | 240 | 241 | 242 | 385 | -------------------------------------------------------------------------------- /src/components/user.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 91 | 92 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import router from './router' 4 | 5 | import axios from 'axios' 6 | Vue.prototype.$http = axios; 7 | import store from './vuex/store' 8 | new Vue({ 9 | el: '#app', 10 | router, 11 | store, 12 | template: '', 13 | components: { App }, 14 | }) 15 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Hello from '@/components/Hello' 4 | import Search from '@/components/Search' 5 | import User from '@/components/User' 6 | import Login from '@/components/login' 7 | import Mine from '@/components/mine' 8 | 9 | Vue.use(Router) 10 | 11 | export default new Router({ 12 | mode:'history', 13 | routes: [ 14 | { 15 | path: '/', 16 | name: 'Hello', 17 | component: Hello 18 | }, 19 | { 20 | path: '/search', 21 | name: 'Search', 22 | component: Search 23 | }, 24 | { 25 | path: '/user', 26 | name: 'User', 27 | component: User 28 | }, 29 | { 30 | path: '/login', 31 | name: 'Login', 32 | component: Login 33 | }, 34 | { 35 | path: '/mine', 36 | name: 'Mine', 37 | component: Mine 38 | } 39 | ] 40 | }) 41 | -------------------------------------------------------------------------------- /src/vuex/actions.js: -------------------------------------------------------------------------------- 1 | 2 | import * as types from './mutation-types' 3 | 4 | export default { 5 | history_record({ commit },search) { 6 | commit(types.HISTORY_RECORD,search) 7 | }, 8 | history_read({ commit }) { 9 | commit(types.HISTORY_READ) 10 | }, 11 | history_delete({ commit },index) { 12 | commit(types.HISTORY_DELETE,index) 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /src/vuex/mutation-types.js: -------------------------------------------------------------------------------- 1 | 2 | export const HISTORY_RECORD = 'history_record' 3 | export const HISTORY_READ = 'history_read' 4 | export const HISTORY_DELETE = 'history_delete' 5 | 6 | -------------------------------------------------------------------------------- /src/vuex/mutations.js: -------------------------------------------------------------------------------- 1 | import * as types from './mutation-types' 2 | 3 | export default { 4 | //搜索历史记录 5 | [types.HISTORY_RECORD] (state,search) { 6 | state.history.push(search) 7 | //提示:在history中删除重复数据 8 | Array.prototype.del = function() { 9 | var a = {}, c = [], l = this.length; 10 | for (var i = 0; i < l; i++) { 11 | var b = this[i]; 12 | var d = (typeof b) + b; 13 | if (a[d] === undefined) { 14 | c.push(b); 15 | a[d] = 1; 16 | } 17 | } 18 | return c; 19 | } 20 | state.history = state.history.del() 21 | window.localStorage.setItem('HISTORY_RECORD',JSON.stringify(state.history)); 22 | }, 23 | //读取搜索历史记录 24 | [types.HISTORY_READ](state) { 25 | if (window.localStorage.getItem("HISTORY_RECORD")) { 26 | state.history=JSON.parse(window.localStorage.getItem('HISTORY_RECORD')); 27 | }else state.history=[] 28 | }, 29 | //删除搜索历史记录 30 | [types.HISTORY_DELETE](state,index) { 31 | state.history.splice(index,1) 32 | window.localStorage.setItem('HISTORY_RECORD',JSON.stringify(state.history)); 33 | }, 34 | } -------------------------------------------------------------------------------- /src/vuex/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import mutations from './mutations' 4 | import actions from './actions' 5 | import {getCookie} from '../assets/js/cookie.js'; 6 | Vue.use(Vuex) 7 | 8 | const state = { 9 | citys:[ 10 | { 11 | "cityList":["北京","上海","广州","深圳","成都","杭州"], 12 | "name":"热门城市", 13 | "nameStr":"热门城市" 14 | }, 15 | { 16 | "cityList":["安庆","安阳","保定","北京","包头","长春","成都","重庆","长沙","常州","郴州","沧州","东莞","大连","东营","佛山","阜阳","福州","抚州"], 17 | "name":"", 18 | "nameStr":"ABCDEF" 19 | }, 20 | { 21 | "cityList":["桂林","贵阳","广州","赣州","邯郸","哈尔滨","合肥","呼和浩特","海口","衡水","河源","杭州","惠州","湖州","金华","九江","吉林","江门","济南","济宁","嘉兴","荆州"], 22 | "name":"", 23 | "nameStr":"GHIJ" 24 | }, 25 | { 26 | "cityList":["昆明","聊城","廊坊","丽水","拉萨","临沂","洛阳","连云港","兰州","柳州","泸州","马鞍山","茂名","绵阳","梅州","宁波","南昌","南京","南宁","南通","南阳"], 27 | "name":"", 28 | "nameStr":"KLMN" 29 | }, 30 | { 31 | "cityList":["平顶山","莆田","濮阳","青岛","秦皇岛","清远","泉州","衢州","日照"], 32 | "name":"", 33 | "nameStr":"OPQR" 34 | }, 35 | { 36 | "cityList":["上海","石家庄","汕头","汕尾","绍兴","沈阳","三亚","深圳","苏州","宿州","泰安","天津","唐山","太原","台州"], 37 | "name":"", 38 | "nameStr":"STUV" 39 | }, 40 | { 41 | "cityList":["潍坊","武汉","芜湖","威海","乌鲁木齐","无锡","温州","西安","孝感","香港特别行政区","厦门","西宁","新乡","咸阳","襄阳","徐州","宜宾","银川","盐城","宜昌","烟台","扬州","淄博","珠海","镇江","湛江","肇庆","中山","遵义","郑州","漳州","株洲"], 42 | "name":"", 43 | "nameStr":"WXYZ" 44 | } 45 | ], 46 | positionName:['产品经理','Java','运营','Android','PHP','UI','IOS','编辑','BD'], 47 | salary:['没有要求','2k以下','2k-5k','5k-10k','10k-15k','15k-25k','25k-50k','50k以上'], 48 | positionName_selected:getCookie('p'), 49 | city_selected:getCookie('c'), 50 | salary_selected:getCookie('s'), 51 | history:[], //搜索历史 52 | search_result:[], //搜索列表 53 | search_data: [], //搜索数据(这里用的假数据而不是通过接口获取,可以练习vuex) 54 | filterSearch:[] 55 | } 56 | export default new Vuex.Store({ 57 | state, 58 | mutations, 59 | actions 60 | }); 61 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1uokun/vue-node-mysql/3015d946046b9cd7aefbcd8d577750687175294e/static/.gitkeep -------------------------------------------------------------------------------- /static/QQ截图20180529230333.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1uokun/vue-node-mysql/3015d946046b9cd7aefbcd8d577750687175294e/static/QQ截图20180529230333.png -------------------------------------------------------------------------------- /static/QQ截图20180529230411.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1uokun/vue-node-mysql/3015d946046b9cd7aefbcd8d577750687175294e/static/QQ截图20180529230411.png -------------------------------------------------------------------------------- /static/back-info.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1uokun/vue-node-mysql/3015d946046b9cd7aefbcd8d577750687175294e/static/back-info.jpg -------------------------------------------------------------------------------- /static/icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1uokun/vue-node-mysql/3015d946046b9cd7aefbcd8d577750687175294e/static/icon2.png -------------------------------------------------------------------------------- /static/theme.scss: -------------------------------------------------------------------------------- 1 | $base_color: #00b38a; 2 | $dim_font_color: #c2cfcc; 3 | $dim_bg_color: #f5f5f5; 4 | $btn_bg_color: #e7f3f0; 5 | $border_color:#e8e8e8; 6 | @mixin foot_icon($x,$y) { 7 | background:url(/static/icon2.png) no-repeat $x $y; 8 | background-size: 250px 250px; 9 | } 10 | %nav { 11 | width:100%; 12 | height: 45px; 13 | line-height: 45px; 14 | text-align: center; 15 | } 16 | %btn { 17 | color:$dim_font_color; 18 | background-color: $dim_bg_color; 19 | text-align: center; 20 | } 21 | %btn_selected { 22 | color: $base_color; 23 | background-color: $btn_bg_color; 24 | } 25 | %after_clearfix { 26 | content: "."; 27 | display: block; 28 | height: 0; 29 | clear: both; 30 | visibility: hidden; 31 | } 32 | 33 | //--------------公共样式---------------------------------- 34 | .pull-left{float: left;} 35 | .pull-right{float: right;} 36 | .center {text-align: center;width: 125px;margin: 0 auto;} 37 | .selected{background-color: #e7f3f0;} 38 | //-------------------------------------------------------- 39 | 40 | 41 | //---------公司信息list--------------- 42 | .list { 43 | .list-item { 44 | padding: 14px; 45 | border-bottom: 1px solid #e8e8e8; 46 | &:after { 47 | content: "."; 48 | display: block; 49 | height: 0; 50 | clear: both; 51 | visibility: hidden; 52 | } 53 | } 54 | .item-logo { 55 | display: inline-block; 56 | float: left; 57 | width: 60px; 58 | height: 60px; 59 | } 60 | .item-desc { 61 | margin-left: 70px; 62 | height: 62px; 63 | color: #333; 64 | } 65 | .item-title { 66 | font-size: 1.6rem; 67 | margin-bottom: 6px; 68 | width: 80%; 69 | overflow: hidden; 70 | text-overflow: ellipsis; 71 | white-space: nowrap; 72 | } 73 | .item-pos { 74 | font-size: 1.2rem; 75 | float: left; 76 | width: 60%; 77 | display: inline-block; 78 | overflow: hidden; 79 | text-overflow: ellipsis; 80 | white-space: nowrap; 81 | } 82 | .item-info { 83 | margin-bottom: 6px; 84 | width: 100%; 85 | height: 15px; 86 | line-height: 15px; 87 | } 88 | .item-time { 89 | font-size: 1rem; 90 | color: #888; 91 | } 92 | .item-salary { 93 | font-size: 1.6rem; 94 | color: $base_color; 95 | float: right; 96 | } 97 | .list-more { 98 | height: 50px; 99 | line-height: 50px; 100 | text-align: center; 101 | font-size: 1.5rem; 102 | background-color: $dim_bg_color; 103 | } 104 | } 105 | 106 | //---------city-list----------- 107 | .city-list { 108 | width: 100%; 109 | height:auto; 110 | li{ 111 | width: (100%/3); 112 | height: 60px; 113 | float: left; 114 | line-height: 60px; 115 | text-align: center; 116 | font-size: 1.5rem; 117 | color: #333; 118 | min-width: 100px; 119 | } 120 | &:after{ 121 | @extend %after_clearfix; 122 | } 123 | } 124 | .Acronym { 125 | border-bottom: 1px solid $border_color; 126 | border-top: 1px solid $border_color; 127 | width:100%; 128 | height: 25px; 129 | line-height: 25px; 130 | font-size: 1.2rem; 131 | color: #888; 132 | span{padding-left: 25px;} 133 | } 134 | #city-table:last-child{ 135 | margin-bottom:60px; 136 | border-bottom: 1px solid $border_color; 137 | } 138 | 139 | 140 | //-------my-head-------- 141 | #header{ 142 | @extend %nav; 143 | background-color:$base_color; 144 | color: #fff; 145 | font-size: 1.8rem; 146 | .left{ 147 | position: absolute; 148 | left: 0; 149 | top: 0; 150 | height: 100%; 151 | width: 40px; 152 | } 153 | .glyphicon-corner { 154 | width: 20px; 155 | height: 20px; 156 | margin: 13px auto 0 20px; 157 | @include foot_icon(-1.5px, -20.5px); 158 | display: inline-block; 159 | } 160 | } 161 | 162 | //-------normalize-------- 163 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}/*# sourceMappingURL=normalize.min.css.map */ 164 | --------------------------------------------------------------------------------