├── 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 | 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 | 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 | 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 | 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 | 10 | 11 | 12 | 21 | -------------------------------------------------------------------------------- /front-end/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 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 | 32 | 33 | 83 | -------------------------------------------------------------------------------- /front-end/src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 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 | 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 | 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}}]); --------------------------------------------------------------------------------