├── back-end
├── log
│ └── .keep
├── tmp
│ └── .keep
├── storage
│ └── .keep
├── vendor
│ └── .keep
├── lib
│ ├── tasks
│ │ └── .keep
│ └── json_web_token.rb
├── public
│ ├── index.html
│ ├── favicon.ico
│ └── assets
│ │ ├── css
│ │ └── app.dc330d96.css
│ │ └── js
│ │ ├── about.6dc12a1b.js
│ │ ├── app.46bf1d3b.js
│ │ └── chunk-vendors.a8c4cf9f.js
├── test
│ ├── fixtures
│ │ ├── .keep
│ │ └── files
│ │ │ └── .keep
│ ├── mailers
│ │ └── .keep
│ ├── models
│ │ ├── .keep
│ │ ├── user_test.rb
│ │ └── product_test.rb
│ ├── controllers
│ │ └── .keep
│ ├── integration
│ │ └── .keep
│ └── test_helper.rb
├── .ruby-version
├── app
│ ├── models
│ │ ├── concerns
│ │ │ └── .keep
│ │ ├── product.rb
│ │ ├── application_record.rb
│ │ └── user.rb
│ ├── controllers
│ │ ├── concerns
│ │ │ ├── .keep
│ │ │ └── exception_handler.rb
│ │ └── api
│ │ │ └── v1
│ │ │ ├── users_controller.rb
│ │ │ ├── sessions_controller.rb
│ │ │ ├── application_controller.rb
│ │ │ └── products_controller.rb
│ ├── views
│ │ └── layouts
│ │ │ ├── mailer.text.erb
│ │ │ └── mailer.html.erb
│ ├── jobs
│ │ └── application_job.rb
│ ├── channels
│ │ └── application_cable
│ │ │ ├── channel.rb
│ │ │ └── connection.rb
│ └── mailers
│ │ └── application_mailer.rb
├── .powenv
├── bin
│ ├── bundle
│ ├── rake
│ ├── rails
│ ├── spring
│ ├── update
│ └── setup
├── config
│ ├── spring.rb
│ ├── environment.rb
│ ├── initializers
│ │ ├── mime_types.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── application_controller_renderer.rb
│ │ ├── backtrace_silencers.rb
│ │ ├── wrap_parameters.rb
│ │ ├── cors.rb
│ │ └── inflections.rb
│ ├── boot.rb
│ ├── cable.yml
│ ├── routes.rb
│ ├── credentials.yml.enc
│ ├── database.yml
│ ├── locales
│ │ └── en.yml
│ ├── storage.yml
│ ├── puma.rb
│ ├── application.rb
│ └── environments
│ │ ├── test.rb
│ │ ├── development.rb
│ │ └── production.rb
├── config.ru
├── Rakefile
├── db
│ ├── migrate
│ │ ├── 20181112023303_create_users.rb
│ │ └── 20181109040318_create_products.rb
│ ├── seeds.rb
│ └── schema.rb
├── README.md
├── .gitignore
├── Gemfile
└── Gemfile.lock
├── _config.yml
├── front-end
├── .browserslistrc
├── src
│ ├── components
│ │ ├── Footer.vue
│ │ ├── Header.vue
│ │ ├── ProductList.vue
│ │ └── ProductForm.vue
│ ├── store.js
│ ├── main.js
│ ├── views
│ │ ├── Home.vue
│ │ ├── Add.vue
│ │ ├── Edit.vue
│ │ ├── About.vue
│ │ ├── Login.vue
│ │ └── Register.vue
│ ├── router.js
│ ├── App.vue
│ └── API.js
├── postcss.config.js
├── public
│ ├── favicon.ico
│ └── index.html
├── .gitignore
├── .eslintrc.js
├── README.md
├── package.json
└── vue.config.js
├── .gitignore
└── README.md
/back-end/log/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/tmp/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/storage/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/vendor/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/lib/tasks/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/public/index.html:
--------------------------------------------------------------------------------
1 | aa
--------------------------------------------------------------------------------
/back-end/test/fixtures/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/test/mailers/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/test/models/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/test/controllers/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/test/integration/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-slate
--------------------------------------------------------------------------------
/back-end/.ruby-version:
--------------------------------------------------------------------------------
1 | ruby-2.6.3
2 |
--------------------------------------------------------------------------------
/back-end/app/models/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/test/fixtures/files/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/back-end/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/front-end/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not ie <= 8
4 |
--------------------------------------------------------------------------------
/back-end/app/models/product.rb:
--------------------------------------------------------------------------------
1 | class Product < ApplicationRecord
2 | end
3 |
--------------------------------------------------------------------------------
/back-end/.powenv:
--------------------------------------------------------------------------------
1 | source /usr/local/share/chruby/chruby.sh
2 | chruby $(cat .ruby-version)
--------------------------------------------------------------------------------
/back-end/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 |
--------------------------------------------------------------------------------
/front-end/src/components/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .vscode
3 | *.suo
4 | *.ntvs*
5 | *.njsproj
6 | *.sln
7 | *.sw*
8 | .DS_Store
9 | .git
--------------------------------------------------------------------------------
/front-end/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/back-end/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/back-end/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huobazi/vuejs-with-rails-api-and-jwt-example/HEAD/back-end/public/favicon.ico
--------------------------------------------------------------------------------
/front-end/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huobazi/vuejs-with-rails-api-and-jwt-example/HEAD/front-end/public/favicon.ico
--------------------------------------------------------------------------------
/back-end/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/back-end/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/back-end/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/back-end/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/back-end/config/spring.rb:
--------------------------------------------------------------------------------
1 | %w[
2 | .ruby-version
3 | .rbenv-vars
4 | tmp/restart.txt
5 | tmp/caching-dev.txt
6 | ].each { |path| Spring.watch(path) }
7 |
--------------------------------------------------------------------------------
/back-end/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative 'config/environment'
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/back-end/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/back-end/test/models/user_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class UserTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/back-end/test/models/product_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ProductTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/back-end/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ApplicationRecord
2 | has_secure_password
3 |
4 | validates :username, uniqueness: true, presence: true
5 | validates :password_confirmation, presence: true
6 | end
7 |
--------------------------------------------------------------------------------
/back-end/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/back-end/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/front-end/src/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | Vue.use(Vuex)
5 |
6 | export default new Vuex.Store({
7 | state: {
8 |
9 | },
10 | mutations: {
11 |
12 | },
13 | actions: {
14 |
15 | }
16 | })
17 |
--------------------------------------------------------------------------------
/back-end/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/back-end/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: back-end_production
11 |
--------------------------------------------------------------------------------
/back-end/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/back-end/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | require_relative '../config/boot'
8 | require 'rake'
9 | Rake.application.run
10 |
--------------------------------------------------------------------------------
/back-end/db/migrate/20181112023303_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :users do |t|
4 | t.string :username
5 | t.string :password_digest
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/back-end/public/assets/css/app.dc330d96.css:
--------------------------------------------------------------------------------
1 | #app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50}#nav{padding:30px}#nav a{font-weight:700;color:#2c3e50}#nav a.router-link-exact-active{color:#42b983}
--------------------------------------------------------------------------------
/back-end/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/back-end/db/migrate/20181109040318_create_products.rb:
--------------------------------------------------------------------------------
1 | class CreateProducts < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :products do |t|
4 | t.string :name
5 | t.string :description
6 | t.decimal :price
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/back-end/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | APP_PATH = File.expand_path('../config/application', __dir__)
8 | require_relative '../config/boot'
9 | require 'rails/commands'
10 |
--------------------------------------------------------------------------------
/back-end/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/front-end/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw*
22 |
--------------------------------------------------------------------------------
/back-end/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV['RAILS_ENV'] ||= 'test'
2 | require_relative '../config/environment'
3 | require 'rails/test_help'
4 |
5 | class ActiveSupport::TestCase
6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
7 | fixtures :all
8 |
9 | # Add more helper methods to be used by all tests here...
10 | end
11 |
--------------------------------------------------------------------------------
/back-end/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | namespace :api do
3 | namespace :v1 do
4 | resources :products
5 | post "users", to: "users#create"
6 | post "sessions", to: "sessions#create"
7 | end
8 | end
9 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
10 | end
11 |
--------------------------------------------------------------------------------
/back-end/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
7 | # Character.create(name: 'Luke', movie: movies.first)
8 |
--------------------------------------------------------------------------------
/front-end/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import router from './router'
4 | import store from './store'
5 |
6 | import ElementUI from 'element-ui';
7 | import 'element-ui/lib/theme-chalk/index.css';
8 | Vue.use(ElementUI);
9 |
10 | Vue.config.productionTip = false
11 |
12 | new Vue({
13 | router,
14 | store,
15 | render: function (h) { return h(App) }
16 | }).$mount('#app')
17 |
--------------------------------------------------------------------------------
/front-end/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | 'eslint:recommended'
9 | ],
10 | rules: {
11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
13 | },
14 | parserOptions: {
15 | parser: 'babel-eslint'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/back-end/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/back-end/public/assets/js/about.6dc12a1b.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["about"],{f820:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},s=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"about"},[n("h1",[t._v("This is an about page")])])}],u=n("2877"),i={},o=Object(u["a"])(i,a,s,!1,null,null,null);o.options.__file="About.vue";e["default"]=o.exports}}]);
--------------------------------------------------------------------------------
/back-end/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | a7wTtOWD2d6IoV2J6OnDRNnJig/YJ1/0AtHLKHg3iUmPPvTFFWoE5oONQmq+LvFTqYBifjHhjFvP0FBazWfdKXnpk0Gz600esDg+TMc2qjVmzmpXB37mHJ/kWhr1Q5yBbPZXBOrxuJ7U0AKkfUc+u7Bf7awAKYBN00vPGXyL3mMuZ964TlXsarYpA9I/x7jrwNNFbk80W8950un6IdDdl0zpuhpWOWKrnxvrs9Or9TV+0cO+0fkeNpjhlPNxbWzxob16tcp6YT+/defoYmcx/JJSMhAJAeXFRtVSgOaJhQWF3Bg+6SOfo2aJG6ywBPf8LQPaRVALc2BaUPhTRaOuJhQu8iI0ULF+dW6MeNDafFf8xGY2bWgdtDywHCq80FauW633s7ej9C92NkjJfE2es04E/WJDoRqBdKty--qq6TQAIkv1pBkwkt--8jRCFyQN1R5P6M/+H3IOvw==
--------------------------------------------------------------------------------
/back-end/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | This README would normally document whatever steps are necessary to get the
4 | application up and running.
5 |
6 | Things you may want to cover:
7 |
8 | * Ruby version
9 |
10 | * System dependencies
11 |
12 | * Configuration
13 |
14 | * Database creation
15 |
16 | * Database initialization
17 |
18 | * How to run the test suite
19 |
20 | * Services (job queues, cache servers, search engines, etc.)
21 |
22 | * Deployment instructions
23 |
24 | * ...
25 |
--------------------------------------------------------------------------------
/front-end/README.md:
--------------------------------------------------------------------------------
1 | # front-end
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Run your tests
19 | ```
20 | npm run test
21 | ```
22 |
23 | ### Lints and fixes files
24 | ```
25 | npm run lint
26 | ```
27 |
28 | ### Customize configuration
29 | See [Configuration Reference](https://cli.vuejs.org/config/).
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 前后端分离示例
2 |
3 | - vuejs 做前端
4 | - rails-api 做后端
5 | - JWT 做验证
6 |
7 | ## clone the repo
8 | ```
9 | git clone https://github.com/huobazi/vuejs-with-rails-api-and-jwt-example.git
10 | ```
11 |
12 | ## run the backend
13 | ```
14 | cd vuejs-with-rails-api-example/back-end
15 |
16 | bundle
17 |
18 | bundle exec rails db:migrate
19 |
20 | bundle exec rails s
21 | ```
22 |
23 | ## run the frotend
24 | ```
25 | cd vuejs-with-rails-api-example/front-end
26 |
27 | npm install
28 |
29 | npm run serve
30 | ```
31 |
--------------------------------------------------------------------------------
/back-end/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/front-end/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
26 |
--------------------------------------------------------------------------------
/back-end/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" }
12 | if spring
13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
14 | gem 'spring', spring.version
15 | require 'spring/binstub'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/front-end/src/views/Add.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
26 |
--------------------------------------------------------------------------------
/back-end/config/initializers/cors.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Avoid CORS issues when API is called from the frontend app.
4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
5 |
6 | # Read more: https://github.com/cyu/rack-cors
7 |
8 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do
9 | # allow do
10 | # origins 'example.com'
11 | #
12 | # resource '*',
13 | # headers: :any,
14 | # methods: [:get, :post, :put, :patch, :delete, :options, :head]
15 | # end
16 | # end
17 |
--------------------------------------------------------------------------------
/front-end/src/views/Edit.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
26 |
--------------------------------------------------------------------------------
/front-end/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | front-end
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/back-end/app/controllers/api/v1/users_controller.rb:
--------------------------------------------------------------------------------
1 | module Api
2 | module V1
3 | class UsersController < ApplicationController
4 | skip_before_action :authenticate_request!, only: [:create]
5 |
6 | # POST /register
7 | def create
8 | @user = User.create(user_params)
9 | if @user.save
10 | response = {message: "User created successfully"}
11 | render json: response, status: :created
12 | else
13 | render json: @user.errors
14 | end
15 | end
16 |
17 | private
18 |
19 | def user_params
20 | params.require(:user).permit(:username, :password, :password_confirmation)
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/back-end/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/back-end/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/back-end/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore the default SQLite database.
11 | /db/*.sqlite3
12 | /db/*.sqlite3-journal
13 |
14 | # Ignore all logfiles and tempfiles.
15 | /log/*
16 | /tmp/*
17 | !/log/.keep
18 | !/tmp/.keep
19 |
20 | # Ignore uploaded files in development
21 | /storage/*
22 | !/storage/.keep
23 |
24 | .byebug_history
25 |
26 | # Ignore master key for decrypting credentials and more.
27 | /config/master.key
28 |
--------------------------------------------------------------------------------
/back-end/lib/json_web_token.rb:
--------------------------------------------------------------------------------
1 | class JsonWebToken
2 | ALGORITHM = "HS256"
3 |
4 | class << self
5 | def token_secret
6 | ENV["JWT_AUTH_SECRET"] || Rails.application.credentials.secret_key_base
7 | end
8 |
9 | def encode(paylod, exp = 1.hours.from_now)
10 | paylod[:exp] = exp.to_i
11 | JWT.encode(paylod, token_secret, ALGORITHM)
12 | end
13 |
14 | def decode(token)
15 | body = JWT.decode(token, token_secret, false, {algorithm: ALGORITHM})[0]
16 | return HashWithIndifferentAccess.new body
17 | rescue JWT::ExpiredSignature, JWT::VerificationError => e
18 | raise ExceptionHandler::ExpiredSignature, e.message
19 | rescue JWT::DecodeError, JWT::VerificationError => e
20 | raise ExceptionHandler::DecodeError, e.message
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/back-end/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | puts "\n== Updating database =="
21 | system! 'bin/rails db:migrate'
22 |
23 | puts "\n== Removing old logs and tempfiles =="
24 | system! 'bin/rails log:clear tmp:clear'
25 |
26 | puts "\n== Restarting application server =="
27 | system! 'bin/rails restart'
28 | end
29 |
--------------------------------------------------------------------------------
/back-end/app/controllers/api/v1/sessions_controller.rb:
--------------------------------------------------------------------------------
1 | module Api
2 | module V1
3 | class SessionsController < ApplicationController
4 | skip_before_action :authenticate_request!, only: [:create]
5 |
6 | # POST /login
7 | def create
8 | user = User.find_by(username: user_params[:username])
9 | if user && user.authenticate(params[:password])
10 | render json: json_to_render(user), status: :created
11 | else
12 | render json: {message: ["用户名或密码错误"]}
13 | end
14 | end
15 |
16 | private
17 |
18 | def json_to_render(user)
19 | return nil unless user and user.id
20 | {
21 | auth_token: JsonWebToken.encode({user_id: user.id}),
22 | user: {id: user.id, username: user.username},
23 | }
24 | end
25 |
26 | private
27 |
28 | def user_params
29 | params.permit(:username, :password)
30 | end
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/front-end/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "front-end",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^0.18.0",
12 | "element-ui": "^2.13.2",
13 | "qs": "^6.5.2",
14 | "vue": "^2.6.12",
15 | "vue-router": "^3.4.3",
16 | "vuex": "^3.5.1"
17 | },
18 | "devDependencies": {
19 | "@vue/cli-plugin-eslint": "^3.12.1",
20 | "@vue/cli-service": "^3.12.1",
21 | "babel-cli": "^6.26.0",
22 | "babel-eslint": "^10.1.0",
23 | "babel-polyfill": "^6.26.0",
24 | "babel-preset-env": "^1.7.0",
25 | "css-loader": "^1.0.1",
26 | "eslint": "^5.16.0",
27 | "eslint-plugin-vue": "^5.2.3",
28 | "node-sass": "^4.14.1",
29 | "sass-loader": "^7.3.1",
30 | "style-loader": "^0.23.1",
31 | "vue-template-compiler": "^2.6.12",
32 | "webpack": "^4.44.1"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/back-end/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at http://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/back-end/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a starting point to setup your application.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # puts "\n== Copying sample files =="
21 | # unless File.exist?('config/database.yml')
22 | # cp 'config/database.yml.sample', 'config/database.yml'
23 | # end
24 |
25 | puts "\n== Preparing database =="
26 | system! 'bin/rails db:setup'
27 |
28 | puts "\n== Removing old logs and tempfiles =="
29 | system! 'bin/rails log:clear tmp:clear'
30 |
31 | puts "\n== Restarting application server =="
32 | system! 'bin/rails restart'
33 | end
34 |
--------------------------------------------------------------------------------
/back-end/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/back-end/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2018_11_12_023303) do
14 |
15 | create_table "products", force: :cascade do |t|
16 | t.string "name"
17 | t.string "description"
18 | t.decimal "price"
19 | t.datetime "created_at", null: false
20 | t.datetime "updated_at", null: false
21 | end
22 |
23 | create_table "users", force: :cascade do |t|
24 | t.string "username"
25 | t.string "password_digest"
26 | t.datetime "created_at", null: false
27 | t.datetime "updated_at", null: false
28 | end
29 |
30 | end
31 |
--------------------------------------------------------------------------------
/back-end/app/controllers/api/v1/application_controller.rb:
--------------------------------------------------------------------------------
1 | module Api
2 | module V1
3 | class ApplicationController < ActionController::API
4 | include ExceptionHandler
5 |
6 | before_action :authenticate_request!
7 |
8 | def current_user
9 | @current_user
10 | end
11 |
12 | def logged_in?
13 | !!@current_user
14 | end
15 |
16 | protected
17 |
18 | def authenticate_request!
19 | if !http_token
20 | raise AuthenticationError, "Not Authenticated: Require X-Authorization-Token"
21 | end
22 | unless user_id_in_token?
23 | raise AuthenticationError, "Not Authenticated: Bad tok format"
24 | end
25 | @current_user = User.find(auth_token[:user_id])
26 | rescue JWT::VerificationError
27 | raise InvalidToken, "Not Authenticated: InvalidToken, Bad tok format"
28 | rescue JWT::DecodeError
29 | raise DecodeError, "Not Authenticated: DecodeError, Bad tok format"
30 | end
31 |
32 | private
33 |
34 | def http_token
35 | return request.headers["X-Authorization-Token"].split(" ").last if request.headers["X-Authorization-Token"].present?
36 | nil
37 | end
38 |
39 | def auth_token
40 | JsonWebToken.decode(http_token)
41 | end
42 |
43 | def user_id_in_token?
44 | http_token && auth_token && auth_token[:user_id].to_i
45 | end
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/back-end/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | threads threads_count, threads_count
9 |
10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
11 | #
12 | port ENV.fetch("PORT") { 3000 }
13 |
14 | # Specifies the `environment` that Puma will run in.
15 | #
16 | environment ENV.fetch("RAILS_ENV") { "development" }
17 |
18 | # Specifies the number of `workers` to boot in clustered mode.
19 | # Workers are forked webserver processes. If using threads and workers together
20 | # the concurrency of the application would be max `threads` * `workers`.
21 | # Workers do not work on JRuby or Windows (both of which do not support
22 | # processes).
23 | #
24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
25 |
26 | # Use the `preload_app!` method when specifying a `workers` number.
27 | # This directive tells Puma to first boot the application and load code
28 | # before forking the application. This takes advantage of Copy On Write
29 | # process behavior so workers use less memory.
30 | #
31 | # preload_app!
32 |
33 | # Allow puma to be restarted by `rails restart` command.
34 | plugin :tmp_restart
35 |
--------------------------------------------------------------------------------
/front-end/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 |
4 | import { WebAppAPI } from '@/API.js';
5 |
6 | import Login from '@/views/Login.vue'
7 | import Register from '@/views/Register.vue'
8 | import Home from '@/views/Home.vue'
9 | import About from '@/views/About.vue'
10 | import Add from '@/views/Add.vue'
11 | import Edit from '@/views/Edit.vue'
12 |
13 | Vue.use(Router)
14 |
15 | const router = new Router({
16 | routes: [
17 | {
18 | path: '/login',
19 | name: 'login',
20 | component: Login
21 | },
22 | {
23 | path: '/register',
24 | name: 'register',
25 | component: Register
26 | },
27 | {
28 | path: '/',
29 | name: 'home',
30 | component: Home
31 | },
32 | {
33 | path: '/about',
34 | name: 'about',
35 | component: About
36 | },
37 | {
38 | path: '/add',
39 | name: 'add',
40 | component: Add
41 | },
42 | {
43 | path: '/edit/:id',
44 | name: 'edit',
45 | component: Edit
46 | },
47 | ]
48 | });
49 |
50 |
51 | router.beforeEach((to, from, next) => {
52 | // redirect to login page if not logged in and trying to access a restricted page
53 | const publicPageNames = ['login', 'register','about'];
54 | const authRequired = !publicPageNames.includes(to.name);
55 | const currentUser = WebAppAPI.GetCurrentUser();
56 |
57 | if (authRequired && (!currentUser || currentUser.id <=0)) {
58 | return next('/login');
59 | }
60 |
61 | next();
62 | });
63 |
64 | export default router;
65 |
--------------------------------------------------------------------------------
/back-end/app/controllers/api/v1/products_controller.rb:
--------------------------------------------------------------------------------
1 | module Api
2 | module V1
3 | class ProductsController < ApplicationController
4 | before_action :set_product, only: [:show, :update, :destroy]
5 |
6 | # GET /products
7 | def index
8 | @products = Product.all
9 | render json: @products
10 | end
11 |
12 | # GET /products/1
13 | def show
14 | render json: @product
15 | end
16 |
17 | # POST /products
18 | def create
19 | @product = Product.new(product_params)
20 |
21 | if @product.save
22 | render json: @product, status: :created, location: api_v1_product_url(@product)
23 | else
24 | render json: @product.errors, status: :unprocessable_entity
25 | end
26 | end
27 |
28 | # PATCH/PUT /products/1
29 | def update
30 | if @product.update(product_params)
31 | render json: @product
32 | else
33 | render json: @product.errors, status: :unprocessable_entity
34 | end
35 | end
36 |
37 | # DELETE /products/1
38 | def destroy
39 | @product.destroy
40 | end
41 |
42 | private
43 |
44 | # Use callbacks to share common setup or constraints between actions.
45 | def set_product
46 | @product = Product.find(params[:id])
47 | end
48 |
49 | # Only allow a trusted parameter "white list" through.
50 | def product_params
51 | params.require(:product).permit(:name, :description, :price)
52 | end
53 | end
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/front-end/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
21 |
--------------------------------------------------------------------------------
/front-end/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 关于
8 |
9 | 1. 使用 vue 做前端
10 | 2. 使用 rails-api 做后端
11 | 3. 使用 JWT 做 身份验证. 使用 vue 做前端
12 | @huobazi
13 |
20 |
21 |
22 |
23 |
24 |
25 |
38 |
39 |
--------------------------------------------------------------------------------
/back-end/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative "boot"
2 |
3 | require "rails"
4 | # Pick the frameworks you want:
5 | require "active_model/railtie"
6 | require "active_job/railtie"
7 | require "active_record/railtie"
8 | require "active_storage/engine"
9 | require "action_controller/railtie"
10 | require "action_mailer/railtie"
11 | require "action_view/railtie"
12 | require "action_cable/engine"
13 | # require "sprockets/railtie"
14 | require "rails/test_unit/railtie"
15 |
16 | # Require the gems listed in Gemfile, including any gems
17 | # you've limited to :test, :development, or :production.
18 | Bundler.require(*Rails.groups)
19 |
20 | module BackEnd
21 | class Application < Rails::Application
22 | # Initialize configuration defaults for originally generated Rails version.
23 | config.load_defaults 5.2
24 |
25 | config.autoload_paths << Rails.root.join("lib")
26 |
27 | config.middleware.insert_before 0, Rack::Cors do
28 | allow do
29 | origins "*"
30 | resource "*", headers: :any, methods: [:get, :post, :put, :delete, :options]
31 | end
32 | end
33 |
34 | # Settings in config/environments/* take precedence over those specified here.
35 | # Application configuration can go into files in config/initializers
36 | # -- all .rb files in that directory are automatically loaded after loading
37 | # the framework and any gems in your application.
38 |
39 | # Only loads a smaller set of middleware suitable for API only apps.
40 | # Middleware like session, flash, cookies can be added back manually.
41 | # Skip views, helpers and assets when generating a new resource.
42 | config.api_only = true
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/back-end/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://gems.ruby-china.com'
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 |
5 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
6 | gem 'rails', '~> 5.2.1'
7 | # Use sqlite3 as the database for Active Record
8 | gem 'sqlite3'
9 |
10 | gem 'jwt', '~> 2.2', '>= 2.2.2'
11 | gem 'bcrypt', '~> 3.1', '>= 3.1.12'
12 |
13 | # Use Puma as the app server
14 | gem 'puma', '~> 3.12'
15 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
16 | # gem 'jbuilder', '~> 2.5'
17 | # Use Redis adapter to run Action Cable in production
18 | # gem 'redis', '~> 4.0'
19 | # Use ActiveModel has_secure_password
20 | # gem 'bcrypt', '~> 3.1.7'
21 |
22 | # Use ActiveStorage variant
23 | # gem 'mini_magick', '~> 4.8'
24 |
25 | # Use Capistrano for deployment
26 | # gem 'capistrano-rails', group: :development
27 |
28 | # Reduces boot times through caching; required in config/boot.rb
29 | gem 'bootsnap', require: false
30 |
31 | # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
32 | gem 'rack-cors'
33 |
34 | group :development, :test do
35 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
36 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
37 | end
38 |
39 | group :development do
40 | gem 'listen', '>= 3.0.5', '< 3.2'
41 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
42 | gem 'spring'
43 | gem 'spring-watcher-listen', '~> 2.0.0'
44 | end
45 |
46 |
47 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
48 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
49 |
--------------------------------------------------------------------------------
/back-end/app/controllers/concerns/exception_handler.rb:
--------------------------------------------------------------------------------
1 | module ExceptionHandler
2 | extend ActiveSupport::Concern
3 |
4 | class AuthenticationError < StandardError; end
5 | class MissingToken < StandardError; end
6 | class InvalidToken < StandardError; end
7 | class ExpiredSignature < StandardError; end
8 | class DecodeError < StandardError; end
9 |
10 | included do
11 | rescue_from ActiveRecord::RecordInvalid, with: :four_twenty_two
12 | rescue_from ExceptionHandler::AuthenticationError, with: :unauthorized_request
13 | rescue_from ExceptionHandler::MissingToken, with: :four_twenty_two
14 | rescue_from ExceptionHandler::InvalidToken, with: :four_twenty_two
15 | rescue_from ExceptionHandler::ExpiredSignature, with: :four_ninety_eight
16 | rescue_from ExceptionHandler::DecodeError, with: :four_zero_one
17 |
18 | rescue_from ActiveRecord::RecordNotFound do |e|
19 | render json: {message: e.message}, status: :not_found
20 | end
21 |
22 | rescue_from ActiveRecord::RecordInvalid do |e|
23 | render json: {message: e.message}, status: :unprocessable_entity
24 | end
25 | end
26 |
27 | private
28 |
29 | # JSON response with message; Status code 422 - unprocessable entity
30 | def four_twenty_two(e)
31 | render json: {message: e.message}, status: :unprocessable_entity
32 | end
33 |
34 | # JSON response with message; Status code 401 - Unauthorized
35 | def four_ninety_eight(e)
36 | render json: {message: e.message}, status: :unauthorized
37 | end
38 |
39 | # JSON response with message; Status code 401 - Unauthorized
40 | def four_zero_one(e)
41 | render json: {message: e.message}, status: :unauthorized
42 | end
43 |
44 | # JSON response with message; Status code 401 - Unauthorized
45 | def unauthorized_request(e)
46 | render json: {message: e.message}, status: :unauthorized
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/back-end/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure public file server for tests with Cache-Control for performance.
16 | config.public_file_server.enabled = true
17 | config.public_file_server.headers = {
18 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
19 | }
20 |
21 | # Show full error reports and disable caching.
22 | config.consider_all_requests_local = true
23 | config.action_controller.perform_caching = false
24 |
25 | # Raise exceptions instead of rendering exception templates.
26 | config.action_dispatch.show_exceptions = false
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 |
31 | # Store uploaded files on the local file system in a temporary directory
32 | config.active_storage.service = :test
33 |
34 | config.action_mailer.perform_caching = false
35 |
36 | # Tell Action Mailer not to deliver emails to the real world.
37 | # The :test delivery method accumulates sent emails in the
38 | # ActionMailer::Base.deliveries array.
39 | config.action_mailer.delivery_method = :test
40 |
41 | # Print deprecation notices to the stderr.
42 | config.active_support.deprecation = :stderr
43 |
44 | # Raises error for missing translations
45 | # config.action_view.raise_on_missing_translations = true
46 | end
47 |
--------------------------------------------------------------------------------
/back-end/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | # Run rails dev:cache to toggle caching.
17 | if Rails.root.join('tmp', 'caching-dev.txt').exist?
18 | config.action_controller.perform_caching = true
19 |
20 | config.cache_store = :memory_store
21 | config.public_file_server.headers = {
22 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
23 | }
24 | else
25 | config.action_controller.perform_caching = false
26 |
27 | config.cache_store = :null_store
28 | end
29 |
30 | # Store uploaded files on the local file system (see config/storage.yml for options)
31 | config.active_storage.service = :local
32 |
33 | # Don't care if the mailer can't send.
34 | config.action_mailer.raise_delivery_errors = false
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | # Print deprecation notices to the Rails logger.
39 | config.active_support.deprecation = :log
40 |
41 | # Raise an error on page load if there are pending migrations.
42 | config.active_record.migration_error = :page_load
43 |
44 | # Highlight code that triggered database queries in logs.
45 | config.active_record.verbose_query_logs = true
46 |
47 |
48 | # Raises error for missing translations
49 | # config.action_view.raise_on_missing_translations = true
50 |
51 | # Use an evented file watcher to asynchronously detect changes in source code,
52 | # routes, locales, etc. This feature depends on the listen gem.
53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
54 | end
55 |
--------------------------------------------------------------------------------
/front-end/src/components/Header.vue:
--------------------------------------------------------------------------------
1 |
20 |
21 |
84 |
--------------------------------------------------------------------------------
/front-end/src/components/ProductList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
83 |
--------------------------------------------------------------------------------
/front-end/src/views/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 登陆
14 | 重置
15 |
16 | 没有账户?去注册
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
84 |
85 |
87 |
--------------------------------------------------------------------------------
/front-end/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // Project deployment base
3 | // By default we assume your app will be deployed at the root of a domain,
4 | // e.g. https://www.my-app.com/
5 | // If your app is deployed at a sub-path, you will need to specify that
6 | // sub-path here. For example, if your app is deployed at
7 | // https://www.foobar.com/my-app/
8 | // then change this to '/my-app/'
9 |
10 | // EXAMPLE
11 | // https://www.example.com/project/v1.1/index.html
12 | // baseUrl becomes /project/v1.1/
13 |
14 | //baseUrl: '/',
15 | publicPath: '/',
16 | // baseUrl: process.env.NODE_ENV === 'production' ? '/your/dir/' : '/'
17 |
18 | // where to output built files
19 | outputDir: './dist',
20 |
21 | // where to put static assets (js/css/img/font/...)
22 | assetsDir: 'assets',
23 |
24 | // whether to use eslint-loader for lint on save.
25 | // valid values: true | false | 'error'
26 | // when set to 'error', lint errors will cause compilation to fail.
27 | lintOnSave: true,
28 |
29 | // use the full build with in-browser compiler?
30 | // https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only
31 | runtimeCompiler: false,
32 |
33 | // babel-loader skips `node_modules` deps by default.
34 | // explicitly transpile a dependency with this option.
35 | transpileDependencies: [/* string or regex */],
36 |
37 | // generate sourceMap for production build?
38 | productionSourceMap: false,
39 |
40 | // tweak internal webpack configuration.
41 | // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
42 | chainWebpack: () => {},
43 | configureWebpack: () => {},
44 |
45 | // CSS related options
46 | css: {
47 | // extract CSS in components into a single CSS file (only in production)
48 | // can also be an object of options to pass to extract-text-webpack-plugin
49 | extract: true,
50 |
51 | // Enable CSS modules for all css / pre-processor files.
52 | // This option does not affect *.vue files.
53 | // modules: false,
54 |
55 | // enable CSS source maps?
56 | sourceMap: false,
57 |
58 | // pass custom options to pre-processor loaders. e.g. to pass options to
59 | // sass-loader, use { sass: { ... } }
60 | loaderOptions: {}
61 | },
62 |
63 | // use thread-loader for babel & TS in production build
64 | // enabled by default if the machine has more than 1 cores
65 | parallel: require("os").cpus().length > 1,
66 |
67 | // options for the PWA plugin.
68 | // see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
69 |
70 | // configure webpack-dev-server behavior
71 | devServer: {
72 | open: process.platform === "darwin",
73 | host: "0.0.0.0",
74 | port: 8088,
75 | https: false,
76 | hotOnly: false,
77 | compress: true
78 | // See https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#configuring-proxy
79 | },
80 |
81 | // options for 3rd party plugins
82 | pluginOptions: {
83 | // ...
84 | }
85 | };
--------------------------------------------------------------------------------
/front-end/src/views/Register.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
22 | 注册
23 | 重置
24 | 已有账户?去登陆
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
99 |
100 |
102 |
--------------------------------------------------------------------------------
/front-end/src/API.js:
--------------------------------------------------------------------------------
1 | import qs from 'qs';
2 | import axios from 'axios';
3 | import router from '@/router.js'
4 | axios.defaults.baseURL = 'http://localhost:3000/api/v1';
5 | axios.defaults.timeout = 1000 * 60 * 3;
6 | axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
7 | axios.defaults.headers.get['Content-Type'] = 'application/json';
8 |
9 |
10 |
11 | export const WebAppAPI = {
12 | CreateHttpClient: function () {
13 |
14 | let token = localStorage.getItem('X-Authorization-Token');
15 | let axiosInstance = axios.create({
16 | headers: {
17 | 'X-Authorization-Token': token
18 | },
19 |
20 | paramsSerializer: (params) => {
21 | return qs.stringify(params, {
22 | arrayFormat: 'brackets'
23 | });
24 | }
25 | });
26 |
27 | axiosInstance.interceptors.response.use((response) => {
28 | return response;
29 | }, (error) => {
30 | if (error.response.status === 401 ||
31 | error.response.status === 402 ||
32 | error.response.status === 403 ||
33 | error.response.status === 422) {
34 | this.RemoveCurrentUser();
35 | router.push('login');
36 | return Promise.reject(error);
37 |
38 | } else {
39 | return Promise.reject(error);
40 | }
41 | });
42 |
43 | return axiosInstance;
44 | },
45 | IsSignedIn: function(){
46 | let o = this.GetCurrentUser();
47 | if(o && o.id > 0){
48 | return true;
49 | }
50 | return false;
51 | },
52 | GetCurrentUser: function () {
53 | let currentUser = undefined
54 | if (localStorage.getItem('current-user')) {
55 | currentUser = qs.parse(localStorage.getItem('current-user'));
56 | }
57 | return currentUser;
58 | },
59 | SetCurrentUser: function (auth_token, user) {
60 | localStorage.setItem('X-Authorization-Token', auth_token);
61 | localStorage.setItem('current-user', qs.stringify(user));
62 | },
63 | RemoveCurrentUser: function () {
64 | localStorage.removeItem('X-Authorization-Token');
65 | localStorage.removeItem('current-user');
66 | },
67 | GetAllProducts: function () {
68 | return this.CreateHttpClient().get("/products").then(response => response);
69 | },
70 | GetProductById: function (id) {
71 | return this.CreateHttpClient().get("/products/" + id).then(response => response);
72 | },
73 | CreateProduct: function (product) {
74 | return this.CreateHttpClient().post("/products/", product).then(response => response);
75 | },
76 |
77 | DeleteProduct: function (id) {
78 | return this.CreateHttpClient().delete("/products/" + id).then(response => response);
79 | },
80 |
81 | UpdateProduct: function (product) {
82 | return this.CreateHttpClient().put("/products/" + product.id, product).then(response => response);
83 | },
84 |
85 | CreateUser: function (user) {
86 | return this.CreateHttpClient().post("/users", user).then(response => response);
87 | },
88 |
89 | CreateSession: function (username, password) {
90 | let loginData = {
91 | username: username,
92 | password: password,
93 | };
94 | return this.CreateHttpClient().post("/sessions", loginData).then(response => response);
95 | },
96 | }
--------------------------------------------------------------------------------
/back-end/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
19 | # config.require_master_key = true
20 |
21 | # Disable serving static files from the `/public` folder by default since
22 | # Apache or NGINX already handles this.
23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
24 |
25 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
26 | # config.action_controller.asset_host = 'http://assets.example.com'
27 |
28 | # Specifies the header that your server uses for sending files.
29 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
30 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
31 |
32 | # Store uploaded files on the local file system (see config/storage.yml for options)
33 | config.active_storage.service = :local
34 |
35 | # Mount Action Cable outside main process or domain
36 | # config.action_cable.mount_path = nil
37 | # config.action_cable.url = 'wss://example.com/cable'
38 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
39 |
40 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
41 | # config.force_ssl = true
42 |
43 | # Use the lowest log level to ensure availability of diagnostic information
44 | # when problems arise.
45 | config.log_level = :debug
46 |
47 | # Prepend all log lines with the following tags.
48 | config.log_tags = [ :request_id ]
49 |
50 | # Use a different cache store in production.
51 | # config.cache_store = :mem_cache_store
52 |
53 | # Use a real queuing backend for Active Job (and separate queues per environment)
54 | # config.active_job.queue_adapter = :resque
55 | # config.active_job.queue_name_prefix = "back-end_#{Rails.env}"
56 |
57 | config.action_mailer.perform_caching = false
58 |
59 | # Ignore bad email addresses and do not raise email delivery errors.
60 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
61 | # config.action_mailer.raise_delivery_errors = false
62 |
63 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
64 | # the I18n.default_locale when a translation cannot be found).
65 | config.i18n.fallbacks = true
66 |
67 | # Send deprecation notices to registered listeners.
68 | config.active_support.deprecation = :notify
69 |
70 | # Use default logging formatter so that PID and timestamp are not suppressed.
71 | config.log_formatter = ::Logger::Formatter.new
72 |
73 | # Use a different logger for distributed setups.
74 | # require 'syslog/logger'
75 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
76 |
77 | if ENV["RAILS_LOG_TO_STDOUT"].present?
78 | logger = ActiveSupport::Logger.new(STDOUT)
79 | logger.formatter = config.log_formatter
80 | config.logger = ActiveSupport::TaggedLogging.new(logger)
81 | end
82 |
83 | # Do not dump schema after migrations.
84 | config.active_record.dump_schema_after_migration = false
85 | end
86 |
--------------------------------------------------------------------------------
/front-end/src/components/ProductForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 立即创建
14 | 保存
15 | 重置
16 |
17 |
18 | 返回
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
122 |
123 |
128 |
--------------------------------------------------------------------------------
/back-end/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://gems.ruby-china.com/
3 | specs:
4 | actioncable (5.2.4.4)
5 | actionpack (= 5.2.4.4)
6 | nio4r (~> 2.0)
7 | websocket-driver (>= 0.6.1)
8 | actionmailer (5.2.4.4)
9 | actionpack (= 5.2.4.4)
10 | actionview (= 5.2.4.4)
11 | activejob (= 5.2.4.4)
12 | mail (~> 2.5, >= 2.5.4)
13 | rails-dom-testing (~> 2.0)
14 | actionpack (5.2.4.4)
15 | actionview (= 5.2.4.4)
16 | activesupport (= 5.2.4.4)
17 | rack (~> 2.0, >= 2.0.8)
18 | rack-test (>= 0.6.3)
19 | rails-dom-testing (~> 2.0)
20 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
21 | actionview (5.2.4.4)
22 | activesupport (= 5.2.4.4)
23 | builder (~> 3.1)
24 | erubi (~> 1.4)
25 | rails-dom-testing (~> 2.0)
26 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
27 | activejob (5.2.4.4)
28 | activesupport (= 5.2.4.4)
29 | globalid (>= 0.3.6)
30 | activemodel (5.2.4.4)
31 | activesupport (= 5.2.4.4)
32 | activerecord (5.2.4.4)
33 | activemodel (= 5.2.4.4)
34 | activesupport (= 5.2.4.4)
35 | arel (>= 9.0)
36 | activestorage (5.2.4.4)
37 | actionpack (= 5.2.4.4)
38 | activerecord (= 5.2.4.4)
39 | marcel (~> 0.3.1)
40 | activesupport (5.2.4.4)
41 | concurrent-ruby (~> 1.0, >= 1.0.2)
42 | i18n (>= 0.7, < 2)
43 | minitest (~> 5.1)
44 | tzinfo (~> 1.1)
45 | arel (9.0.0)
46 | bcrypt (3.1.16)
47 | bootsnap (1.4.8)
48 | msgpack (~> 1.0)
49 | builder (3.2.4)
50 | byebug (11.1.3)
51 | concurrent-ruby (1.1.7)
52 | crass (1.0.6)
53 | erubi (1.9.0)
54 | ffi (1.13.1)
55 | globalid (0.4.2)
56 | activesupport (>= 4.2.0)
57 | i18n (1.8.5)
58 | concurrent-ruby (~> 1.0)
59 | jwt (2.2.2)
60 | listen (3.1.5)
61 | rb-fsevent (~> 0.9, >= 0.9.4)
62 | rb-inotify (~> 0.9, >= 0.9.7)
63 | ruby_dep (~> 1.2)
64 | loofah (2.7.0)
65 | crass (~> 1.0.2)
66 | nokogiri (>= 1.5.9)
67 | mail (2.7.1)
68 | mini_mime (>= 0.1.1)
69 | marcel (0.3.3)
70 | mimemagic (~> 0.3.2)
71 | method_source (1.0.0)
72 | mimemagic (0.3.5)
73 | mini_mime (1.0.2)
74 | mini_portile2 (2.4.0)
75 | minitest (5.14.2)
76 | msgpack (1.3.3)
77 | nio4r (2.5.3)
78 | nokogiri (1.10.10)
79 | mini_portile2 (~> 2.4.0)
80 | puma (3.12.6)
81 | rack (2.2.3)
82 | rack-cors (1.1.1)
83 | rack (>= 2.0.0)
84 | rack-test (1.1.0)
85 | rack (>= 1.0, < 3)
86 | rails (5.2.4.4)
87 | actioncable (= 5.2.4.4)
88 | actionmailer (= 5.2.4.4)
89 | actionpack (= 5.2.4.4)
90 | actionview (= 5.2.4.4)
91 | activejob (= 5.2.4.4)
92 | activemodel (= 5.2.4.4)
93 | activerecord (= 5.2.4.4)
94 | activestorage (= 5.2.4.4)
95 | activesupport (= 5.2.4.4)
96 | bundler (>= 1.3.0)
97 | railties (= 5.2.4.4)
98 | sprockets-rails (>= 2.0.0)
99 | rails-dom-testing (2.0.3)
100 | activesupport (>= 4.2.0)
101 | nokogiri (>= 1.6)
102 | rails-html-sanitizer (1.3.0)
103 | loofah (~> 2.3)
104 | railties (5.2.4.4)
105 | actionpack (= 5.2.4.4)
106 | activesupport (= 5.2.4.4)
107 | method_source
108 | rake (>= 0.8.7)
109 | thor (>= 0.19.0, < 2.0)
110 | rake (13.0.1)
111 | rb-fsevent (0.10.4)
112 | rb-inotify (0.10.1)
113 | ffi (~> 1.0)
114 | ruby_dep (1.5.0)
115 | spring (2.1.1)
116 | spring-watcher-listen (2.0.1)
117 | listen (>= 2.7, < 4.0)
118 | spring (>= 1.2, < 3.0)
119 | sprockets (4.0.2)
120 | concurrent-ruby (~> 1.0)
121 | rack (> 1, < 3)
122 | sprockets-rails (3.2.2)
123 | actionpack (>= 4.0)
124 | activesupport (>= 4.0)
125 | sprockets (>= 3.0.0)
126 | sqlite3 (1.4.2)
127 | thor (1.0.1)
128 | thread_safe (0.3.6)
129 | tzinfo (1.2.7)
130 | thread_safe (~> 0.1)
131 | websocket-driver (0.7.3)
132 | websocket-extensions (>= 0.1.0)
133 | websocket-extensions (0.1.5)
134 |
135 | PLATFORMS
136 | ruby
137 |
138 | DEPENDENCIES
139 | bcrypt (~> 3.1, >= 3.1.12)
140 | bootsnap
141 | byebug
142 | jwt (~> 2.2, >= 2.2.2)
143 | listen (>= 3.0.5, < 3.2)
144 | puma (~> 3.12)
145 | rack-cors
146 | rails (~> 5.2.1)
147 | spring
148 | spring-watcher-listen (~> 2.0.0)
149 | sqlite3
150 | tzinfo-data
151 |
152 | BUNDLED WITH
153 | 1.17.3
154 |
--------------------------------------------------------------------------------
/back-end/public/assets/js/app.46bf1d3b.js:
--------------------------------------------------------------------------------
1 | (function(t){function e(e){for(var n,i,s=e[0],u=e[1],c=e[2],d=0,p=[];d{return t.name.indexOf(this.searchKey)>-1})}}},l=d,p=r("2877"),f=Object(p["a"])(l,u,c,!1,null,null,null);f.options.__file="ProductList.vue";var m=f.exports,v={name:"home",components:{ProductList:m}},b=v,h=Object(p["a"])(b,i,s,!1,null,null,null);h.options.__file="Home.vue";var _=h.exports,g=function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"add-product"},[r("h2",[t._v("添加商品")]),r("form",{on:{submit:t.createProduct}},[r("div",{staticClass:"form-group"},[r("label",{attrs:{for:"add-name"}},[t._v("名称")]),r("input",{directives:[{name:"model",rawName:"v-model",value:t.product.name,expression:"product.name"}],staticClass:"form-control",attrs:{id:"add-name",required:""},domProps:{value:t.product.name},on:{input:function(e){e.target.composing||t.$set(t.product,"name",e.target.value)}}})]),r("div",{staticClass:"form-group"},[r("label",{attrs:{for:"add-description"}},[t._v("描述")]),r("textarea",{directives:[{name:"model",rawName:"v-model",value:t.product.description,expression:"product.description"}],staticClass:"form-control",attrs:{id:"add-description",rows:"10"},domProps:{value:t.product.description},on:{input:function(e){e.target.composing||t.$set(t.product,"description",e.target.value)}}})]),r("div",{staticClass:"form-group"},[t._m(0),r("input",{directives:[{name:"model",rawName:"v-model",value:t.product.price,expression:"product.price"}],staticClass:"form-control",attrs:{type:"number",id:"add-price"},domProps:{value:t.product.price},on:{input:function(e){e.target.composing||t.$set(t.product,"price",e.target.value)}}})]),r("button",{staticClass:"btn btn-primary",attrs:{type:"submit"}},[t._v("创建")]),r("a",{staticClass:"btn btn-default"},[r("router-link",{attrs:{to:"/"}},[t._v("取消")])],1)])])},y=[function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("label",{attrs:{for:"add-price"}},[t._v("\n 价格\n "),r("span",{staticClass:"glyphicon glyphicon-rmb"})])}],w={name:"add-product",template:"#add-product",data:function(){return{product:{name:"",description:"",price:""}}},methods:{createProduct:function(){alert(this.product.name),router.push("/")}}},C=w,P=(r("53f9"),Object(p["a"])(C,g,y,!1,null,null,null));P.options.__file="AddProduct.vue";var x=P.exports;n["a"].use(o["a"]);var j=new o["a"]({routes:[{path:"/",name:"home",component:_},{path:"/about",name:"about",component:function(){return r.e("about").then(r.bind(null,"f820"))}},{path:"/add-product",name:"add-product",component:x}]}),k=r("2f62");n["a"].use(k["a"]);var O=new k["a"].Store({state:{},mutations:{},actions:{}});n["a"].config.productionTip=!1,new n["a"]({router:j,store:O,render:function(t){return t(a["default"])}}).$mount("#app")},"64a9":function(t,e,r){},"79a2":function(t,e){},a91d:function(t,e,r){}});
--------------------------------------------------------------------------------
/back-end/public/assets/js/chunk-vendors.a8c4cf9f.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-vendors"],{2877:function(t,e,n){"use strict";function r(t,e,n,r,o,i,a,s){var c,u="function"===typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=n,u._compiled=!0),r&&(u.functional=!0),i&&(u._scopeId="data-v-"+i),a?(c=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(a)},u._ssrRegister=c):o&&(c=s?function(){o.call(this,this.$root.$options.shadowRoot)}:o),c)if(u.functional){u._injectStyles=c;var f=u.render;u.render=function(t,e){return c.call(e),f(t,e)}}else{var l=u.beforeCreate;u.beforeCreate=l?[].concat(l,c):[c]}return{exports:t,options:u}}n.d(e,"a",function(){return r})},"2b0e":function(t,e,n){"use strict";(function(t){
2 | /*!
3 | * Vue.js v2.5.17
4 | * (c) 2014-2018 Evan You
5 | * Released under the MIT License.
6 | */
7 | var n=Object.freeze({});function r(t){return void 0===t||null===t}function o(t){return void 0!==t&&null!==t}function i(t){return!0===t}function a(t){return!1===t}function s(t){return"string"===typeof t||"number"===typeof t||"symbol"===typeof t||"boolean"===typeof t}function c(t){return null!==t&&"object"===typeof t}var u=Object.prototype.toString;function f(t){return"[object Object]"===u.call(t)}function l(t){return"[object RegExp]"===u.call(t)}function p(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function d(t){return null==t?"":"object"===typeof t?JSON.stringify(t,null,2):String(t)}function h(t){var e=parseFloat(t);return isNaN(e)?t:e}function v(t,e){for(var n=Object.create(null),r=t.split(","),o=0;o-1)return t.splice(n,1)}}var g=Object.prototype.hasOwnProperty;function _(t,e){return g.call(t,e)}function b(t){var e=Object.create(null);return function(n){var r=e[n];return r||(e[n]=t(n))}}var w=/-(\w)/g,C=b(function(t){return t.replace(w,function(t,e){return e?e.toUpperCase():""})}),x=b(function(t){return t.charAt(0).toUpperCase()+t.slice(1)}),$=/\B([A-Z])/g,A=b(function(t){return t.replace($,"-$1").toLowerCase()});function k(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n}function O(t,e){return t.bind(e)}var E=Function.prototype.bind?O:k;function S(t,e){e=e||0;var n=t.length-e,r=new Array(n);while(n--)r[n]=t[n+e];return r}function j(t,e){for(var n in e)t[n]=e[n];return t}function T(t){for(var e={},n=0;n0,tt=Q&&Q.indexOf("edge/")>0,et=(Q&&Q.indexOf("android"),Q&&/iphone|ipad|ipod|ios/.test(Q)||"ios"===J),nt=(Q&&/chrome\/\d+/.test(Q),{}.watch),rt=!1;if(K)try{var ot={};Object.defineProperty(ot,"passive",{get:function(){rt=!0}}),window.addEventListener("test-passive",null,ot)}catch(ta){}var it=function(){return void 0===G&&(G=!K&&!X&&"undefined"!==typeof t&&"server"===t["process"].env.VUE_ENV),G},at=K&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function st(t){return"function"===typeof t&&/native code/.test(t.toString())}var ct,ut="undefined"!==typeof Symbol&&st(Symbol)&&"undefined"!==typeof Reflect&&st(Reflect.ownKeys);ct="undefined"!==typeof Set&&st(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var ft=I,lt=0,pt=function(){this.id=lt++,this.subs=[]};pt.prototype.addSub=function(t){this.subs.push(t)},pt.prototype.removeSub=function(t){y(this.subs,t)},pt.prototype.depend=function(){pt.target&&pt.target.addDep(this)},pt.prototype.notify=function(){for(var t=this.subs.slice(),e=0,n=t.length;e-1)if(i&&!_(o,"default"))a=!1;else if(""===a||a===A(t)){var c=Jt(String,o.type);(c<0||s0&&(a=$e(a,(e||"")+"_"+n),xe(a[0])&&xe(u)&&(f[c]=_t(u.text+a[0].text),a.shift()),f.push.apply(f,a)):s(a)?xe(u)?f[c]=_t(u.text+a):""!==a&&f.push(_t(a)):xe(a)&&xe(u)?f[c]=_t(u.text+a.text):(i(t._isVList)&&o(a.tag)&&r(a.key)&&o(e)&&(a.key="__vlist"+e+"_"+n+"__"),f.push(a)));return f}function Ae(t,e){return(t.__esModule||ut&&"Module"===t[Symbol.toStringTag])&&(t=t.default),c(t)?e.extend(t):t}function ke(t,e,n,r,o){var i=gt();return i.asyncFactory=t,i.asyncMeta={data:e,context:n,children:r,tag:o},i}function Oe(t,e,n){if(i(t.error)&&o(t.errorComp))return t.errorComp;if(o(t.resolved))return t.resolved;if(i(t.loading)&&o(t.loadingComp))return t.loadingComp;if(!o(t.contexts)){var a=t.contexts=[n],s=!0,u=function(){for(var t=0,e=a.length;t1?S(n):n;for(var r=S(arguments,1),o=0,i=n.length;oYe&&We[n].id>t.id)n--;We.splice(n+1,0,t)}else We.push(t);Je||(Je=!0,fe(tn))}}var an=0,sn=function(t,e,n,r,o){this.vm=t,o&&(t._watcher=this),t._watchers.push(this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++an,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new ct,this.newDepIds=new ct,this.expression="","function"===typeof e?this.getter=e:(this.getter=z(e),this.getter||(this.getter=function(){})),this.value=this.lazy?void 0:this.get()};sn.prototype.get=function(){var t;ht(this);var e=this.vm;try{t=this.getter.call(e,e)}catch(ta){if(!this.user)throw ta;Qt(ta,e,'getter for watcher "'+this.expression+'"')}finally{this.deep&&pe(t),vt(),this.cleanupDeps()}return t},sn.prototype.addDep=function(t){var e=t.id;this.newDepIds.has(e)||(this.newDepIds.add(e),this.newDeps.push(t),this.depIds.has(e)||t.addSub(this))},sn.prototype.cleanupDeps=function(){var t=this,e=this.deps.length;while(e--){var n=t.deps[e];t.newDepIds.has(n.id)||n.removeSub(t)}var r=this.depIds;this.depIds=this.newDepIds,this.newDepIds=r,this.newDepIds.clear(),r=this.deps,this.deps=this.newDeps,this.newDeps=r,this.newDeps.length=0},sn.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():on(this)},sn.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||c(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(ta){Qt(ta,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,t,e)}}},sn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},sn.prototype.depend=function(){var t=this,e=this.deps.length;while(e--)t.deps[e].depend()},sn.prototype.teardown=function(){var t=this;if(this.active){this.vm._isBeingDestroyed||y(this.vm._watchers,this);var e=this.deps.length;while(e--)t.deps[e].removeSub(t);this.active=!1}};var cn={enumerable:!0,configurable:!0,get:I,set:I};function un(t,e,n){cn.get=function(){return this[e][n]},cn.set=function(t){this[e][n]=t},Object.defineProperty(t,n,cn)}function fn(t){t._watchers=[];var e=t.$options;e.props&&ln(t,e.props),e.methods&&gn(t,e.methods),e.data?pn(t):jt(t._data={},!0),e.computed&&vn(t,e.computed),e.watch&&e.watch!==nt&&_n(t,e.watch)}function ln(t,e){var n=t.$options.propsData||{},r=t._props={},o=t.$options._propKeys=[],i=!t.$parent;i||kt(!1);var a=function(i){o.push(i);var a=Gt(i,e,n,t);Tt(r,i,a),i in t||un(t,"_props",i)};for(var s in e)a(s);kt(!0)}function pn(t){var e=t.$options.data;e=t._data="function"===typeof e?dn(e,t):e||{},f(e)||(e={});var n=Object.keys(e),r=t.$options.props,o=(t.$options.methods,n.length);while(o--){var i=n[o];0,r&&_(r,i)||H(i)||un(t,"_data",i)}jt(e,!0)}function dn(t,e){ht();try{return t.call(e,e)}catch(ta){return Qt(ta,e,"data()"),{}}finally{vt()}}var hn={lazy:!0};function vn(t,e){var n=t._computedWatchers=Object.create(null),r=it();for(var o in e){var i=e[o],a="function"===typeof i?i:i.get;0,r||(n[o]=new sn(t,a||I,I,hn)),o in t||mn(t,o,i)}}function mn(t,e,n){var r=!it();"function"===typeof n?(cn.get=r?yn(e):n,cn.set=I):(cn.get=n.get?r&&!1!==n.cache?yn(e):n.get:I,cn.set=n.set?n.set:I),Object.defineProperty(t,e,cn)}function yn(t){return function(){var e=this._computedWatchers&&this._computedWatchers[t];if(e)return e.dirty&&e.evaluate(),pt.target&&e.depend(),e.value}}function gn(t,e){t.$options.props;for(var n in e)t[n]=null==e[n]?I:E(e[n],t)}function _n(t,e){for(var n in e){var r=e[n];if(Array.isArray(r))for(var o=0;o=0||n.indexOf(t[o])<0)&&r.push(t[o]);return r}return t}function sr(t){this._init(t)}function cr(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=S(arguments,1);return n.unshift(this),"function"===typeof t.install?t.install.apply(t,n):"function"===typeof t&&t.apply(null,n),e.push(t),this}}function ur(t){t.mixin=function(t){return this.options=Bt(this.options,t),this}}function fr(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,r=n.cid,o=t._Ctor||(t._Ctor={});if(o[r])return o[r];var i=t.name||n.options.name;var a=function(t){this._init(t)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=e++,a.options=Bt(n.options,t),a["super"]=n,a.options.props&&lr(a),a.options.computed&&pr(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,U.forEach(function(t){a[t]=n[t]}),i&&(a.options.components[i]=a),a.superOptions=n.options,a.extendOptions=t,a.sealedOptions=j({},a.options),o[r]=a,a}}function lr(t){var e=t.options.props;for(var n in e)un(t.prototype,"_props",n)}function pr(t){var e=t.options.computed;for(var n in e)mn(t.prototype,n,e[n])}function dr(t){U.forEach(function(e){t[e]=function(t,n){return n?("component"===e&&f(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&"function"===typeof n&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}})}function hr(t){return t&&(t.Ctor.options.name||t.tag)}function vr(t,e){return Array.isArray(t)?t.indexOf(e)>-1:"string"===typeof t?t.split(",").indexOf(e)>-1:!!l(t)&&t.test(e)}function mr(t,e){var n=t.cache,r=t.keys,o=t._vnode;for(var i in n){var a=n[i];if(a){var s=hr(a.componentOptions);s&&!e(s)&&yr(n,i,r,o)}}}function yr(t,e,n,r){var o=t[e];!o||r&&o.tag===r.tag||o.componentInstance.$destroy(),t[e]=null,y(n,e)}nr(sr),wn(sr),Pe(sr),Ve(sr),tr(sr);var gr=[String,RegExp,Array],_r={name:"keep-alive",abstract:!0,props:{include:gr,exclude:gr,max:[String,Number]},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){var t=this;for(var e in t.cache)yr(t.cache,e,t.keys)},mounted:function(){var t=this;this.$watch("include",function(e){mr(t,function(t){return vr(e,t)})}),this.$watch("exclude",function(e){mr(t,function(t){return!vr(e,t)})})},render:function(){var t=this.$slots.default,e=Se(t),n=e&&e.componentOptions;if(n){var r=hr(n),o=this,i=o.include,a=o.exclude;if(i&&(!r||!vr(i,r))||a&&r&&vr(a,r))return e;var s=this,c=s.cache,u=s.keys,f=null==e.key?n.Ctor.cid+(n.tag?"::"+n.tag:""):e.key;c[f]?(e.componentInstance=c[f].componentInstance,y(u,f),u.push(f)):(c[f]=e,u.push(f),this.max&&u.length>parseInt(this.max)&&yr(c,u[0],u,this._vnode)),e.data.keepAlive=!0}return e||t&&t[0]}},br={KeepAlive:_r};function wr(t){var e={get:function(){return F}};Object.defineProperty(t,"config",e),t.util={warn:ft,extend:j,mergeOptions:Bt,defineReactive:Tt},t.set=It,t.delete=Mt,t.nextTick=fe,t.options=Object.create(null),U.forEach(function(e){t.options[e+"s"]=Object.create(null)}),t.options._base=t,j(t.options.components,br),cr(t),ur(t),fr(t),dr(t)}wr(sr),Object.defineProperty(sr.prototype,"$isServer",{get:it}),Object.defineProperty(sr.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(sr,"FunctionalRenderContext",{value:Nn}),sr.version="2.5.17";var Cr=v("style,class"),xr=v("input,textarea,option,select,progress"),$r=function(t,e,n){return"value"===n&&xr(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},Ar=v("contenteditable,draggable,spellcheck"),kr=v("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Or="http://www.w3.org/1999/xlink",Er=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},Sr=function(t){return Er(t)?t.slice(6,t.length):""},jr=function(t){return null==t||!1===t};function Tr(t){var e=t.data,n=t,r=t;while(o(r.componentInstance))r=r.componentInstance._vnode,r&&r.data&&(e=Ir(r.data,e));while(o(n=n.parent))n&&n.data&&(e=Ir(e,n.data));return Mr(e.staticClass,e.class)}function Ir(t,e){return{staticClass:Pr(t.staticClass,e.staticClass),class:o(t.class)?[t.class,e.class]:e.class}}function Mr(t,e){return o(t)||o(e)?Pr(t,Rr(e)):""}function Pr(t,e){return t?e?t+" "+e:t:e||""}function Rr(t){return Array.isArray(t)?Lr(t):c(t)?Nr(t):"string"===typeof t?t:""}function Lr(t){for(var e,n="",r=0,i=t.length;r-1?qr[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:qr[t]=/HTMLUnknownElement/.test(e.toString())}var zr=v("text,number,password,search,email,tel,url");function Gr(t){if("string"===typeof t){var e=document.querySelector(t);return e||document.createElement("div")}return t}function Wr(t,e){var n=document.createElement(t);return"select"!==t?n:(e.data&&e.data.attrs&&void 0!==e.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n)}function Kr(t,e){return document.createElementNS(Dr[t],e)}function Xr(t){return document.createTextNode(t)}function Jr(t){return document.createComment(t)}function Qr(t,e,n){t.insertBefore(e,n)}function Yr(t,e){t.removeChild(e)}function Zr(t,e){t.appendChild(e)}function to(t){return t.parentNode}function eo(t){return t.nextSibling}function no(t){return t.tagName}function ro(t,e){t.textContent=e}function oo(t,e){t.setAttribute(e,"")}var io=Object.freeze({createElement:Wr,createElementNS:Kr,createTextNode:Xr,createComment:Jr,insertBefore:Qr,removeChild:Yr,appendChild:Zr,parentNode:to,nextSibling:eo,tagName:no,setTextContent:ro,setStyleScope:oo}),ao={create:function(t,e){so(e)},update:function(t,e){t.data.ref!==e.data.ref&&(so(t,!0),so(e))},destroy:function(t){so(t,!0)}};function so(t,e){var n=t.data.ref;if(o(n)){var r=t.context,i=t.componentInstance||t.elm,a=r.$refs;e?Array.isArray(a[n])?y(a[n],i):a[n]===i&&(a[n]=void 0):t.data.refInFor?Array.isArray(a[n])?a[n].indexOf(i)<0&&a[n].push(i):a[n]=[i]:a[n]=i}}var co=new mt("",{},[]),uo=["create","activate","update","remove","destroy"];function fo(t,e){return t.key===e.key&&(t.tag===e.tag&&t.isComment===e.isComment&&o(t.data)===o(e.data)&&lo(t,e)||i(t.isAsyncPlaceholder)&&t.asyncFactory===e.asyncFactory&&r(e.asyncFactory.error))}function lo(t,e){if("input"!==t.tag)return!0;var n,r=o(n=t.data)&&o(n=n.attrs)&&n.type,i=o(n=e.data)&&o(n=n.attrs)&&n.type;return r===i||zr(r)&&zr(i)}function po(t,e,n){var r,i,a={};for(r=e;r<=n;++r)i=t[r].key,o(i)&&(a[i]=r);return a}function ho(t){var e,n,a={},c=t.modules,u=t.nodeOps;for(e=0;ev?(l=r(n[g+1])?null:n[g+1].elm,x(t,l,n,h,g,i)):h>g&&A(t,e,p,v)}function E(t,e,n,r){for(var i=n;i-1?Ao(t,e,n):kr(e)?jr(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):Ar(e)?t.setAttribute(e,jr(n)||"false"===n?"false":"true"):Er(e)?jr(n)?t.removeAttributeNS(Or,Sr(e)):t.setAttributeNS(Or,e,n):Ao(t,e,n)}function Ao(t,e,n){if(jr(n))t.removeAttribute(e);else{if(Y&&!Z&&"TEXTAREA"===t.tagName&&"placeholder"===e&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",r)};t.addEventListener("input",r),t.__ieph=!0}t.setAttribute(e,n)}}var ko={create:xo,update:xo};function Oo(t,e){var n=e.elm,i=e.data,a=t.data;if(!(r(i.staticClass)&&r(i.class)&&(r(a)||r(a.staticClass)&&r(a.class)))){var s=Tr(e),c=n._transitionClasses;o(c)&&(s=Pr(s,Rr(c))),s!==n._prevClass&&(n.setAttribute("class",s),n._prevClass=s)}}var Eo,So={create:Oo,update:Oo},jo="__r",To="__c";function Io(t){if(o(t[jo])){var e=Y?"change":"input";t[e]=[].concat(t[jo],t[e]||[]),delete t[jo]}o(t[To])&&(t.change=[].concat(t[To],t.change||[]),delete t[To])}function Mo(t,e,n){var r=Eo;return function o(){var i=t.apply(null,arguments);null!==i&&Ro(e,o,n,r)}}function Po(t,e,n,r,o){e=ue(e),n&&(e=Mo(e,t,r)),Eo.addEventListener(t,e,rt?{capture:r,passive:o}:r)}function Ro(t,e,n,r){(r||Eo).removeEventListener(t,e._withTask||e,n)}function Lo(t,e){if(!r(t.data.on)||!r(e.data.on)){var n=e.data.on||{},o=t.data.on||{};Eo=e.elm,Io(n),ye(n,o,Po,Ro,e.context),Eo=void 0}}var No={create:Lo,update:Lo};function Do(t,e){if(!r(t.data.domProps)||!r(e.data.domProps)){var n,i,a=e.elm,s=t.data.domProps||{},c=e.data.domProps||{};for(n in o(c.__ob__)&&(c=e.data.domProps=j({},c)),s)r(c[n])&&(a[n]="");for(n in c){if(i=c[n],"textContent"===n||"innerHTML"===n){if(e.children&&(e.children.length=0),i===s[n])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===n){a._value=i;var u=r(i)?"":String(i);Uo(a,u)&&(a.value=u)}else a[n]=i}}}function Uo(t,e){return!t.composing&&("OPTION"===t.tagName||Vo(t,e)||Fo(t,e))}function Vo(t,e){var n=!0;try{n=document.activeElement!==t}catch(ta){}return n&&t.value!==e}function Fo(t,e){var n=t.value,r=t._vModifiers;if(o(r)){if(r.lazy)return!1;if(r.number)return h(n)!==h(e);if(r.trim)return n.trim()!==e.trim()}return n!==e}var Ho={create:Do,update:Do},qo=b(function(t){var e={},n=/;(?![^(]*\))/g,r=/:(.+)/;return t.split(n).forEach(function(t){if(t){var n=t.split(r);n.length>1&&(e[n[0].trim()]=n[1].trim())}}),e});function Bo(t){var e=zo(t.style);return t.staticStyle?j(t.staticStyle,e):e}function zo(t){return Array.isArray(t)?T(t):"string"===typeof t?qo(t):t}function Go(t,e){var n,r={};if(e){var o=t;while(o.componentInstance)o=o.componentInstance._vnode,o&&o.data&&(n=Bo(o.data))&&j(r,n)}(n=Bo(t.data))&&j(r,n);var i=t;while(i=i.parent)i.data&&(n=Bo(i.data))&&j(r,n);return r}var Wo,Ko=/^--/,Xo=/\s*!important$/,Jo=function(t,e,n){if(Ko.test(e))t.style.setProperty(e,n);else if(Xo.test(n))t.style.setProperty(e,n.replace(Xo,""),"important");else{var r=Yo(e);if(Array.isArray(n))for(var o=0,i=n.length;o-1?e.split(/\s+/).forEach(function(e){return t.classList.add(e)}):t.classList.add(e);else{var n=" "+(t.getAttribute("class")||"")+" ";n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function ni(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(/\s+/).forEach(function(e){return t.classList.remove(e)}):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{var n=" "+(t.getAttribute("class")||"")+" ",r=" "+e+" ";while(n.indexOf(r)>=0)n=n.replace(r," ");n=n.trim(),n?t.setAttribute("class",n):t.removeAttribute("class")}}function ri(t){if(t){if("object"===typeof t){var e={};return!1!==t.css&&j(e,oi(t.name||"v")),j(e,t),e}return"string"===typeof t?oi(t):void 0}}var oi=b(function(t){return{enterClass:t+"-enter",enterToClass:t+"-enter-to",enterActiveClass:t+"-enter-active",leaveClass:t+"-leave",leaveToClass:t+"-leave-to",leaveActiveClass:t+"-leave-active"}}),ii=K&&!Z,ai="transition",si="animation",ci="transition",ui="transitionend",fi="animation",li="animationend";ii&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(ci="WebkitTransition",ui="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(fi="WebkitAnimation",li="webkitAnimationEnd"));var pi=K?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function di(t){pi(function(){pi(t)})}function hi(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),ei(t,e))}function vi(t,e){t._transitionClasses&&y(t._transitionClasses,e),ni(t,e)}function mi(t,e,n){var r=gi(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s=o===ai?ui:li,c=0,u=function(){t.removeEventListener(s,f),n()},f=function(e){e.target===t&&++c>=a&&u()};setTimeout(function(){c0&&(n=ai,f=a,l=i.length):e===si?u>0&&(n=si,f=u,l=c.length):(f=Math.max(a,u),n=f>0?a>u?ai:si:null,l=n?n===ai?i.length:c.length:0);var p=n===ai&&yi.test(r[ci+"Property"]);return{type:n,timeout:f,propCount:l,hasTransform:p}}function _i(t,e){while(t.length1}function Ai(t,e){!0!==e.data.show&&wi(e)}var ki=K?{create:Ai,activate:Ai,remove:function(t,e){!0!==t.data.show?Ci(t,e):e()}}:{},Oi=[ko,So,No,Ho,ti,ki],Ei=Oi.concat(Co),Si=ho({nodeOps:io,modules:Ei});Z&&document.addEventListener("selectionchange",function(){var t=document.activeElement;t&&t.vmodel&&Ni(t,"input")});var ji={inserted:function(t,e,n,r){"select"===n.tag?(r.elm&&!r.elm._vOptions?ge(n,"postpatch",function(){ji.componentUpdated(t,e,n)}):Ti(t,e,n.context),t._vOptions=[].map.call(t.options,Pi)):("textarea"===n.tag||zr(t.type))&&(t._vModifiers=e.modifiers,e.modifiers.lazy||(t.addEventListener("compositionstart",Ri),t.addEventListener("compositionend",Li),t.addEventListener("change",Li),Z&&(t.vmodel=!0)))},componentUpdated:function(t,e,n){if("select"===n.tag){Ti(t,e,n.context);var r=t._vOptions,o=t._vOptions=[].map.call(t.options,Pi);if(o.some(function(t,e){return!R(t,r[e])})){var i=t.multiple?e.value.some(function(t){return Mi(t,o)}):e.value!==e.oldValue&&Mi(e.value,o);i&&Ni(t,"change")}}}};function Ti(t,e,n){Ii(t,e,n),(Y||tt)&&setTimeout(function(){Ii(t,e,n)},0)}function Ii(t,e,n){var r=e.value,o=t.multiple;if(!o||Array.isArray(r)){for(var i,a,s=0,c=t.options.length;s-1,a.selected!==i&&(a.selected=i);else if(R(Pi(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function Mi(t,e){return e.every(function(e){return!R(e,t)})}function Pi(t){return"_value"in t?t._value:t.value}function Ri(t){t.target.composing=!0}function Li(t){t.target.composing&&(t.target.composing=!1,Ni(t.target,"input"))}function Ni(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function Di(t){return!t.componentInstance||t.data&&t.data.transition?t:Di(t.componentInstance._vnode)}var Ui={bind:function(t,e,n){var r=e.value;n=Di(n);var o=n.data&&n.data.transition,i=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&o?(n.data.show=!0,wi(n,function(){t.style.display=i})):t.style.display=r?i:"none"},update:function(t,e,n){var r=e.value,o=e.oldValue;if(!r!==!o){n=Di(n);var i=n.data&&n.data.transition;i?(n.data.show=!0,r?wi(n,function(){t.style.display=t.__vOriginalDisplay}):Ci(n,function(){t.style.display="none"})):t.style.display=r?t.__vOriginalDisplay:"none"}},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}},Vi={model:ji,show:Ui},Fi={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function Hi(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?Hi(Se(e.children)):t}function qi(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var i in o)e[C(i)]=o[i];return e}function Bi(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}function zi(t){while(t=t.parent)if(t.data.transition)return!0}function Gi(t,e){return e.key===t.key&&e.tag===t.tag}var Wi={name:"transition",props:Fi,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(function(t){return t.tag||Ee(t)}),n.length)){0;var r=this.mode;0;var o=n[0];if(zi(this.$vnode))return o;var i=Hi(o);if(!i)return o;if(this._leaving)return Bi(t,o);var a="__transition-"+this._uid+"-";i.key=null==i.key?i.isComment?a+"comment":a+i.tag:s(i.key)?0===String(i.key).indexOf(a)?i.key:a+i.key:i.key;var c=(i.data||(i.data={})).transition=qi(this),u=this._vnode,f=Hi(u);if(i.data.directives&&i.data.directives.some(function(t){return"show"===t.name})&&(i.data.show=!0),f&&f.data&&!Gi(i,f)&&!Ee(f)&&(!f.componentInstance||!f.componentInstance._vnode.isComment)){var l=f.data.transition=j({},c);if("out-in"===r)return this._leaving=!0,ge(l,"afterLeave",function(){e._leaving=!1,e.$forceUpdate()}),Bi(t,o);if("in-out"===r){if(Ee(i))return u;var p,d=function(){p()};ge(c,"afterEnter",d),ge(c,"enterCancelled",d),ge(l,"delayLeave",function(t){p=t})}}return o}}},Ki=j({tag:String,moveClass:String},Fi);delete Ki.mode;var Xi={props:Ki,render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=qi(this),s=0;s=2)t.mixin({beforeCreate:r});else{var n=t.prototype._init;t.prototype._init=function(t){void 0===t&&(t={}),t.init=t.init?[r].concat(t.init):r,n.call(this,t)}}function r(){var t=this.$options;t.store?this.$store="function"===typeof t.store?t.store():t.store:t.parent&&t.parent.$store&&(this.$store=t.parent.$store)}},o="undefined"!==typeof window&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function i(t){o&&(t._devtoolHook=o,o.emit("vuex:init",t),o.on("vuex:travel-to-state",function(e){t.replaceState(e)}),t.subscribe(function(t,e){o.emit("vuex:mutation",t,e)}))}function a(t,e){Object.keys(t).forEach(function(n){return e(t[n],n)})}function s(t){return null!==t&&"object"===typeof t}function c(t){return t&&"function"===typeof t.then}var u=function(t,e){this.runtime=e,this._children=Object.create(null),this._rawModule=t;var n=t.state;this.state=("function"===typeof n?n():n)||{}},f={namespaced:{configurable:!0}};f.namespaced.get=function(){return!!this._rawModule.namespaced},u.prototype.addChild=function(t,e){this._children[t]=e},u.prototype.removeChild=function(t){delete this._children[t]},u.prototype.getChild=function(t){return this._children[t]},u.prototype.update=function(t){this._rawModule.namespaced=t.namespaced,t.actions&&(this._rawModule.actions=t.actions),t.mutations&&(this._rawModule.mutations=t.mutations),t.getters&&(this._rawModule.getters=t.getters)},u.prototype.forEachChild=function(t){a(this._children,t)},u.prototype.forEachGetter=function(t){this._rawModule.getters&&a(this._rawModule.getters,t)},u.prototype.forEachAction=function(t){this._rawModule.actions&&a(this._rawModule.actions,t)},u.prototype.forEachMutation=function(t){this._rawModule.mutations&&a(this._rawModule.mutations,t)},Object.defineProperties(u.prototype,f);var l=function(t){this.register([],t,!1)};function p(t,e,n){if(e.update(n),n.modules)for(var r in n.modules){if(!e.getChild(r))return void 0;p(t.concat(r),e.getChild(r),n.modules[r])}}l.prototype.get=function(t){return t.reduce(function(t,e){return t.getChild(e)},this.root)},l.prototype.getNamespace=function(t){var e=this.root;return t.reduce(function(t,n){return e=e.getChild(n),t+(e.namespaced?n+"/":"")},"")},l.prototype.update=function(t){p([],this.root,t)},l.prototype.register=function(t,e,n){var r=this;void 0===n&&(n=!0);var o=new u(e,n);if(0===t.length)this.root=o;else{var i=this.get(t.slice(0,-1));i.addChild(t[t.length-1],o)}e.modules&&a(e.modules,function(e,o){r.register(t.concat(o),e,n)})},l.prototype.unregister=function(t){var e=this.get(t.slice(0,-1)),n=t[t.length-1];e.getChild(n).runtime&&e.removeChild(n)};var d;var h=function(t){var e=this;void 0===t&&(t={}),!d&&"undefined"!==typeof window&&window.Vue&&E(window.Vue);var n=t.plugins;void 0===n&&(n=[]);var r=t.strict;void 0===r&&(r=!1);var o=t.state;void 0===o&&(o={}),"function"===typeof o&&(o=o()||{}),this._committing=!1,this._actions=Object.create(null),this._actionSubscribers=[],this._mutations=Object.create(null),this._wrappedGetters=Object.create(null),this._modules=new l(t),this._modulesNamespaceMap=Object.create(null),this._subscribers=[],this._watcherVM=new d;var a=this,s=this,c=s.dispatch,u=s.commit;this.dispatch=function(t,e){return c.call(a,t,e)},this.commit=function(t,e,n){return u.call(a,t,e,n)},this.strict=r,_(this,o,[],this._modules.root),g(this,o),n.forEach(function(t){return t(e)}),d.config.devtools&&i(this)},v={state:{configurable:!0}};function m(t,e){return e.indexOf(t)<0&&e.push(t),function(){var n=e.indexOf(t);n>-1&&e.splice(n,1)}}function y(t,e){t._actions=Object.create(null),t._mutations=Object.create(null),t._wrappedGetters=Object.create(null),t._modulesNamespaceMap=Object.create(null);var n=t.state;_(t,n,[],t._modules.root,!0),g(t,n,e)}function g(t,e,n){var r=t._vm;t.getters={};var o=t._wrappedGetters,i={};a(o,function(e,n){i[n]=function(){return e(t)},Object.defineProperty(t.getters,n,{get:function(){return t._vm[n]},enumerable:!0})});var s=d.config.silent;d.config.silent=!0,t._vm=new d({data:{$$state:e},computed:i}),d.config.silent=s,t.strict&&A(t),r&&(n&&t._withCommit(function(){r._data.$$state=null}),d.nextTick(function(){return r.$destroy()}))}function _(t,e,n,r,o){var i=!n.length,a=t._modules.getNamespace(n);if(r.namespaced&&(t._modulesNamespaceMap[a]=r),!i&&!o){var s=k(e,n.slice(0,-1)),c=n[n.length-1];t._withCommit(function(){d.set(s,c,r.state)})}var u=r.context=b(t,a,n);r.forEachMutation(function(e,n){var r=a+n;C(t,r,e,u)}),r.forEachAction(function(e,n){var r=e.root?n:a+n,o=e.handler||e;x(t,r,o,u)}),r.forEachGetter(function(e,n){var r=a+n;$(t,r,e,u)}),r.forEachChild(function(r,i){_(t,e,n.concat(i),r,o)})}function b(t,e,n){var r=""===e,o={dispatch:r?t.dispatch:function(n,r,o){var i=O(n,r,o),a=i.payload,s=i.options,c=i.type;return s&&s.root||(c=e+c),t.dispatch(c,a)},commit:r?t.commit:function(n,r,o){var i=O(n,r,o),a=i.payload,s=i.options,c=i.type;s&&s.root||(c=e+c),t.commit(c,a,s)}};return Object.defineProperties(o,{getters:{get:r?function(){return t.getters}:function(){return w(t,e)}},state:{get:function(){return k(t.state,n)}}}),o}function w(t,e){var n={},r=e.length;return Object.keys(t.getters).forEach(function(o){if(o.slice(0,r)===e){var i=o.slice(r);Object.defineProperty(n,i,{get:function(){return t.getters[o]},enumerable:!0})}}),n}function C(t,e,n,r){var o=t._mutations[e]||(t._mutations[e]=[]);o.push(function(e){n.call(t,r.state,e)})}function x(t,e,n,r){var o=t._actions[e]||(t._actions[e]=[]);o.push(function(e,o){var i=n.call(t,{dispatch:r.dispatch,commit:r.commit,getters:r.getters,state:r.state,rootGetters:t.getters,rootState:t.state},e,o);return c(i)||(i=Promise.resolve(i)),t._devtoolHook?i.catch(function(e){throw t._devtoolHook.emit("vuex:error",e),e}):i})}function $(t,e,n,r){t._wrappedGetters[e]||(t._wrappedGetters[e]=function(t){return n(r.state,r.getters,t.state,t.getters)})}function A(t){t._vm.$watch(function(){return this._data.$$state},function(){0},{deep:!0,sync:!0})}function k(t,e){return e.length?e.reduce(function(t,e){return t[e]},t):t}function O(t,e,n){return s(t)&&t.type&&(n=e,e=t,t=t.type),{type:t,payload:e,options:n}}function E(t){d&&t===d||(d=t,r(d))}v.state.get=function(){return this._vm._data.$$state},v.state.set=function(t){0},h.prototype.commit=function(t,e,n){var r=this,o=O(t,e,n),i=o.type,a=o.payload,s=(o.options,{type:i,payload:a}),c=this._mutations[i];c&&(this._withCommit(function(){c.forEach(function(t){t(a)})}),this._subscribers.forEach(function(t){return t(s,r.state)}))},h.prototype.dispatch=function(t,e){var n=this,r=O(t,e),o=r.type,i=r.payload,a={type:o,payload:i},s=this._actions[o];if(s)return this._actionSubscribers.forEach(function(t){return t(a,n.state)}),s.length>1?Promise.all(s.map(function(t){return t(i)})):s[0](i)},h.prototype.subscribe=function(t){return m(t,this._subscribers)},h.prototype.subscribeAction=function(t){return m(t,this._actionSubscribers)},h.prototype.watch=function(t,e,n){var r=this;return this._watcherVM.$watch(function(){return t(r.state,r.getters)},e,n)},h.prototype.replaceState=function(t){var e=this;this._withCommit(function(){e._vm._data.$$state=t})},h.prototype.registerModule=function(t,e,n){void 0===n&&(n={}),"string"===typeof t&&(t=[t]),this._modules.register(t,e),_(this,this.state,t,this._modules.get(t),n.preserveState),g(this,this.state)},h.prototype.unregisterModule=function(t){var e=this;"string"===typeof t&&(t=[t]),this._modules.unregister(t),this._withCommit(function(){var n=k(e.state,t.slice(0,-1));d.delete(n,t[t.length-1])}),y(this)},h.prototype.hotUpdate=function(t){this._modules.update(t),y(this,!0)},h.prototype._withCommit=function(t){var e=this._committing;this._committing=!0,t(),this._committing=e},Object.defineProperties(h.prototype,v);var S=R(function(t,e){var n={};return P(e).forEach(function(e){var r=e.key,o=e.val;n[r]=function(){var e=this.$store.state,n=this.$store.getters;if(t){var r=L(this.$store,"mapState",t);if(!r)return;e=r.context.state,n=r.context.getters}return"function"===typeof o?o.call(this,e,n):e[o]},n[r].vuex=!0}),n}),j=R(function(t,e){var n={};return P(e).forEach(function(e){var r=e.key,o=e.val;n[r]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var r=this.$store.commit;if(t){var i=L(this.$store,"mapMutations",t);if(!i)return;r=i.context.commit}return"function"===typeof o?o.apply(this,[r].concat(e)):r.apply(this.$store,[o].concat(e))}}),n}),T=R(function(t,e){var n={};return P(e).forEach(function(e){var r=e.key,o=e.val;o=t+o,n[r]=function(){if(!t||L(this.$store,"mapGetters",t))return this.$store.getters[o]},n[r].vuex=!0}),n}),I=R(function(t,e){var n={};return P(e).forEach(function(e){var r=e.key,o=e.val;n[r]=function(){var e=[],n=arguments.length;while(n--)e[n]=arguments[n];var r=this.$store.dispatch;if(t){var i=L(this.$store,"mapActions",t);if(!i)return;r=i.context.dispatch}return"function"===typeof o?o.apply(this,[r].concat(e)):r.apply(this.$store,[o].concat(e))}}),n}),M=function(t){return{mapState:S.bind(null,t),mapGetters:T.bind(null,t),mapMutations:j.bind(null,t),mapActions:I.bind(null,t)}};function P(t){return Array.isArray(t)?t.map(function(t){return{key:t,val:t}}):Object.keys(t).map(function(e){return{key:e,val:t[e]}})}function R(t){return function(e,n){return"string"!==typeof e?(n=e,e=""):"/"!==e.charAt(e.length-1)&&(e+="/"),t(e,n)}}function L(t,e,n){var r=t._modulesNamespaceMap[n];return r}var N={Store:h,install:E,version:"3.0.1",mapState:S,mapMutations:j,mapGetters:T,mapActions:I,createNamespacedHelpers:M};e["a"]=N},"8c4f":function(t,e,n){"use strict";
13 | /**
14 | * vue-router v3.0.1
15 | * (c) 2017 Evan You
16 | * @license MIT
17 | */function r(t,e){0}function o(t){return Object.prototype.toString.call(t).indexOf("Error")>-1}var i={name:"router-view",functional:!0,props:{name:{type:String,default:"default"}},render:function(t,e){var n=e.props,r=e.children,o=e.parent,i=e.data;i.routerView=!0;var c=o.$createElement,u=n.name,f=o.$route,l=o._routerViewCache||(o._routerViewCache={}),p=0,d=!1;while(o&&o._routerRoot!==o)o.$vnode&&o.$vnode.data.routerView&&p++,o._inactive&&(d=!0),o=o.$parent;if(i.routerViewDepth=p,d)return c(l[u],i,r);var h=f.matched[p];if(!h)return l[u]=null,c();var v=l[u]=h.components[u];i.registerRouteInstance=function(t,e){var n=h.instances[u];(e&&n!==t||!e&&n===t)&&(h.instances[u]=e)},(i.hook||(i.hook={})).prepatch=function(t,e){h.instances[u]=e.componentInstance};var m=i.props=a(f,h.props&&h.props[u]);if(m){m=i.props=s({},m);var y=i.attrs=i.attrs||{};for(var g in m)v.props&&g in v.props||(y[g]=m[g],delete m[g])}return c(v,i,r)}};function a(t,e){switch(typeof e){case"undefined":return;case"object":return e;case"function":return e(t);case"boolean":return e?t.params:void 0;default:0}}function s(t,e){for(var n in e)t[n]=e[n];return t}var c=/[!'()*]/g,u=function(t){return"%"+t.charCodeAt(0).toString(16)},f=/%2C/g,l=function(t){return encodeURIComponent(t).replace(c,u).replace(f,",")},p=decodeURIComponent;function d(t,e,n){void 0===e&&(e={});var r,o=n||h;try{r=o(t||"")}catch(a){r={}}for(var i in e)r[i]=e[i];return r}function h(t){var e={};return t=t.trim().replace(/^(\?|#|&)/,""),t?(t.split("&").forEach(function(t){var n=t.replace(/\+/g," ").split("="),r=p(n.shift()),o=n.length>0?p(n.join("=")):null;void 0===e[r]?e[r]=o:Array.isArray(e[r])?e[r].push(o):e[r]=[e[r],o]}),e):e}function v(t){var e=t?Object.keys(t).map(function(e){var n=t[e];if(void 0===n)return"";if(null===n)return l(e);if(Array.isArray(n)){var r=[];return n.forEach(function(t){void 0!==t&&(null===t?r.push(l(e)):r.push(l(e)+"="+l(t)))}),r.join("&")}return l(e)+"="+l(n)}).filter(function(t){return t.length>0}).join("&"):null;return e?"?"+e:""}var m=/\/?$/;function y(t,e,n,r){var o=r&&r.options.stringifyQuery,i=e.query||{};try{i=g(i)}catch(s){}var a={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:i,params:e.params||{},fullPath:w(e,o),matched:t?b(t):[]};return n&&(a.redirectedFrom=w(n,o)),Object.freeze(a)}function g(t){if(Array.isArray(t))return t.map(g);if(t&&"object"===typeof t){var e={};for(var n in t)e[n]=g(t[n]);return e}return t}var _=y(null,{path:"/"});function b(t){var e=[];while(t)e.unshift(t),t=t.parent;return e}function w(t,e){var n=t.path,r=t.query;void 0===r&&(r={});var o=t.hash;void 0===o&&(o="");var i=e||v;return(n||"/")+i(r)+o}function C(t,e){return e===_?t===e:!!e&&(t.path&&e.path?t.path.replace(m,"")===e.path.replace(m,"")&&t.hash===e.hash&&x(t.query,e.query):!(!t.name||!e.name)&&(t.name===e.name&&t.hash===e.hash&&x(t.query,e.query)&&x(t.params,e.params)))}function x(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var n=Object.keys(t),r=Object.keys(e);return n.length===r.length&&n.every(function(n){var r=t[n],o=e[n];return"object"===typeof r&&"object"===typeof o?x(r,o):String(r)===String(o)})}function $(t,e){return 0===t.path.replace(m,"/").indexOf(e.path.replace(m,"/"))&&(!e.hash||t.hash===e.hash)&&A(t.query,e.query)}function A(t,e){for(var n in e)if(!(n in t))return!1;return!0}var k,O=[String,Object],E=[String,Array],S={name:"router-link",props:{to:{type:O,required:!0},tag:{type:String,default:"a"},exact:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,event:{type:E,default:"click"}},render:function(t){var e=this,n=this.$router,r=this.$route,o=n.resolve(this.to,r,this.append),i=o.location,a=o.route,s=o.href,c={},u=n.options.linkActiveClass,f=n.options.linkExactActiveClass,l=null==u?"router-link-active":u,p=null==f?"router-link-exact-active":f,d=null==this.activeClass?l:this.activeClass,h=null==this.exactActiveClass?p:this.exactActiveClass,v=i.path?y(null,i,null,n):a;c[h]=C(r,v),c[d]=this.exact?c[h]:$(r,v);var m=function(t){j(t)&&(e.replace?n.replace(i):n.push(i))},g={click:j};Array.isArray(this.event)?this.event.forEach(function(t){g[t]=m}):g[this.event]=m;var _={class:c};if("a"===this.tag)_.on=g,_.attrs={href:s};else{var b=T(this.$slots.default);if(b){b.isStatic=!1;var w=k.util.extend,x=b.data=w({},b.data);x.on=g;var A=b.data.attrs=w({},b.data.attrs);A.href=s}else _.on=g}return t(this.tag,_,this.$slots.default)}};function j(t){if(!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)&&!t.defaultPrevented&&(void 0===t.button||0===t.button)){if(t.currentTarget&&t.currentTarget.getAttribute){var e=t.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(e))return}return t.preventDefault&&t.preventDefault(),!0}}function T(t){if(t)for(var e,n=0;n=0&&(e=t.slice(r),t=t.slice(0,r));var o=t.indexOf("?");return o>=0&&(n=t.slice(o+1),t=t.slice(0,o)),{path:t,query:n,hash:e}}function L(t){return t.replace(/\/\//g,"/")}var N=Array.isArray||function(t){return"[object Array]"==Object.prototype.toString.call(t)},D=rt,U=B,V=z,F=K,H=nt,q=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function B(t,e){var n,r=[],o=0,i=0,a="",s=e&&e.delimiter||"/";while(null!=(n=q.exec(t))){var c=n[0],u=n[1],f=n.index;if(a+=t.slice(i,f),i=f+c.length,u)a+=u[1];else{var l=t[i],p=n[2],d=n[3],h=n[4],v=n[5],m=n[6],y=n[7];a&&(r.push(a),a="");var g=null!=p&&null!=l&&l!==p,_="+"===m||"*"===m,b="?"===m||"*"===m,w=n[2]||s,C=h||v;r.push({name:d||o++,prefix:p||"",delimiter:w,optional:b,repeat:_,partial:g,asterisk:!!y,pattern:C?J(C):y?".*":"[^"+X(w)+"]+?"})}}return i-1&&(s.params[p]=n.params[p]);if(u)return s.path=it(u.path,s.params,'named route "'+c+'"'),f(u,s,a)}else if(s.path){s.params={};for(var d=0;d=t.length?n():t[o]?e(t[o],function(){r(o+1)}):r(o+1)};r(0)}function Rt(t){return function(e,n,r){var i=!1,a=0,s=null;Lt(t,function(t,e,n,c){if("function"===typeof t&&void 0===t.cid){i=!0,a++;var u,f=Vt(function(e){Ut(e)&&(e=e.default),t.resolved="function"===typeof e?e:k.extend(e),n.components[c]=e,a--,a<=0&&r()}),l=Vt(function(t){var e="Failed to resolve async component "+c+": "+t;s||(s=o(t)?t:new Error(e),r(s))});try{u=t(f,l)}catch(d){l(d)}if(u)if("function"===typeof u.then)u.then(f,l);else{var p=u.component;p&&"function"===typeof p.then&&p.then(f,l)}}}),i||r()}}function Lt(t,e){return Nt(t.map(function(t){return Object.keys(t.components).map(function(n){return e(t.components[n],t.instances[n],t,n)})}))}function Nt(t){return Array.prototype.concat.apply([],t)}var Dt="function"===typeof Symbol&&"symbol"===typeof Symbol.toStringTag;function Ut(t){return t.__esModule||Dt&&"Module"===t[Symbol.toStringTag]}function Vt(t){var e=!1;return function(){var n=[],r=arguments.length;while(r--)n[r]=arguments[r];if(!e)return e=!0,t.apply(this,n)}}var Ft=function(t,e){this.router=t,this.base=Ht(e),this.current=_,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[]};function Ht(t){if(!t)if(M){var e=document.querySelector("base");t=e&&e.getAttribute("href")||"/",t=t.replace(/^https?:\/\/[^\/]+/,"")}else t="/";return"/"!==t.charAt(0)&&(t="/"+t),t.replace(/\/$/,"")}function qt(t,e){var n,r=Math.max(t.length,e.length);for(n=0;n=0?e.slice(0,n):e;return r+"#"+t}function ie(t){kt?It(oe(t)):window.location.hash=t}function ae(t){kt?Mt(oe(t)):window.location.replace(oe(t))}var se=function(t){function e(e,n){t.call(this,e,n),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,n){var r=this;this.transitionTo(t,function(t){r.stack=r.stack.slice(0,r.index+1).concat(t),r.index++,e&&e(t)},n)},e.prototype.replace=function(t,e,n){var r=this;this.transitionTo(t,function(t){r.stack=r.stack.slice(0,r.index).concat(t),e&&e(t)},n)},e.prototype.go=function(t){var e=this,n=this.index+t;if(!(n<0||n>=this.stack.length)){var r=this.stack[n];this.confirmTransition(r,function(){e.index=n,e.updateRoute(r)})}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},e.prototype.ensureURL=function(){},e}(Ft),ce=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=pt(t.routes||[],this);var e=t.mode||"hash";switch(this.fallback="history"===e&&!kt&&!1!==t.fallback,this.fallback&&(e="hash"),M||(e="abstract"),this.mode=e,e){case"history":this.history=new Yt(this,t.base);break;case"hash":this.history=new te(this,t.base,this.fallback);break;case"abstract":this.history=new se(this,t.base);break;default:0}},ue={currentRoute:{configurable:!0}};function fe(t,e){return t.push(e),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}function le(t,e,n){var r="hash"===n?"#"+e:e;return t?L(t+"/"+r):r}ce.prototype.match=function(t,e,n){return this.matcher.match(t,e,n)},ue.currentRoute.get=function(){return this.history&&this.history.current},ce.prototype.init=function(t){var e=this;if(this.apps.push(t),!this.app){this.app=t;var n=this.history;if(n instanceof Yt)n.transitionTo(n.getCurrentLocation());else if(n instanceof te){var r=function(){n.setupListeners()};n.transitionTo(n.getCurrentLocation(),r,r)}n.listen(function(t){e.apps.forEach(function(e){e._route=t})})}},ce.prototype.beforeEach=function(t){return fe(this.beforeHooks,t)},ce.prototype.beforeResolve=function(t){return fe(this.resolveHooks,t)},ce.prototype.afterEach=function(t){return fe(this.afterHooks,t)},ce.prototype.onReady=function(t,e){this.history.onReady(t,e)},ce.prototype.onError=function(t){this.history.onError(t)},ce.prototype.push=function(t,e,n){this.history.push(t,e,n)},ce.prototype.replace=function(t,e,n){this.history.replace(t,e,n)},ce.prototype.go=function(t){this.history.go(t)},ce.prototype.back=function(){this.go(-1)},ce.prototype.forward=function(){this.go(1)},ce.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map(function(t){return Object.keys(t.components).map(function(e){return t.components[e]})})):[]},ce.prototype.resolve=function(t,e,n){var r=ft(t,e||this.history.current,n,this),o=this.match(r,e),i=o.redirectedFrom||o.fullPath,a=this.history.base,s=le(a,i,this.mode);return{location:r,route:o,href:s,normalizedTo:r,resolved:o}},ce.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==_&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(ce.prototype,ue),ce.install=I,ce.version="3.0.1",M&&window.Vue&&window.Vue.use(ce),e["a"]=ce},c8ba:function(t,e){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(r){"object"===typeof window&&(n=window)}t.exports=n}}]);
--------------------------------------------------------------------------------