本日、このトレーニングプロジェクトの作業は完了します。 つまり、この記事では、新しい顧客の記録と財務書類をシステムに追加するページの開発と、このデータを編集するメカニズムの作成について説明します。 ここでは、APIの改善点をいくつか見て、Budget Managerを稼働状態にします。
APIの改訂
まず、
models
フォルダーに移動し、
budget.js
ファイルを開きます。 モデルの
description
フィールドを追加します。
description: { type: String, required: true },
app/api
フォルダーに移動し、その中にある
budget.js
ファイルを開きます。 ここでは、新しいドキュメントが正しく処理されるようにデータストレージ関数
store
を編集し、ドキュメントを編集できる
edit
機能を追加し、ドキュメントを削除するために必要な
remove
関数を追加し、ドキュメントをフィルター処理できる
getByState
関数を追加します。 完全なファイルコードを次に示します。 表示するには、対応するブロックを展開します。 将来、同じ方法で大きなコードフラグメントが発行されます。
ソースコード
const mongoose = require('mongoose'); const api = {}; api.store = (User, Budget, Client, Token) => (req, res) => { if (Token) { Client.findOne({ _id: req.body.client }, (error, client) => { if (error) res.status(400).json(error); if (client) { const budget = new Budget({ client_id: req.body.client, user_id: req.query.user_id, client: client.name, state: req.body.state, description: req.body.description, title: req.body.title, total_price: req.body.total_price, items: req.body.items }); budget.save(error => { if (error) return res.status(400).json(error) res.status(200).json({ success: true, message: "Budget registered successfully" }) }) } else { res.status(400).json({ success: false, message: "Invalid client" }) } }) } else return res.status(401).send({ success: false, message: 'Unauthorized' }); } api.getAll = (User, Budget, Token) => (req, res) => { if (Token) { Budget.find({ user_id: req.query.user_id }, (error, budget) => { if (error) return res.status(400).json(error); res.status(200).json(budget); return true; }) } else return res.status(403).send({ success: false, message: 'Unauthorized' }); } api.getAllFromClient = (User, Budget, Token) => (req, res) => { if (Token) { Budget.find({ client_id: req.query.client_id }, (error, budget) => { if (error) return res.status(400).json(error); res.status(200).json(budget); return true; }) } else return res.status(401).send({ success: false, message: 'Unauthorized' }); } api.index = (User, Budget, Client, Token) => (req, res) => { if (Token) { User.findOne({ _id: req.query.user_id }, (error, user) => { if (error) res.status(400).json(error); if (user) { Budget.findOne({ _id: req.query._id }, (error, budget) => { if (error) res.status(400).json(error); res.status(200).json(budget); }) } else { res.status(400).json({ success: false, message: "Invalid budget" }) } }) } else return res.status(401).send({ success: false, message: 'Unauthorized' }); } api.edit = (User, Budget, Client, Token) => (req, res) => { if (Token) { User.findOne({ _id: req.query.user_id }, (error, user) => { if (error) res.status(400).json(error); if (user) { Budget.findOneAndUpdate({ _id: req.body._id }, req.body, (error, budget) => { if (error) res.status(400).json(error); res.status(200).json(budget); }) } else { res.status(400).json({ success: false, message: "Invalid budget" }) } }) } else return res.status(401).send({ success: false, message: 'Unauthorized' }); } api.getByState = (User, Budget, Client, Token) => (req, res) => { if (Token) { User.findOne({ _id: req.query.user_id }, (error, user) => { if (error) res.status(400).json(error); if (user) { Budget.find({ state: req.query.state }, (error, budget) => { console.log(budget) if (error) res.status(400).json(error); res.status(200).json(budget); }) } else { res.status(400).json({ success: false, message: "Invalid budget" }) } }) } else return res.status(401).send({ success: false, message: 'Unauthorized' }); } api.remove = (User, Budget, Client, Token) => (req, res) => { if (Token) { Budget.remove({ _id: req.query._id }, (error, removed) => { if (error) res.status(400).json(error); res.status(200).json({ success: true, message: 'Removed successfully' }); }) } else return res.status(401).send({ success: false, message: 'Unauthorized' }); } module.exports = api;
api
フォルダーからclient.jsファイルに同様の変更を加えます。
ソースコード
const mongoose = require('mongoose'); const api = {}; api.store = (User, Client, Token) => (req, res) => { if (Token) { User.findOne({ _id: req.query.user_id }, (error, user) => { if (error) res.status(400).json(error); if (user) { const client = new Client({ user_id: req.query.user_id, name: req.body.name, email: req.body.email, phone: req.body.phone, }); client.save(error => { if (error) return res.status(400).json(error); res.status(200).json({ success: true, message: "Client registration successful" }); }) } else { res.status(400).json({ success: false, message: "Invalid client" }) } }) } else return res.status(403).send({ success: false, message: 'Unauthorized' }); } api.getAll = (User, Client, Token) => (req, res) => { if (Token) { Client.find({ user_id: req.query.user_id }, (error, client) => { if (error) return res.status(400).json(error); res.status(200).json(client); return true; }) } else return res.status(403).send({ success: false, message: 'Unauthorized' }); } api.index = (User, Client, Token) => (req, res) => { if (Token) { User.findOne({ _id: req.query.user_id }, (error, user) => { if (error) res.status(400).json(error); if (user) { Client.findOne({ _id: req.query._id }, (error, client) => { if (error) res.status(400).json(error); res.status(200).json(client); }) } else { res.status(400).json({ success: false, message: "Invalid client" }) } }) } else return res.status(401).send({ success: false, message: 'Unauthorized' }); } api.edit = (User, Client, Token) => (req, res) => { if (Token) { User.findOne({ _id: req.query.user_id }, (error, user) => { if (error) res.status(400).json(error); if (user) { Client.findOneAndUpdate({ _id: req.body._id }, req.body, (error, client) => { if (error) res.status(400).json(error); res.status(200).json(client); }) } else { res.status(400).json({ success: false, message: "Invalid client" }) } }) } else return res.status(401).send({ success: false, message: 'Unauthorized' }); } api.remove = (User, Client, Token) => (req, res) => { if (Token) { User.findOne({ _id: req.query.user_id }, (error, user) => { if (error) res.status(400).json(error); if (user) { Client.remove({ _id: req.query._id }, (error, removed) => { if (error) res.status(400).json(error); res.status(200).json({ success: true, message: 'Removed successfully' }); }) } else { res.status(400).json({ success: false, message: "Invalid client" }) } }) } else return res.status(401).send({ success: false, message: 'Unauthorized' }); } module.exports = api;
最後に、システムに新しいルートを追加します。 これを行うには、
routes
フォルダーに移動して
budget.js
ファイルを開きます。
ソースコード
const passport = require('passport'), config = require('@config'), models = require('@BudgetManager/app/setup'); module.exports = (app) => { const api = app.BudgetManagerAPI.app.api.budget; app.route('/api/v1/budget') .post(passport.authenticate('jwt', config.session), api.store(models.User, models.Budget, models.Client, app.get('budgetsecret'))) .get(passport.authenticate('jwt', config.session), api.getAll(models.User, models.Budget, app.get('budgetsecret'))) .get(passport.authenticate('jwt', config.session), api.getAllFromClient(models.User, models.Budget, app.get('budgetsecret'))) .delete(passport.authenticate('jwt', config.session), api.remove(models.User, models.Budget, models.Client, app.get('budgetsecret'))) app.route('/api/v1/budget/single') .get(passport.authenticate('jwt', config.session), api.index(models.User, models.Budget, models.Client, app.get('budgetsecret'))) .put(passport.authenticate('jwt', config.session), api.edit(models.User, models.Budget, models.Client, app.get('budgetsecret'))) app.route('/api/v1/budget/state') .get(passport.authenticate('jwt', config.session), api.getByState(models.User, models.Budget, models.Client, app.get('budgetsecret'))) }
同じフォルダーにある
client.js
ファイルに同様の変更を加えます。
ソースコード
const passport = require('passport'), config = require('@config'), models = require('@BudgetManager/app/setup'); module.exports = (app) => { const api = app.BudgetManagerAPI.app.api.client; app.route('/api/v1/client') .post(passport.authenticate('jwt', config.session), api.store(models.User, models.Client, app.get('budgetsecret'))) .get(passport.authenticate('jwt', config.session), api.getAll(models.User, models.Client, app.get('budgetsecret'))) .delete(passport.authenticate('jwt', config.session), api.remove(models.User, models.Client, app.get('budgetsecret'))) app.route('/api/v1/client/single') .get(passport.authenticate('jwt', config.session), api.index(models.User, models.Client, app.get('budgetsecret'))) .put(passport.authenticate('jwt', config.session), api.edit(models.User, models.Client, app.get('budgetsecret'))) }
APIに対して行う必要があるすべての変更です。
ルーターの改良
次に、ルートに新しいコンポーネントを追加します。 これを行うには、
router
フォルダー内にある
index.js
ファイルを開きます。
ソースコード
... // Global components import Header from '@/components/Header' import List from '@/components/List/List' import Create from '@/components/pages/Create' // Register components Vue.component('app-header', Header) Vue.component('list', List) Vue.component('create', Create) Vue.use(Router) const router = new Router({ routes: [ { path: '/', name: 'Home', components: { default: Home, header: Header, list: List, create: Create } }, { path: '/login', name: 'Authentication', component: Authentication } ] }) …
ここで、
Create
コンポーネントをインポートして定義し、
Home
ルートコンポーネントに割り当てました(以下でコンポーネントを作成します)。
新しいコンポーネントの作成
componentコンポーネントの作成
Create
コンポーネントから始めましょう。
components/pages
フォルダーに移動し、
Create.vue
新しい
Create.vue
ファイルを作成します。
ソースコード
<template> <div class="l-create-page"> <budget-creation v-if="budgetCreation && !editPage" slot="budget-creation" :clients="clients" :saveBudget="saveBudget"></budget-creation> <client-creation v-if="!budgetCreation && !editPage" slot="client-creation" :saveClient="saveClient"></client-creation> <budget-edit v-else-if="budgetEdit && editPage" slot="budget-creation" :clients="clients" :selectedBudget="budget" :fixClientNameAndUpdate="fixClientNameAndUpdate"> </budget-edit> <client-edit v-else-if="!budgetEdit && editPage" slot="client-creation" :selectedClient="client" :updateClient="updateClient"> </client-edit> </div> </template> <script> import BudgetCreation from './../Creation/BudgetCreation' import ClientCreation from './../Creation/ClientCreation' import BudgetEdit from './../Creation/BudgetEdit' import ClientEdit from './../Creation/ClientEdit' export default { props: [ 'budgetCreation', 'clients', 'saveBudget', 'saveClient', 'budget', 'client', 'updateClient', 'fixClientNameAndUpdate', 'editPage', 'budgetEdit' ], components: { 'budget-creation': BudgetCreation, 'client-creation': ClientCreation, 'budget-edit': BudgetEdit, 'client-edit': ClientEdit } } </script>
最初の名前付きスロットは
budget-creation
です。 これは、新しい財務ドキュメントを作成するために使用するコンポーネントを表します。
budgetCreation
プロパティ
budgetCreation
true
設定され、
editPage
が
false
に設定されている場合にのみ表示され
false
。すべてのクライアントと
saveBudget
メソッドを
saveBudget
ます。
2番目の名前付きスロットは
client-creation
です。 これは、新しい顧客を作成するために使用されるコンポーネントです。
budgetCreation
プロパティ
budgetCreation
false
に設定され、
editPage
も
false
場合にのみ表示され
false
。 ここで、saveClientメソッドを渡します。
3番目の名前付きスロットは
budget-edit
です。 これは、選択したドキュメントの編集に使用されるコンポーネントです。
editPage
と
editPage
true
設定されて
editPage
場合にのみ
budgetEdit
され
true
。 ここでは、すべてのクライアント、選択した財務ドキュメント、および
fixClientNameAndUpdate
メソッドを転送します。
そして最後に、顧客情報の編集に使用される最後の名前付きスロットがあります。
budgetEdit
プロパティ
budgetEdit
false
に設定され、
editPage
が
true
設定されている
true
され
true
。 選択したクライアントと
updateClient
メソッドを渡します。
予算BudgetCreationコンポーネント
新しい財務ドキュメントの作成に使用されるコンポーネントを開発します。
components
フォルダーに移動して、その中に新しいフォルダーを作成し、それに
Creation
という名前を付けます。 このフォルダーで、
BudgetCreation.vue
コンポーネント
BudgetCreation.vue
作成し
BudgetCreation.vue
。
コンポーネントは非常に大きいため、テンプレートから始めて段階的に分析します。
BudgetCreationコンポーネントテンプレート
コンポーネントテンプレートコードは次のとおりです
<template> <div class="l-budget-creation"> <v-layout row wrap> <span class="md-budget-state-hint uppercased white--text">status</span> <v-flex xs12 md2> <v-select label="Status" :items="states" v-model="budget.state" > </v-select> </v-flex> <v-flex xs12 md9 offset-md1> <v-select label="Client" :items="clients" v-model="budget.client" item-text="name" item-value="_id" > </v-select> </v-flex> <v-flex xs12 md12> <v-text-field label="Title" v-model="budget.title" required color="light-blue lighten-1"> </v-text-field> <v-text-field label="Description" v-model="budget.description" textarea required color="light-blue lighten-1"> </v-text-field> </v-flex> <v-layout row wrap v-for="item in budget.items" class="l-budget-item" :key="item.id"> <v-flex xs12 md1> <v-btn block dark color="red lighten-1" @click.native="removeItem(item)">Remove</v-btn> </v-flex> <v-flex xs12 md3 offset-md1> <v-text-field label="Title" box dark v-model="item.title" required color="light-blue lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md1 offset-md1> <v-text-field label="Price" box dark prefix="$" v-model="item.price" required color="light-blue lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md2 offset-md1> <v-text-field label="Quantity" box dark min="0" v-model="item.quantity" type="number" required color="light-blue lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md2> <span class="md-budget-item-subtotal white--text">ITEM PRICE $ {{ item.subtotal }}</span> </v-flex> </v-layout> <v-flex xs12 md2 offset-md10> <v-btn block color="md-add-item-btn light-blue lighten-1" @click.native="addItem()">Add item</v-btn> </v-flex> <v-flex xs12 md2 offset-md10> <span class="md-budget-item-total white--text">TOTAL $ {{ budget.total_price }}</span> </v-flex> <v-flex xs12 md2 offset-md10> <v-btn block color="md-add-item-btn green lighten-1" @click.native="saveBudget(budget)">Save</v-btn> </v-flex> </v-layout> </div> </template>
ここでは、最初に
v-select
要素をテンプレートに追加してドキュメントの状態を設定し、次に
v-select
を使用して必要なクライアントを選択します。 次に、ドキュメントのタイトルを入力する
v-text-field
と説明を表示する
v-text-field
があります。
次に、
budget.items
要素を
budget.items
します。これにより、ドキュメントに要素を追加して削除する機会が与えられます。 また、
removeItem
関数を呼び出して、削除する要素を渡すことができる赤いボタンもあります。
さらに、製品名、単価、数量をそれぞれ対象とする3つの
v-text-fields
があります。
シリーズの最後には単純な
span
要素があり、商品の数量と価格の積である
subtotal
という行に
subtotal
を表示します。
製品リストの下にはさらに3つのアイテムがあります。 これは、
addItem
関数、ドキュメント内のすべての商品の合計コストを表示する
span
要素(すべての要素の
subtotal
合計)を呼び出して新しい要素を追加するために使用される青いボタン、およびドキュメントをデータベースに保存するために使用される緑のボタン
saveBudget
関数を呼び出して、保存するドキュメントをパラメーターとして渡します。
BudgetCreationコンポーネントスクリプト
BudgetCreationコンポーネントを強化するコードは次のとおりです。
<script> export default { props: ['clients', 'saveBudget'], data () { return { budget: { title: null, description: null, state: 'writing', client: null, get total_price () { let value = 0 this.items.forEach(({ subtotal }) => { value += parseInt(subtotal) }) return value }, items: [ { title: null, quantity: 0, price: 0, get subtotal () { return this.quantity * this.price } } ] }, states: [ 'writing', 'editing', 'pending', 'approved', 'denied', 'waiting' ] } }, methods: { addItem () { const items = this.budget.items const item = { title: '', quantity: 0, price: 0, get subtotal () { return this.quantity * this.price } } items.push(item) }, removeItem (selected) { const items = this.budget.items items.forEach((item, index) => { if (item === selected) { items.splice(index, 1) } }) } } } </script>
このコードでは、
clients
と
saveBudget
2つのプロパティを最初に取得し
clients
。 これらのプロパティのソースは、
Home
コンポーネントです。
次に、データの役割を果たすオブジェクトと配列を定義します。 オブジェクトの名前は
budget
です。 ドキュメントの作成に使用され、値を追加してデータベースに保存できます。 このオブジェクトには、プロパティ
title
(title)、
description
(description)、
state
(デフォルトのstateを
writing
設定)、
client
(client)、
total_price
(ドキュメントの総コスト)、items
items
配列があり
items
。 商品には、
title
(名前)、
quantity
(数量)、
price
(価格)、および
subtotal
(小計)のプロパティがあります。
ここでは、ドキュメントの
states
の配列
states
が定義されています。 その値は、ドキュメントの状態を設定するために使用されます。 これらの状態は、
writing
、
editing
pending
、
pending
、
approved
、
denied
、
waiting
です。
以下に、データ構造の説明の後に、
addItem
(製品を追加するための)と
removeItem
(それらを削除するための)の2つのメソッドがあり
addItem
。
青いボタンをクリックするたびに、
addItem
メソッドが
addItem
メソッドは、要素を
budget
オブジェクト内の
items
配列に追加します。
removeItem
メソッドは反対のアクションを実行します。 つまり、赤いボタンをクリックすると、指定されたアイテムが
items
配列から削除され
items
。
BudgetCreationコンポーネントスタイル
問題のコンポーネントのスタイルは次のとおりです。
<style lang="scss"> @import "./../../assets/styles"; .uppercased { text-transform: uppercase; } .l-budget-creation { label, input, .icon, .input-group__selections__comma, textarea { color: #29b6f6!important; } .input-group__details { &:before { background-color: $border-color-input !important; } } .input-group__input { border-color: $border-color-input !important; .input-group--text-field__prefix { margin-bottom: 3px !important; } } .input-group--focused { .input-group__input { border-color: #29b6f6!important; } } } .md-budget-state-hint { margin: 10px 0; display: block; width: 100%; } .md-budget-state { background-color: rgba(41, 182, 246, .6); display: flex; height: 35px; width: 100%; font-size: 14px; align-items: center; justify-content: center; border-radius: 2px; margin: 10px 0 15px; } .l-budget-item { align-items: center; } .md-budget-item-subtotal { font-size: 16px; text-align: center; display: block; } .md-budget-item-total { font-size: 22px; text-align: center; display: block; width: 100%; margin: 30px 0 10px; } .md-add-item-btn { margin-top: 30px !important; display: block; } .list__tile__title, .input-group__selections { text-transform: uppercase !important; } </style>
次に、次のコンポーネントを検討します。
▍ClientCreationコンポーネント
実際、このコンポーネントは、レビューしたばかりの
BudgetCreation
コンポーネントの簡易バージョンです。 上記で行ったように、我々はそれを部分的に検討します。
BudgetCreation
コンポーネントを理解すれば、
BudgetCreation
を簡単に理解できます。
ClientCreationコンポーネントテンプレート
<template> <div class="l-client-creation"> <v-layout row wrap> <v-flex xs12 md4> <v-text-field label="Name" v-model="client.name" required color="green lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md3 offset-md1> <v-text-field label="Email" v-model="client.email" required color="green lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md3 offset-md1> <v-text-field label="Phone" v-model="client.phone" required mask="phone" color="green lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md2 offset-md10> <v-btn block color="md-add-item-btn green lighten-1" @click.native="saveClient(client)">Save</v-btn> </v-flex> </v-layout> </div> </template>
ClientCreationコンポーネントスクリプト
<script> export default { props: ['saveClient'], data () { return { client: { name: null, email: null, phone: null } } } } </script>
ClientCreationコンポーネントスタイル
<style lang="scss"> @import "./../../assets/styles"; .uppercased { text-transform: uppercase; } .l-client-creation { label, input, .icon, .input-group__selections__comma, textarea { color: #66bb6a!important; } .input-group__details { &:before { background-color: $border-color-input !important; } } .input-group__input { border-color: $border-color-input !important; .input-group--text-field__prefix { margin-bottom: 3px !important; } } .input-group--focused { .input-group__input { border-color: #66bb6a!important; } } } </style>
次に、
BudgetEdit
コンポーネントの
BudgetEdit
です。
▍BudgetEditコンポーネント
実際、このコンポーネントは、すでに考慮されている
BudgetCreation
コンポーネントの修正バージョンです。 そのコンポーネントを検討してください。
BudgetEditコンポーネントテンプレート
<template> <div class="l-budget-creation"> <v-layout row wrap> <span class="md-budget-state-hint uppercased white--text">status</span> <v-flex xs12 md2> <v-select label="Status" :items="states" v-model="budget.state" > </v-select> </v-flex> <v-flex xs12 md9 offset-md1> <v-select label="Client" :items="clients" v-model="budget.client_id" item-text="name" item-value="_id" > </v-select> </v-flex> <v-flex xs12 md12> <v-text-field label="Title" v-model="budget.title" required color="light-blue lighten-1"> </v-text-field> <v-text-field label="Description" v-model="budget.description" textarea required color="light-blue lighten-1"> </v-text-field> </v-flex> <v-layout row wrap v-for="item in budget.items" class="l-budget-item" :key="item.id"> <v-flex xs12 md1> <v-btn block dark color="red lighten-1" @click.native="removeItem(item)">Remove</v-btn> </v-flex> <v-flex xs12 md3 offset-md1> <v-text-field label="Title" box dark v-model="item.title" required color="light-blue lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md1 offset-md1> <v-text-field label="Price" box dark prefix="$" v-model="item.price" required color="light-blue lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md2 offset-md1> <v-text-field label="Quantity" box dark min="0" v-model="item.quantity" type="number" required color="light-blue lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md2> <span class="md-budget-item-subtotal white--text">ITEM PRICE $ {{ item.subtotal }}</span> </v-flex> </v-layout> <v-flex xs12 md2 offset-md10> <v-btn block color="md-add-item-btn light-blue lighten-1" @click.native="addItem()">Add item</v-btn> </v-flex> <v-flex xs12 md2 offset-md10> <span class="md-budget-item-total white--text">TOTAL $ {{ budget.total_price }}</span> </v-flex> <v-flex xs12 md2 offset-md10> <v-btn block color="md-add-item-btn green lighten-1" @click.native="fixClientNameAndUpdate(budget)">Update</v-btn> </v-flex> </v-layout> </div> </template>
BudgetEdit
と
BudgetCreation
コンポーネントテンプレートの唯一の違いは、保存ボタンとその関連ロジックです。 つまり、
BudgetCreation
では
Save
と表示され、
saveBudget
メソッドが呼び出され
Save
。
BudgetEdit
このボタンに碑文の
Update
が表示され、
fixClientNameAndUpdate
メソッドが
fixClientNameAndUpdate
ます。
BudgetCreationコンポーネントスクリプト
<script> export default { props: ['clients', 'fixClientNameAndUpdate', 'selectedBudget'], data () { return { budget: { title: null, description: null, state: 'pending', client: null, get total_price () { let value = 0 this.items.forEach(({ subtotal }) => { value += parseInt(subtotal) }) return value }, items: [ { title: null, quantity: 0, price: null, get subtotal () { return this.quantity * this.price } } ] }, states: [ 'writing', 'editing', 'pending', 'approved', 'denied', 'waiting' ] } }, mounted () { this.parseBudget() }, methods: { addItem () { const items = this.budget.items const item = { title: '', quantity: 0, price: 0, get subtotal () { return this.quantity * this.price } } items.push(item) }, removeItem (selected) { const items = this.budget.items items.forEach((item, index) => { if (item === selected) { items.splice(index, 1) } }) }, parseBudget () { for (let key in this.selectedBudget) { if (key !== 'total' && key !== 'items') { this.budget[key] = this.selectedBudget[key] } if (key === 'items') { const items = this.selectedBudget.items const buildItems = item => ({ title: item.title, quantity: item.quantity, price: item.price, get subtotal () { return this.quantity * this.price } }) const parseItems = items => items.map(buildItems) this.budget.items = parseItems(items) } } } } } </script>
すべて3つのプロパティで始まります。 これらは、
clients
、
fixClientNameAndUpdate
、および
selectedBudget
です。 このデータは、
BudgetCreation
コンポーネントのデータと同じです。 つまり、
Budget
オブジェクトと
states
配列があり
states
。
さらに、ここでは、
mounted
コンポーネントライフサイクルイベントハンドラーを確認できます。このハンドラーでは、
parseBudget
メソッドを呼び出します。 最後に、
BudgetCreation
コンポーネントでおなじみの
addItem
と
removeItem
メソッド、および新しい
parseBudget
メソッドを含む
methods
オブジェクトがあり
addItem
。 このメソッドは、
budget
オブジェクトの値を
selectedBudget
プロパティで渡される値に設定するために使用
selectedBudget
ますが、ドキュメントの商品の小計とドキュメントの合計金額の計算にも使用されます。
BudgetCreationコンポーネントスタイル
<style lang="scss"> @import "./../../assets/styles"; .uppercased { text-transform: uppercase; } .l-budget-creation { label, input, .icon, .input-group__selections__comma, textarea { color: #29b6f6!important; } .input-group__details { &:before { background-color: $border-color-input !important; } } .input-group__input { border-color: $border-color-input !important; .input-group--text-field__prefix { margin-bottom: 3px !important; } } .input-group--focused { .input-group__input { border-color: #29b6f6!important; } } } .md-budget-state-hint { margin: 10px 0; display: block; width: 100%; } .md-budget-state { background-color: rgba(41, 182, 246, .6); display: flex; height: 35px; width: 100%; font-size: 14px; align-items: center; justify-content: center; border-radius: 2px; margin: 10px 0 15px; } .l-budget-item { align-items: center; } .md-budget-item-subtotal { font-size: 16px; text-align: center; display: block; } .md-budget-item-total { font-size: 22px; text-align: center; display: block; width: 100%; margin: 30px 0 10px; } .md-add-item-btn { margin-top: 30px !important; display: block; } .list__tile__title, .input-group__selections { text-transform: uppercase !important; } </style>
▍ClientEditコンポーネント
検討したばかりのこのコンポーネントは、クライアントの作成に使用する対応するコンポーネント
ClientCreation
似ています。 主な違いは、
saveClient
メソッドの代わりに
updateClient
メソッドが
updateClient
です。 デバイスコンポーネント
ClientEdit
検討してください。
ClientEditコンポーネントテンプレート
<template> <div class="l-client-creation"> <v-layout row wrap> <v-flex xs12 md4> <v-text-field label="Name" v-model="client.name" required color="green lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md3 offset-md1> <v-text-field label="Email" v-model="client.email" required color="green lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md3 offset-md1> <v-text-field label="Phone" v-model="client.phone" required mask="phone" color="green lighten-1"> </v-text-field> </v-flex> <v-flex xs12 md2 offset-md10> <v-btn block color="md-add-item-btn green lighten-1" @click.native="updateClient(client)">Update</v-btn> </v-flex> </v-layout> </div> </template>
ClientEditコンポーネントスクリプト
<script> export default { props: ['updateClient', 'selectedClient'], data () { return { client: { name: null, email: null, phone: null } } }, mounted () { this.client = this.selectedClient } } </script>
ClientEditコンポーネントスタイル
<style lang="scss"> @import "./../../assets/styles"; .uppercased { text-transform: uppercase; } .l-client-creation { label, input, .icon, .input-group__selections__comma, textarea { color: #66bb6a!important; } .input-group__details { &:before { background-color: $border-color-input !important; } } .input-group__input { border-color: $border-color-input !important; .input-group--text-field__prefix { margin-bottom: 3px !important; } } .input-group--focused { .input-group__input { border-color: #66bb6a!important; } } } </style>
これにより、新しいコンポーネントの作成が完了し、システムに既に存在していたコンポーネントの処理に進みます。
既存のコンポーネントの改良
これで、既存のコンポーネントにいくつかの変更を加えるだけで、アプリケーションを使用する準備が整います。
ListBody
コンポーネントから始めましょう。
▍ListBodyコンポーネント
ListBodyコンポーネントテンプレート
このコンポーネントのコードは
ListBody.vue
ファイルに保存されていることを思い出してください
ソースコード
<template> <section class="l-list-body"> <div class="md-list-item" v-if="data != null && parsedBudgets === null" v-for="item in data"> <div :class="budgetsVisible ? 'md-budget-info white--text' : 'md-client-info white--text'" v-for="info in item" v-if="info != item._id && info != item.client_id"> {{ info }} </div> <div :class="budgetsVisible ? 'l-budget-actions white--text' : 'l-client-actions white--text'"> <v-btn small flat color="yellow accent-1" @click.native="getItemAndEdit(item)"> <v-icon>mode_edit</v-icon> </v-btn> <v-btn small flat color="red lighten-1" @click.native="deleteItem(item, data, budgetsVisible)"> <v-icon>delete_forever</v-icon> </v-btn> </div> </div> <div class="md-list-item" v-if="parsedBudgets !== null" v-for="item in parsedBudgets"> <div :class="budgetsVisible ? 'md-budget-info white--text' : 'md-client-info white--text'" v-for="info in item" v-if="info != item._id && info != item.client_id"> {{ info }} </div> <div :class="budgetsVisible ? 'l-budget-actions white--text' : 'l-client-actions white--text'"> <v-btn small flat color="yellow accent-1" @click.native="getItemAndEdit(item)"> <v-icon>mode_edit</v-icon> </v-btn> <v-btn small flat color="red lighten-1" @click.native="deleteItem(item, data, budgetsVisible)"> <v-icon>delete_forever</v-icon> </v-btn> </div> </div> </section> </template>
このコンポーネントでは、いくつかの変更と追加を実行するだけで済みます。 そのため、最初に
md-list-item
ブロックの
v-if
コンストラクトに新しい条件を追加します。
parsedBudgets === null
さらに、編集ボタンをクリックしてドキュメントを表示できるため、ドキュメントを表示するために使用した最初のボタンを削除します。
ここでは、
getItemAndEdit
メソッドを新しい最初のボタンに、
deleteItem
メソッドを最後のボタンに追加して、この要素、データ、および
budgetsVisible
変数をパラメーターとして
budgetsVisible
ます。
この下には
md-item-list
ブロックがあり、検索後にフィルター処理されたドキュメントのリストを表示するために使用します。
ListBodyコンポーネントスクリプト
<script> export default { props: ['data', 'budgetsVisible', 'deleteItem', 'getBudget', 'getClient', 'parsedBudgets'], methods: { getItemAndEdit (item) { !item.phone ? this.getBudget(item) : this.getClient(item) } } } </script>
このコンポーネントでは、多くのプロパティを取得します。 それらについて説明します。
-
data
:これはドキュメントのリストまたはクライアントのリストのいずれかですが、両方ではありません。
-
budgetsVisible
:ドキュメントまたはクライアントのリストを表示しているかどうかを確認するために使用されますtrue
またはfalse
場合がありfalse
。
-
deleteItem
:パラメーターとして要素を取る要素を削除する関数。
-
getBudget
:編集する予定の単一のドキュメントをロードするために使用する関数。
-
getClient
:さらに編集するために個々のクライアントのカードをロードするために使用される関数。
-
parsedBudgets
:検索の実行後にフィルター処理されたドキュメント。
コンポーネントには
getItemAndEdit
1つだけあります。 彼は、パラメータとして要素を受け取り、電話番号を含む要素のプロパティの分析に基づいて、要素が顧客のカードであるか財務書類であるかを決定します。
ListBodyコンポーネントスタイル
<style lang="scss"> @import "./../../assets/styles"; .l-list-body { display: flex; flex-direction: column; .md-list-item { width: 100%; display: flex; flex-direction: column; margin: 15px 0; @media (min-width: 960px) { flex-direction: row; margin: 0; } .md-budget-info { flex-basis: 25%; width: 100%; background-color: rgba(0, 175, 255, 0.45); border: 1px solid $border-color-input; padding: 0 15px; display: flex; height: 35px; align-items: center; justify-content: center; &:first-of-type, &:nth-of-type(2) { text-transform: capitalize; } &:nth-of-type(3) { text-transform: uppercase; } @media (min-width: 601px) { justify-content: flex-start; } } .md-client-info { @extend .md-budget-info; background-color: rgba(102, 187, 106, 0.45)!important; &:nth-of-type(2) { text-transform: none; } } .l-budget-actions { flex-basis: 25%; display: flex; background-color: rgba(0, 175, 255, 0.45); border: 1px solid $border-color-input; align-items: center; justify-content: center; .btn { min-width: 45px !important; margin: 0 5px !important; } } .l-client-actions { @extend .l-budget-actions; background-color: rgba(102, 187, 106, 0.45)!important; } } } </style>
ListBody
,
Header
.
▍ Header
Header
<template> <header class="l-header-container"> <v-layout row wrap :class="budgetsVisible ? 'l-budgets-header' : 'l-clients-header'"> <v-flex xs12 md5> <v-text-field v-model="searchValue" label="Search" append-icon="search" :color="budgetsVisible ? 'light-blue lighten-1' : 'green lighten-1'"> </v-text-field> </v-flex> <v-flex xs12 offset-md1 md1> <v-btn block :color="budgetsVisible ? 'light-blue lighten-1' : 'green lighten-1'" @click.native="$emit('toggleVisibleData')"> {{ budgetsVisible ? "Clients" : "Budgets" }} </v-btn> </v-flex> <v-flex xs12 offset-md1 md2> <v-select label="Status" :color="budgetsVisible ? 'light-blue lighten-1' : 'green lighten-1'" v-model="status" :items="statusItems" single-line @change="selectState"> </v-select> </v-flex> <v-flex xs12 offset-md1 md1> <v-btn block color="red lighten-1 white--text" @click.native="submitSignout()">Sign out</v-btn> </v-flex> </v-layout> </header> </template>
, ,
v-model
searchValue
.
,
v-select
,
change
selectState
.
Header
<script> import Authentication from '@/components/pages/Authentication' export default { props: ['budgetsVisible', 'selectState', 'search'], data () { return { searchValue: '', status: '', statusItems: [ 'all', 'approved', 'denied', 'waiting', 'writing', 'editing' ] } }, watch: { 'searchValue': function () { this.$emit('input', this.searchValue) } }, created () { this.searchValue = this.search }, methods: { submitSignout () { Authentication.signout(this, '/login') } } } </script>
—
selectState
, ,
search
, .
search
searchValue
statusItems
.
Header
<script> import Authentication from '@/components/pages/Authentication' export default { props: ['budgetsVisible', 'selectState', 'search'], data () { return { searchValue: '', status: '', statusItems: [ 'all', 'approved', 'denied', 'waiting', 'writing', 'editing' ] } }, watch: { 'searchValue': function () { this.$emit('input', this.searchValue) } }, created () { this.searchValue = this.search }, methods: { submitSignout () { Authentication.signout(this, '/login') } } } </script>
Header
,
Home
.
▍ Home
Home
<template> <main class="l-home-page"> <app-header :budgetsVisible="budgetsVisible" @toggleVisibleData="budgetsVisible = !budgetsVisible; budgetCreation = !budgetCreation" :selectState="selectState" :search="search" v-model="search"> </app-header> <div class="l-home"> <h4 class="white--text text-xs-center my-0"> Focus Budget Manager </h4> <list v-if="listPage"> <list-header slot="list-header" :headers="budgetsVisible ? budgetHeaders : clientHeaders"></list-header> <list-body slot="list-body" :budgetsVisible="budgetsVisible" :data="budgetsVisible ? budgets : clients" :search="search" :deleteItem="deleteItem" :getBudget="getBudget" :getClient="getClient" :parsedBudgets="parsedBudgets"> </list-body> </list> <create v-else-if="createPage" :budgetCreation="budgetCreation" :budgetEdit="budgetEdit" :editPage="editPage" :clients="clients" :budget="budget" :client="client" :saveBudget="saveBudget" :saveClient="saveClient" :fixClientNameAndUpdate="fixClientNameAndUpdate" :updateClient="updateClient"> </create> </div> <v-snackbar :timeout="timeout" bottom="bottom" :color="snackColor" v-model="snackbar"> {{ message }} </v-snackbar> <v-fab-transition> <v-speed-dial v-model="fab" bottom right fixed direction="top" transition="scale-transition"> <v-btn slot="activator" color="red lighten-1" dark fab v-model="fab"> <v-icon>add</v-icon> <v-icon>close</v-icon> </v-btn> <v-tooltip left> <v-btn color="light-blue lighten-1" dark small fab slot="activator" @click.native="budgetCreation = true; listPage = false; editPage = false; createPage = true"> <v-icon>assignment</v-icon> </v-btn> <span>Add new Budget</span> </v-tooltip> <v-tooltip left> <v-btn color="green lighten-1" dark small fab slot="activator" @click.native="budgetCreation = false; listPage = false; editPage = false; createPage = true"> <v-icon>account_circle</v-icon> </v-btn> <span>Add new Client</span> </v-tooltip> <v-tooltip left> <v-btn color="purple lighten-2" dark small fab slot="activator" @click.native="budgetCreation = false; listPage = true; budgetsVisible = true"> <v-icon>assessment</v-icon> </v-btn> <span>List Budgets</span> </v-tooltip> <v-tooltip left> <v-btn color="deep-orange lighten-2" dark small fab slot="activator" @click.native="budgetCreation = false; listPage = true; budgetsVisible = false;"> <v-icon>supervisor_account</v-icon> </v-btn> <span>List Clients</span> </v-tooltip> </v-speed-dial> </v-fab-transition> </main> </template>
, .
budgetsVisible
,
selectState
,
search
toggleVisibleData
, ,
toggleVisibleData
,
v-model
search
.
list
v-if
, , . ,
list-body
.
create
,
list
, , . , .
v-fab-transition
, , .
Home
<script> import Axios from 'axios' import Authentication from '@/components/pages/Authentication' import ListHeader from './../List/ListHeader' import ListBody from './../List/ListBody' const BudgetManagerAPI = `http://${window.location.hostname}:3001` export default { components: { 'list-header': ListHeader, 'list-body': ListBody }, data () { return { parsedBudgets: null, budget: null, client: null, state: null, search: null, budgets: [], clients: [], budgetHeaders: ['Client', 'Title', 'Status', 'Actions'], clientHeaders: ['Client', 'Email', 'Phone', 'Actions'], budgetsVisible: true, snackbar: false, timeout: 6000, message: '', fab: false, listPage: true, createPage: true, editPage: false, budgetCreation: true, budgetEdit: true, snackColor: 'red lighten-1' } }, mounted () { this.getAllBudgets() this.getAllClients() this.hidden = false }, watch: { 'search': function () { if (this.search !== null || this.search !== '') { const searchTerm = this.search const regex = new RegExp(`^(${searchTerm})`, 'g') const results = this.budgets.filter(budget => budget.client.match(regex)) this.parsedBudgets = results } else { this.parsedBudgets = null } } }, methods: { getAllBudgets () { Axios.get(`${BudgetManagerAPI}/api/v1/budget`, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id') } }).then(({data}) => { this.budgets = this.dataParser(data, '_id', 'client', 'title', 'state', 'client_id') }).catch(error => { this.errorHandler(error) }) }, getAllClients () { Axios.get(`${BudgetManagerAPI}/api/v1/client`, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id') } }).then(({data}) => { this.clients = this.dataParser(data, 'name', 'email', '_id', 'phone') }).catch(error => { this.errorHandler(error) }) }, getBudget (budget) { Axios.get(`${BudgetManagerAPI}/api/v1/budget/single`, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id'), _id: budget._id } }).then(({data}) => { this.budget = data this.enableEdit('budget') }).catch(error => { this.errorHandler(error) }) }, getClient (client) { Axios.get(`${BudgetManagerAPI}/api/v1/client/single`, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id'), _id: client._id } }).then(({data}) => { this.client = data this.enableEdit('client') }).catch(error => { this.errorHandler(error) }) }, enableEdit (type) { if (type === 'budget') { this.listPage = false this.budgetEdit = true this.budgetCreation = false this.editPage = true } else if (type === 'client') { this.listPage = false this.budgetEdit = false this.budgetCreation = false this.editPage = true } }, saveBudget (budget) { Axios.post(`${BudgetManagerAPI}/api/v1/budget`, budget, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id') } }) .then(res => { this.resetFields(budget) this.snackbar = true this.message = res.data.message this.snackColor = 'green lighten-1' this.getAllBudgets() }) .catch(error => { this.errorHandler(error) }) }, fixClientNameAndUpdate (budget) { this.clients.find(client => { if (client._id === budget.client_id) { budget.client = client.name } }) this.updateBudget(budget) }, updateBudget (budget) { Axios.put(`${BudgetManagerAPI}/api/v1/budget/single`, budget, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id') } }) .then(() => { this.snackbar = true this.message = 'Budget updated' this.snackColor = 'green lighten-1' this.listPage = true this.budgetCreation = false this.budgetsVisible = true this.getAllBudgets() }) .catch(error => { this.errorHandler(error) }) }, updateClient (client) { Axios.put(`${BudgetManagerAPI}/api/v1/client/single`, client, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id') } }) .then(() => { this.snackbar = true this.message = 'Client updated' this.snackColor = 'green lighten-1' this.listPage = true this.budgetCreation = false this.budgetsVisible = false this.getAllClients() }) .catch(error => { this.errorHandler(error) }) }, saveClient (client) { Axios.post(`${BudgetManagerAPI}/api/v1/client`, client, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id') } }) .then(res => { this.resetFields(client) this.snackbar = true this.message = res.data.message this.snackColor = 'green lighten-1' this.getAllClients() }) .catch(error => { this.errorHandler(error) }) }, deleteItem (selected, items, api) { let targetApi = '' api ? targetApi = 'budget' : targetApi = 'client' Axios.delete(`${BudgetManagerAPI}/api/v1/${targetApi}`, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id'), _id: selected._id } }) .then(() => { this.removeItem(selected, items) }) .then(() => { api ? this.getAllBudgets() : this.getAllClients() }) .catch(error => { this.errorHandler(error) }) }, errorHandler (error) { const status = error.response.status this.snackbar = true this.snackColor = 'red lighten-1' if (status === 404) { this.message = 'Invalid request' } else if (status === 401 || status === 403) { this.message = 'Unauthorized' } else if (status === 400) { this.message = 'Invalid or missing information' } else { this.message = error.message } }, removeItem (selected, items) { items.forEach((item, index) => { if (item === selected) { items.splice(index, 1) } }) }, dataParser (targetedArray, ...options) { let parsedData = [] targetedArray.forEach(item => { let parsedItem = {} options.forEach(option => (parsedItem[option] = item[option])) parsedData.push(parsedItem) }) return parsedData }, resetFields (item) { for (let key in item) { item[key] = null if (key === 'quantity' || key === 'price') { item[key] = 0 } item['items'] = [] } }, selectState (state) { this.state = state state === 'all' ? this.getAllBudgets() : this.getBudgetsByState(state) }, getBudgetsByState (state) { Axios.get(`${BudgetManagerAPI}/api/v1/budget/state`, { headers: { 'Authorization': Authentication.getAuthenticationHeader(this) }, params: { user_id: this.$cookie.get('user_id'), state } }).then(({data}) => { this.budgets = this.dataParser(data, '_id', 'client', 'title', 'state', 'client_id') }).catch(error => { this.errorHandler(error) }) } } } </script>
. .
-
parsedBudgets
: , .
-
budget
: , .
-
client
: , .
-
state
: , , .
-
search
: , .
-
budgets
: , API.
-
clients
: , API.
-
budgetHeaders
: , .
-
clientHeaders
: , , .
-
budgetsVisible
: , .
-
snackbar
: .
-
timeout
: - .
-
message
: , .
-
fab
: ,false
.
-
listPage
: , ,true
.
-
createPage
: , ,false
.
-
editPage
: , ,false
.
-
budgetCreation
: , ,true
.
-
budgetEdit
: , ,true
.
-
snackColor
: .
, , ,
mounted
, .
, ,
watch
. @mrmonkeytech , ( ).
.
-
getAllBudgets
dataParser
,errorHandler
catch
.getAllClients
.
-
getBudget
getClient
, API.
-
enableEdit
, , , .
-
saveBudget
saveClient
, , .
-
fixClientNameAndUpdate
,ID
,updateBudget
.
-
updateBudget
.
-
updateClient
.
-
deleteItem
. ,selected
,items
( ),api
.
-
errorHandler
.
-
removeItem
deleteItem
, .
-
dataParser
, , .
-
resetFields
. , , .
-
selectState
v-select
Header
.
-
getBudgetsByState
selectState
, .
Home
<style lang="scss"> @import "./../../assets/styles"; .l-home { background-color: $background-color; margin: 25px auto; padding: 15px; min-width: 272px; } .snack__content { justify-content: center !important; } </style>
まとめ
- Budget Manager . .
, , — .
, , , , .
親愛なる読者! , ?