18. Vergi hesaplama sunucusunun Vue.js istemcisi
18.1. Architecture
Aşağıdaki mimariye sahip bir istemci/sunucu uygulaması geliştireceğiz:

Vergi hesaplama sunucusu, |https://tahe.developpez.com/tutoriels-cours/php7| belgesinde geliştirilen sürüm 14 olacaktır
18.2. Uygulamanın görünümleri
[vuejs-10] uygulamasının görünümleri, vergi hesaplama sunucusunun |https://tahe.developpez.com/tutoriels-cours/php7| belgesindeki 13. sürümüne aittir; bu sunucu, HTML modunda kullanıldığında geçerlidir. Ancak mevcut uygulamada bu görünümler, PHP sunucusu tarafından değil, JavaScript istemcisi tarafından oluşturulacaktır.
İlk görünüm, kimlik doğrulama görünümüdür:

İkinci görünüm, vergi hesaplama görünümüdür:

Üçüncü görünüm, kullanıcı tarafından yapılan simülasyonların listesini gösteren görünümdür:

Yukarıdaki ekran, 1 numaralı simülasyonun silinebileceğini göstermektedir. Bunun üzerine aşağıdaki görünüm elde edilir:

Şimdi son simülasyonu silersek, aşağıdaki yeni ekran karşımıza çıkar:

18.3. [vuejs-20] projesinin öğeleri
[vuejs-20] projesinin ağaç yapısı şöyledir:

Proje öğeleri şunlardır:
- [assets/logo.jpg]: projenin logosu;
- [couches]: uygulamanın [métier] ve [dao] katmanları;
- [plugins]: uygulamanın eklentileri;
- [views]: uygulamanın görünümleri;
- [config.js]: uygulamayı yapılandırır;
- [router.js]: uygulamanın yönlendirmesini tanımlar;
- [store.js]: [Vuex]'in deposu;
- [main.js]: uygulamanın ana komut dosyası;
18.3.1. [métier] ve [dao] katmanları
18.3.1.1. [dao] katmanı
[dao] katmanı, |vuejs-10| paragrafındaki [Dao] sınıfı tarafından uygulanır
18.3.1.2. [métier] katmanı
[métier] katmanı, |https://tahe.developpez.com/tutoriels-cours/php7| belgesindeki [Métier] sınıfı tarafından uygulanmaktadır. Buraya aşağıdaki [setTaxAdminData] yöntemi eklenmiştir:
// oluşturucu
constructor(taxAdmindata) {
// this.taxAdminData : vergi idaresi verileri
this.taxAdminData = taxAdmindata;
}
// ayarlayıcı
setTaxAdminData(taxAdmindata) {
// this.taxAdminData: vergi idaresi verileri
this.taxAdminData = taxAdmindata;
}
[setTaxAdminData] yöntemi, oluşturucu ile aynı işlevi görür. Bu yöntemin varlığı, aşağıdaki dizinin kullanılmasını sağlar:
- [Métier] sınıfını, sınıfı oluşturmak istediğimizde ancak [taxAdminData] verisine henüz sahip olmadığımızda bir [métier=new Métier()] komutuyla oluşturmak;
- daha sonra [taxAdminData] özelliğini bir [métier.setTaxAdminData(taxAdmindata)] işlemiyle doldurmak;
18.3.2. [config] yapılandırma dosyası
[config.js] dosyası şu şekildedir:
// QZXW2HTML kütüphanesinin kullanımı BW2F4aW9zXQZQX
const axios = require('axios');
// istek zaman aşımı HTTP
axios.defaults.timeout = 2000;
// vergi hesaplama sunucusunun URL veritabanı
// [https] şeması, vergi hesaplama sunucusu
// kendi imzaladığı bir sertifika gönderiyor. Chrome ve Edge'de sorun yok. Safari test edilmedi.
axios.defaults.baseURL = 'https://localhost/php7/scripts-web/impots/version-14';
// çerezler kullanılacak
axios.defaults.withCredentials = true;
// yapılandırmanın dışa aktarılması
export default {
axios: axios
}
Bu yapılandırma, [dao] katmanının HTTP sorgularını gerçekleştirmek için kullandığı [axios] kütüphanesine aittir. 8. satırda, sunucunun [https] güvenli bağlantı noktası üzerinden çalıştığına dikkat edilmelidir.
18.3.3. Eklentiler
[pluginDao, pluginMétier, pluginConfig] eklentilerinin amacı, [Vue] işlevine/sınıfına üç yeni özellik eklemektir:
- [$dao]: değeri, [Dao] sınıfının bir örneği olacaktır;
- [$métier]: değeri, [Métier] sınıfının bir örneği olacaktır;
- [$config]: [config] yapılandırma dosyası tarafından dışa aktarılan nesneye sahip olacaktır;
[pluginDao]
export default {
install(Vue, dao) {
// Vue sınıfına [$dao] özelliğini ekler
Object.defineProperty(Vue.prototype, '$dao', {
// Vue.$dao çağrıldığında, ikinci parametreyi [dao] olarak döndürüyoruz
get: () => dao,
})
}
}
[pluginMétier]
export default {
install(Vue, métier) {
// Vue sınıfına [$métier] özelliğini ekler
Object.defineProperty(Vue.prototype, '$métier', {
// Vue.$métier referans alındığında, ikinci parametre [métier] olarak döndürülür
get: () => métier,
})
}
}
[pluginConfig]
export default {
install(Vue, config) {
// görünüm sınıfına [$config] özelliğini ekler
Object.defineProperty(Vue.prototype, '$config', {
// Vue.$config referanslandığında, ikinci parametre [config] olarak döndürülür
get: () => config,
})
}
}
18.3.4. [Vuex] depolama alanı
[Vuex] mağazasının uygulaması aşağıdaki [store] dosyası ile gerçekleştirilmiştir:
// Vuex eklentisi
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
// Vuex deposu
const store = new Vuex.Store({
state: {
// simülasyon tablosu
simulations: [],
// son simülasyon numarası
idSimulation: 0
},
mutations: {
// indeks numaralı satırın silinmesi
deleteSimulation(state, index) {
// eslint-disable-next-line no-console
console.log("mutation deleteSimulation");
// [index] numaralı satır siliniyor
state.simulations.splice(index, 1);
// eslint-disable-next-line no-console
console.log("store simulations", state.simulations);
},
// bir simülasyon ekleniyor
addSimulation(state, simulation) {
// eslint-disable-next-line no-console
console.log("mutation addSimulation");
// simülasyon numarası
state.idSimulation++;
simulation.id = state.idSimulation;
// simülasyon simülasyon tablosuna ekleniyor
state.simulations.push(simulation);
},
// state temizleme
clear(state) {
state.simulations = [];
state.idSimulation = 1;
}
}
});
// [store] nesnesinin dışa aktarılması
export default store;
Yorumlar
- 2-4. satırlar: [Vuex] eklentisi, [Vue] çerçeve yapısına entegre edilmiştir;
- 8-13. satırlar: [Vuex] deposuna aşağıdaki öğeleri ekliyoruz:
- [simulations]: kullanıcı tarafından yapılan simülasyonların listesi;
- [idSimulation]: kullanıcı tarafından yapılan son simülasyonun numarası;
Deponun görünümler arasında paylaşılacağını ve içeriğinin reaktif olduğunu hatırlatırız: depo değiştirildiğinde, onu kullanan görünümler otomatik olarak güncellenir. Uygulamamızda, yalnızca [simulations] öğesinin reaktif olması gerekir, [idSimulation] öğesinin ise gerekmez. Bu öğeyi kolaylık olması açısından depoda bıraktık;
- 14-40. satırlar: 8-13. satırlardaki [state] nesnesi üzerinde izin verilen değişiklikler. Bunların her zaman 8-13. satırlardaki [state] nesnesini ilk parametre olarak aldığını hatırlatırız;
- satır 16: [deleteSimulation] mutasyonu, [index] numaralı bir simülasyonu silmeye olanak tanır;
- satır 25: [addSimulation] mutasyonu, simülasyon tablosuna yeni bir simülasyon eklemeye olanak tanır;
- satır 35: [clear] değişikliği, 8-13. satırlardaki [state] nesnesini sıfırlamayı sağlar;
18.3.5. [router] yönlendirme dosyası
Yönlendirme dosyası şöyledir:
// içe aktarmalar
import Vue from 'vue'
import VueRouter from 'vue-router'
// görünümler
import Authentification from './views/Authentification'
import CalculImpot from './views/CalculImpot'
import ListeSimulations from './views/ListeSimulations'
// yönlendirme eklentisi
Vue.use(VueRouter)
// uygulama rotaları
const routes = [
// kimlik doğrulama
{
path: '/', name: 'authentification', component: Authentification
},
// vergi hesaplaması
{
path: '/calcul-impot', name: 'calculImpot', component: CalculImpot
},
// simülasyon listesi
{
path: '/liste-des-simulations', name: 'listeSimulations', component: ListeSimulations
},
// oturum sonlandırma
{
path: '/fin-session', name: 'finSession', component: Authentification
}
]
// yönlendirici
const router = new VueRouter({
// yollar
routes,
// tarayıcıda rotaların görüntülenme modu
mode: 'history',
})
// yönlendiricinin dışa aktarılması
export default router
Açıklamalar
- 16. satır: Uygulama başlatıldığında, [Authentification] görünümü görüntülenir çünkü URL, [/]'in köküdür;
- 20. satır: URL [/calcul-impot] istendiğinde [CalculImpot] görünümü görüntülenir;
- 24. satır: URL veya [/liste-des-simualtions] istendiğinde [ListeSimulations] görünümü görüntülenir;
- satır 28: URL ve [/fin-session] istendiğinde [Authentification] görünümü görüntülenir;
- satır 33-38: Bu rotalar (satır 35) ve URL yönetim modu (satır 37) ile bir [router] nesnesi oluşturulur;
- 41. satır: bu yönlendirici dışa aktarılır;
18.3.6. Ana komut dosyası [main.js]
[main.js] komut dosyası şu şekildedir:
// içe aktarmalar
import Vue from 'vue'
// ana görünüm
import Main from './views/Main.vue'
// [bootstrap-vue] eklentisi
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);
// CSS önyükleme
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
// yönlendirici
import router from './router'
// [config] eklentisi
import config from './config';
import pluginConfig from './plugins/pluginConfig'
Vue.use(pluginConfig, config)
// katman örneği [dao]
import Dao from './couches/Dao';
const dao = new Dao(config.axios);
// [dao] eklentisi
import pluginDao from './plugins/pluginDao'
Vue.use(pluginDao, dao)
// katman örneği [métier]
import Métier from './couches/Métier';
const métier = new Métier();
// [métier] eklentisi
import pluginMétier from './plugins/pluginMétier'
Vue.use(pluginMétier, métier)
// Vuex deposu
import store from './store'
// UI'in başlatılması
new Vue({
el: '#uygulaması,
// yönlendirici
router: router,
// Vuex mağazası
store: store,
// ana görünüm
render: h => h(Main),
})
Aşağıdaki noktalara dikkat edilmelidir:
- 18-21. satırlar: [./config] komut dosyası tarafından dışa aktarılan nesne, [Vue.$config] özniteliğinde mevcut olacak ve dolayısıyla uygulamanın tüm görünümlerinde kullanılabilecektir. Burada bu gereksizdi çünkü [config] nesnesi yalnızca [main] komut dosyası tarafından kullanılıyor (25. satır). Bununla birlikte, yapılandırmanın birden fazla görünüm için gerekli olması sık rastlanan bir durumdur. Bu nedenle burada, nesneyi görünümün bir özniteliğinde kullanılabilir hale getirme ilkesini korumak istedik;
- 24-25. satırlar: [dao] katmanının örneklenmesi. [Dao] sınıfı 24. satırda içe aktarılır ve 25. satırda örneklenir. Bu sınıfın oluşturucusu, tek parametre olarak yapılandırma özelliği olan [axios] nesnesini kabul eder;
- 27-29. satırlar: [dao] katmanı, tüm görünümlerin [$dao] özniteliğinde kullanılabilir hale getirilir;
- satır 31-37: Aynı işlem dizisi, [métier] katmanı için de tekrarlanır. [Métier] sınıfının oluşturucusunun parametresi, vergi idaresi verilerini temsil eden [taxAdminData]'tir. Henüz bu veriye sahip değiliz. Dolayısıyla, 33. satırdaki [métier] nesnesi daha sonra doldurulmalıdır;
- 40. satır: [Vuex] mağazasını içe aktarıyoruz;
- 43-51. satırlar: [Main] ana görünümü (5. ve 50. satırlar) iki parametreyle örneklenir:
- 46. satır: 16. satırda tanımlanan [router] yönlendiricisi;
- satır 48: [Vuex] [store] mağaza, satır 40'ta tanımlanmıştır;
- her iki durumda da özellik adı solda, değeri sağda yer alır. [router, store] özellik adları, [vue-router] ve [vuex] çerçeveleri tarafından belirlenir. İlişkili değerler ise herhangi bir değer olabilir;
18.4. Uygulamanın görünümleri
18.4.1. Ana görünüm [Main]
[Main] ana görünümünün kodu şöyledir:
<!-- HTML görünüm tanımı -->
<template>
<div class="container">
<b-card>
<!-- jumbotron -->
<b-jumbotron>
<b-row>
<b-col cols="4">
<img src="../assets/logo.jpg" alt="Cerisier en fleurs" />
</b-col>
<b-col cols="8">
<h1>Calculez votre impôt</h1>
</b-col>
</b-row>
</b-jumbotron>
<!-- HTTP istek hatası -->
<b-alert
show
variant="danger"
v-if="showError"
>L'erreur suivante s'est produite : {{error.message}}</b-alert>
<!-- geçerli görünüm -->
<router-view v-if="showView" @loading="mShowLoading" @error="mShowError" />
<!-- yükleniyor -->
<b-alert show v-if="showLoading" variant="light">
<strong>Requête au serveur de calcul d'impôt en cours...</strong>
<div class="spinner-border ml-auto" role="status" aria-hidden="true"></div>
</b-alert>
</b-card>
</div>
</template>
<script>
export default {
// ad
name: "app",
// iç durum
data() {
return {
// bekleme uyarısını kontrol eder
showLoading: false,
// hata uyarısını kontrol eder
showError: false,
// geçerli yönlendirme görünümünün görüntülenmesini kontrol eder
showView: true,
// bir hata mesajı
error: ""
};
},
// olay işleyicileri
methods: {
// asenkron istek hatası
mShowError(error) {
// eslint-disable-next-line
console.log("Main evt error");
// hata mesajı görüntüleniyor
this.error = error;
this.showError = true;
// yönlendirilmiş görünüm gizleniyor
this.showView = false;
// bekleme mesajı gizleniyor
this.showLoading = false;
},
// bekleme simgesinin gösterilip gösterilmemesi
mShowLoading(value) {
// eslint-disable-next-line
console.log("Main evt showLoading");
// bekleme uyarısının gösterilip gösterilmeyeceği
this.showLoading = value;
}
}
};
</script>
Açıklamalar
- [Main] görünümü, 23. satırda yönlendirilen ve görüntülenen görünümün sayfa düzenini sağlar:

- 5-15. satırlar 1. alanı görüntüler;
- 23. satır, yönlendirilmiş [2] görünümünü görüntüler;
- 16-19. satırlar: yalnızca vergi hesaplama sunucusuyla iletişim hatası durumunda görüntülenen bir uyarı;
- 25-28. satırlar: sunucuya yapılan her HTTP isteğinde görüntülenen bir bekleme mesajı;
- her yönlendirilmiş görünüm 20-24. satırlar aracılığıyla görüntülendiğinden, tüm görünümler bu sayfa düzeniyle görüntülenecektir. [Main] görünümü, farklı görünümler arasında paylaşılabilecek öğeleri ayırmaya yarar;
- 23. satır: Yönlendirilen her görünüm üç olay gönderebilir:
- [loading]: HTTP sorgusu başlatıldı. Yanıt bekleniyor mesajı gösterilmelidir;
- [error]: HTTP isteği bir hata ile sonuçlandı. Hata mesajı gösterilmeli ve yönlendirilmiş görünüm gizlenmelidir;
- 38-49. satırlar: görünümün durumu:
- 41. satır: [showLoading], HTTP isteğinin (25. satır) tamamlanmasını bekleme mesajının görüntülenmesini kontrol eder;
- satır 43: [showError], HTTP isteğinin (satır 17-21) hata mesajının görüntülenmesini kontrol eder;
- satır 45: [showView], yönlendirilmiş görünümün (satır 23) görüntülenmesini kontrol eder;
- satır 53-63: [mShowError] yöntemi, yönlendirilmiş görünüm tarafından gönderilen [error] olayını yönetir (satır 23);
- satır 65-70: [mShowLoading] yöntemi, yönlendirilmiş görünüm tarafından gönderilen [loading] olayını yönetir (satır 23);
- 23. satır: [error] ve [loading] olaylarına dikkat edilmelidir. Bu olaylar, yalnızca yönlendirilmiş görünüm [showView=true] olarak görüntüleniyorsa yakalanır. Bu nedenle yönlendirilmiş görünüm başlangıçta görüntülenir (45. satır). Yalnızca bir hata durumunda gizlenir (satır 60). Bu sorunu önlemek için [v-if] yerine [v-show] yönergesi kullanılabilirdi. Bu iki yönerge arasındaki fark şudur:
- [v-if=’false’], kontrol edilen bloğu genel HTML kodundan kaldırarak gizler. Bu durumda yönlendirilmiş görünümdeki olaylar artık yakalanamaz;
- [v-show=’false’], kontrol edilen bloğu CSS üzerinde değişiklik yaparak gizler, ancak bloğun kodu genel HTML'te kalır ve böylece yönlendirilmiş görünümdeki olayları yakalayabilir;
18.4.2. [Layout] mizanpaj görünümü
[Layout] görünümünün kodu şöyledir:
<!-- yönlendirilmiş görünümün sayfa düzeninin HTML ile tanımlanması -->
<template>
<!-- satır -->
<div>
<b-row>
<!-- sol tarafta üç sütunlu alan -->
<b-col cols="3" v-if="left">
<slot name="left" />
</b-col>
<!-- sağdaki dokuz sütunlu alan -->
<b-col cols="9" v-if="right">
<slot name="right" />
</b-col>
</b-row>
</div>
</template>
<script>
export default {
// görünüm ayarları
props: {
// sol sütunu kontrol eder
left: {
type: Boolean
},
// sağdaki sütunu kontrol eder
right: {
type: Boolean
}
}
};
</script>
Açıklamalar
- [Layout] görünümü, yönlendirilmiş görünümü iki bölgeye ayırmaya olanak tanır:
- sol tarafta 3 sütunlu bir Bootstrap alanı (7-9. satırlar). Bu alan, varsa gezinme menüsünü barındıracaktır;
- sağda 9 sütunlu bir alan (satır 11-13). Bu alan, yönlendirilmiş görünüm tarafından sağlanan bilgileri barındıracaktır;
18.4.3. [Authentification] görünümü
Kimlik doğrulama görünümü şöyledir:

Bu görünüm, [Layout] dosyasından sol sütunu kaldırarak yalnızca sağ sütunu görüntülemek suretiyle elde edilir.
Kodu şu şekildedir:
<!-- HTML görünüm tanımı -->
<template>
<Layout :left="false" :right="true">
<template slot="right">
<!-- HTML formu - değerler [authentifier-utilisateur] eylemi ile gönderilir -->
<b-form @submit.prevent="login">
<!-- başlık -->
<b-alert show variant="primary">
<h4>Bienvenue. Veuillez vous authentifier pour vous connecter</h4>
</b-alert>
<!-- 1. satır -->
<b-form-group label="Nom d'utilisateur" label-for="user" label-cols="3">
<!-- kullanıcı giriş alanı -->
<b-col cols="6">
<b-form-input type="text" id="user" placeholder="Nom d'utilisateur" v-model="user" />
</b-col>
</b-form-group>
<!-- 2. satır -->
<b-form-group label="Mot de passe" label-for="password" label-cols="3">
<!-- şifre giriş alanı -->
<b-col cols="6">
<b-input type="password" id="password" placeholder="Mot de passe" v-model="password" />
</b-col>
</b-form-group>
<!-- 3. satır -->
<b-alert
show
variant="danger"
v-if="showError"
class="mt-3"
>L'erreur suivante s'est produite : {{message}}</b-alert>
<!-- 3. satırda [submit] tipi düğme -->
<b-row>
<b-col cols="2">
<b-button variant="primary" type="submit" :disabled="!valid">Valider</b-button>
</b-col>
</b-row>
</b-form>
</template>
</Layout>
</template>
<!-- görünüm dinamiği -->
<script>
import Layout from "./Layout";
export default {
// bileşen durumu
data() {
return {
// kullanıcı
user: "",
// şifresi
password: "",
// hata mesajının görüntülenmesini kontrol eder
showError: false,
// hata mesajı
message: "",
// oturum başlatıldı
sessionStarted: false
};
},
// kullanılan bileşenler
components: {
Layout
},
// hesaplanan özellikler
computed: {
// geçerli girdiler
valid() {
return this.user && this.password && this.sessionStarted;
}
},
// olay işleyicileri
methods: {
// ----------- kimlik doğrulama
async login() {
try {
// bekleme başlangıcı
this.$emit("loading", true);
// sunucuda engelleyici kimlik doğrulama
const response = await this.$dao.authentifierUtilisateur(
this.user,
this.password
);
// yükleme sonu
this.$emit("loading", false);
// yanıtın analizi
if (response.état != 200) {
// hata görüntüleniyor
this.message = response.réponse;
this.showError = true;
return;
}
// hata yok
this.showError = false;
// --------- şimdi vergi dairesinden veriler isteniyor
// bekleme başlıyor
this.$emit("loading", true);
// sunucuya yapılan istek askıya alındı
const response2 = await this.$dao.getAdminData();
// yükleme tamamlandı
this.$emit("loading", false);
// yanıtın analizi
if (response2.état != 1000) {
// hata görüntüleniyor
this.message = response2.réponse;
this.showError = true;
return;
}
// hata yok
this.showError = false;
// alınan veriler [métier] katmanında saklanıyor
this.$métier.setTaxAdminData(response2.réponse);
// vergi hesaplama ekranına geçiliyor
this.$router.push({ name: "calculImpot" });
} catch (error) {
// hata ana bileşene iletiliyor
this.$emit("error", error);
}
}
},
// yaşam döngüsü: bileşen az önce oluşturuldu
created() {
// eslint-disable-next-line
console.log("authentification", "created");
// sunucuyla bir jSON oturumu başlatılıyor
// bekleme başlıyor
this.$emit("loading", true);
// sunucuyla oturum başlatılıyor - asenkron istek
// QZXW2HTML katmanındaki yöntemler tarafından döndürülen söz kullanılıyor BW2Rhb10ZQX
this.$dao
// bir oturum başlatılıyor jSON
.initSession()
// yanıt alındı
.then(response => {
// bekleme sona erdi
this.$emit("loading", false);
// yanıtın analizi
if (response.état != 700) {
// hata görüntüleniyor
this.message = response.réponse;
this.showError = true;
return;
}
// oturum başlatıldı
this.sessionStarted = true;
})
// hata durumunda
.catch(error => {
// hata [Main] görünümüne iletiliyor
this.$emit("error", error);
});
}
};
</script>
Açıklamalar
- 3. satır: [Authentification] görünümü, yalnızca [Layout]'in sağ sütununu kullanır (3. ve 4. satırlar);
- 6-38. satırlar: Yukarıdaki ekran görüntüsündeki alan 1'i oluşturan Bootstrap formu;
- 6. satır: [@submit] olayı, kullanıcı 35. satırdaki [submit] türündeki düğmeye tıkladığında gerçekleşir. [prevent] değiştiricisi, [submit] sırasında sayfanın yeniden yüklenmemesini sağlar. Şu şekilde de yazılabilirdi:
- [submit] olayını yönetmeyen bir <b-form> etiketi;
- [@click=’login’] olayını içeren ve [type=’submit’] özniteliği bulunmayan bir <b-button> etiketi;
Bu da işe yarar. Seçilen çözümün avantajı, gönderme işleminin sadece [Valider] düğmesine tıklanarak değil, aynı zamanda giriş alanlarındaki bir onay ([Entrée] tuşu) ile de gerçekleştirilebilmesidir. Dolayısıyla, burada [<b-form @submit.prevent="login">] çözümü, kullanıcı rahatlığı için tercih edilmiştir;
- 33-37. satırlar: Sunucu, kullanıcı tarafından girilen kimlik bilgilerini reddettiğinde görüntülenen bir uyarı:

- 35. satır: [Valider] düğmesi her zaman etkin değildir. Durumu, 71-73. satırlardaki hesaplanan [valid] özniteliğine bağlıdır. [valid] özniteliği şu durumlarda doğrudur:
- formun [user, password] alanlarında bir değer varsa;
- jSON oturumu başlatılmışsa. Başlangıçta bu oturum başlatılmamıştır (satır 59) ve bu nedenle [Valider] düğmesi devre dışıdır.
- 49-60. satırlar: görünümün durumu;
- [user], formun [user] alanına (satır 12-17) kullanıcı tarafından girilen veriyi temsil eder. 15. satırdaki [v-model] yönergesi, kullanıcının girişi ile görünümün [user] özniteliği arasında çift yönlü bir bağlantı kurar;
- [password], formun [password] alanındaki (satır 19-24) kullanıcı girdisini temsil eder. 22. satırdaki [v-model] yönergesi, kullanıcının girişi ile görünümün [password] özniteliği arasında çift yönlü bir bağlantı kurar;
- [showError], 26-31. satırlardaki uyarı mesajının görüntülenmesini kontrol eder (29. satır);
- [message], 26-31. satırlardaki uyarıda görüntülenecek hata mesajıdır (31. satır);
- [sessionStarted], sunucu ile jSON oturumunun başlatılıp başlatılmadığını gösterir. Başlangıçta bu özniteliğin değeri [false]'tir (satır 59). Sunucuyla olan jSON oturumu, görünüm yaşam döngüsünün [created] olayında (satır 126-156) başlatılır. Sunucu olumlu yanıt verirse, [sessionStarted] özniteliği [true] olarak değiştirilir (satır 149);
- 126-156. satırlar: [Authentification] görünümü oluşturulduğunda (henüz görüntülenmemiş olsa bile) [created] işlevi yürütülür. Arka planda, sunucu ile bir jSON oturumu başlatılır. Bunun, vergi hesaplama sunucusuyla yapılacak ilk işlem olduğu bilinmektedir. Bunun için uygulamanın [dao] katmanı kullanılır (satır 134). Bu katmandaki tüm yöntemler asenkron çalışır. Burada, sunucu ile jSON oturumunu başlatan [$dao.initSession] yöntemi tarafından döndürülen Promise kullanılır.
- 138-150. satırlar: Sunucu hatasız bir yanıt verdiğinde yürütülen kod;
- 142. satır: Yanıtın [état] özelliği kontrol edilir. İşlemin başarılı olması için bu özelliğin değeri [700] olmalıdır. Aksi takdirde, [response.réponse] özelliğinde (satır 144) belirtilen bir hata meydana gelmiştir. Bu durumda, görünümün hata mesajı görüntülenir (satır 145);
- satır 149: jSON oturumunun başladığı görülür;
- 152-155. satırlar: Hata durumunda yürütülen kod. Bu hata, üst görünüm olan [Main]’e iletilir ve
- hatayı görüntüleyecektir;
- bekleme mesajını gizleyecek;
- yönlendirilmiş görünüm olan [Autentification] görünümünü gizleyecektir;
- 79-124. satırlar: [login] yöntemi, [Valider] düğmesine yapılan tıklamayı işler;
- 79. satır: Yöntemin önüne, 84. ve 103. satırlardaki [await] anahtar sözcüğünün kullanılabilmesi için [async] anahtar sözcüğü eklenmiştir;
- 84-87. satırlar: [$dao.authentifierUtilisateur(user, password)] yöntemine bloklayıcı çağrı. [created] işlevinde yapıldığı gibi bir [Promise] sözü de kullanılabilirdi. Stil çeşitliliği sağlamak istedik. Tüm HTTP isteklerine 2 saniyelik bir [timeout] eklediğimiz için kullanıcıyı bloke etme riski yoktur. Kullanıcı uzun süre beklemek zorunda kalmayacaktır. Ayrıca, sunucu yanıtını verene kadar kullanıcı hiçbir şey yapamaz; çünkü bu durumda [Valider] düğmesi devre dışı kalır;
- 91. satır: Vergi hesaplama sunucusu, hepsi [{‘action’:action, ‘état’:val, ‘réponse’:réponse}] yapısına sahip jSON yanıtları gönderir. [état==200] ise kimlik doğrulama başarılı olmuştur. Aksi takdirde, satır 93-94'te bir hata mesajı görüntülenir;
- 98. satır: önceki bir işlemden kaynaklanabilecek olası bir hata mesajı gizlenir;
- 99-116. satırlar: Şimdi, vergi hesaplamasına olanak tanıyan vergi idaresi verileri için sunucuya istekte bulunulur. [this.$métier]'te, şu anda bu verilere sahip olmadığı için hiçbir işlem yapamayan [Métier] sınıfının bir örneği bulunur;
- satır 103: Vergi idaresine ait veriler, sunucudan engelleyici bir işlemle talep edilir;
- 107-112. satırlar: Sunucudan gelen yanıt analiz edilir. Durum değeri 1000 olmalıdır; aksi takdirde bir hata meydana gelmiştir. Bu durumda, hata mesajı görüntülenir (109-110. satırlar);
- 113-118. satırlar: İşlem başarılı olursa:
- hata mesajı gizlenir (satır 114);
- vergi idaresinin verileri [métier] katmanına aktarılır (satır 116);
- [CalculImpot] görünümü görüntülenir, satır 118. [this.$router]'in uygulama yönlendiricisini ifade ettiğini hatırlayalım. [push] yöntemi, yönlendirilecek bir sonraki görünümü belirlemeye olanak tanır. Burada bu görünüm, [name] özniteliği ile belirtilmiştir. Aynı görünümü [path] özniteliği ile de belirtebilirdik. Bu bilgiler yönlendirme dosyasında yer almaktadır:
// vergi hesaplaması
{
path: '/calcul-impot', name: 'calculImpot', component: CalculImpot
},
- 119-122. satırlar: [catch], iki HTTP isteğinden biri başarısız olduğunda (sunucu mevcut değil, zaman aşımı, ...) tetiklenir. Bu durumda hata, üst görünüm olan [Main]'e bildirilir; bu görünüm hatayı görüntüler, bekleme mesajını ve [Authentification] görünümünü gizler;
18.4.4. [CalculImpot] görünümü
[CalculImpot] görünümü şöyledir:

- [1]: Yönlendirilmiş görünümün sol sütununda bir gezinme menüsü yer alır;
- [2]: Vergi hesaplama formu, yönlendirilmiş görünümün sağ sütununu kaplar;
[CalculImpot] görünümünün kodu şöyledir:
<!-- HTML görünümünün tanımı -->
<template>
<div>
<Layout :left="true" :right="true">
<!-- sağdaki vergi hesaplama formu -->
<FormCalculImpot slot="right" @resultatObtenu="handleResultatObtenu" />
<!-- sol taraftaki gezinme menüsü -->
<Menu slot="left" :options="options" />
</Layout>
<!-- formun altındaki vergi hesaplama sonuçlarının görüntülenme alanı -->
<b-row v-if="résultatObtenu" class="mt-3">
<!-- boş üç sütunlu alan -->
<b-col cols="3" />
<!-- dokuz sütunlu alan -->
<b-col cols="9">
<b-alert show variant="success">
<span v-html="résultat"></span>
</b-alert>
</b-col>
</b-row>
</div>
</template>
<script>
// ithalatlar
import FormCalculImpot from "./FormCalculImpot";
import Menu from "./Menu";
import Layout from "./Layout";
export default {
// dahili rapor
data() {
return {
// menü seçenekleri
options: [
{
text: "Liste des simulations",
path: "/liste-des-simulations"
},
{
text: "Fin de session",
path: "/fin-session"
}
],
// vergi hesaplama sonucu
résultat: "",
résultatObtenu: false
};
},
// kullanılan bileşenler
components: {
Layout,
FormCalculImpot,
Menu
},
// olay yönetimi yöntemleri
methods: {
// vergi hesaplama sonucu
handleResultatObtenu(résultat) {
// sonuç, HTML dizisi olarak oluşturulur
const impôt = "Montant de l'impôt : " + résultat.impôt + " euro(s)";
const décôte = "Décôte : " + résultat.décôte + " euro(s)";
const réduction = "Réduction : " + résultat.réduction + " euro(s)";
const surcôte = "Surcôte : " + résultat.surcôte + " euro(s)";
const taux = "Taux d'imposition : " + résultat.taux;
this.résultat =
impôt +
"<br/>" +
décôte +
"<br/>" +
réduction +
"<br/>" +
surcôte +
"<br/>" +
taux;
// sonucun görüntülenmesi
this.résultatObtenu = true;
// ---- deponun güncellenmesi [Vuex]
// bir simülasyon daha
this.$store.commit("addSimulation", résultat);
}
}
};
</script>
Açıklamalar
- 4. satır: [Layout]'in iki sütunu burada mevcuttur;
- 6. satır: Vergi hesaplama formu sağ sütunu kaplar. Vergi hesaplamasının sonucu elde edildiğinde [resultatObtenu] olayını tetikler. Olay adlarının ve bunları yöneten yöntemlerin adlarının aksanlı karakterler içermemesi gerektiği unutulmamalıdır;
- 8. satır: Gezinme menüsü sol sütunu kaplar;
- satır 11-20: Vergi hesaplama sonucu formun altında görüntülenir:

- 11. satır: Sonuç, yalnızca [résultatObtenu] özniteliği (47. satır) [true] değerindeyse görüntülenir;
- satır 34-48: görünüm durumu:
- [options]: gezinme menüsünün seçenek listesi. Bu tablo, 8. satırda [Menu] bileşenine parametre olarak aktarılır;
- [résultat]: Vergi hesaplamasının sonucu. Bu sonuç, HTML dizesidir. Bu nedenle, 17. satırda [v-html] yönergesi kullanılarak görüntülenmiştir;
- [résultatObtenu]: sonucun görüntülenmesini kontrol eden boole değeri, 11. satır;
- 59-81. satırlar: [handleResultatObtenu] yöntemi, 6. satırdaki alt görünüm [FormCalculImpot] tarafından kendisine gönderilen vergi hesaplama sonucunu görüntüler. Bu sonuç, [impot, décôte, réduction, surcôte, taux, marié, enfants, salaire] özelliklerine sahip bir nesnedir;
- satır 61-75: [impot, décôte, réduction, surcôte, taux] nesnesi, şablonun 17. satırında görüntülenen HTML metnine yazılır;
- 77. satır: bu sonuç görüntülenir;
- 80. satır: Vuex deposundaki [addSimulation] mutasyonunu çağırıyoruz; bu mutasyon, depoda halihazırda bulunan simülasyonlara [résultat]'i ekleyecektir;
18.4.5. [Menu] gezinme menüsü
Gezinti menüsü, yönlendirilmiş görünümlerin sol sütununda görüntülenir:

[Menu] görünümünün kodu şöyledir:
<!-- HTML görünümünün tanımı -->
<template>
<!-- dikey Bootstrap menüsü -->
<b-nav vertical>
<!-- menü seçenekleri -->
<b-nav-item
v-for="(option,index) of options"
:key="index"
:to="option.path"
exact
exact-active-class="active"
>{{option.text}}</b-nav-item>
</b-nav>
</template>
<script>
export default {
// görünüm ayarları
props: {
options: {
type: Array
}
}
};
</script>
Açıklamalar
- menü seçenekleri, [options] parametresi tarafından sağlanır (7., 20-22. satırlar);
- [options] tablosundaki her bir öğenin, bağlantı metnini içeren [text] özelliği (satır 12) ve bağlantının hedef görünümünün yolunu belirten [path] özelliği (satır 9) vardır;
18.4.6. [FormCalculImpot] görünümü
Bu görünüm, vergi hesaplama formunu sağlar:

Kodu şu şekildedir:
<!-- HTML görünüm tanımı -->
<template>
<!-- HTML formu -->
<b-form @submit.prevent="calculerImpot" class="mb-3">
<!-- mavi arka planlı 12 sütunlu mesaj -->
<b-alert show variant="primary">
<h4>Remplissez le formulaire ci-dessous puis validez-le</h4>
</b-alert>
<!-- form öğeleri -->
<!-- ilk satır -->
<b-form-group label="Etes-vous marié(e) ou pacsé(e) ?" label-cols="4">
<!-- 5 sütunlu radyo düğmeleri-->
<b-col cols="5">
<b-form-radio v-model="marié" value="oui">Oui</b-form-radio>
<b-form-radio v-model="marié" value="non">Non</b-form-radio>
</b-col>
</b-form-group>
<!-- ikinci satır -->
<b-form-group label="Nombre d'enfants à charge" label-cols="4" label-for="enfants">
<b-input
type="text"
id="enfants"
placeholder="Indiquez votre nombre d'enfants"
v-model="enfants"
:state="enfantsValide"
/>
<!-- olası hata mesajı -->
<b-form-invalid-feedback :state="enfantsValide">Vous devez saisir un nombre positif ou nul</b-form-invalid-feedback>
</b-form-group>
<!-- üçüncü satır -->
<b-form-group
label="Salaire annuel"
label-cols="4"
label-for="salaire"
description="Arrondissez à l'euro inférieur"
>
<b-input
type="text"
id="salaire"
placeholder="Salaire annuel"
v-model="salaire"
:state="salaireValide"
/>
<!-- olası hata mesajı -->
<b-form-invalid-feedback :state="salaireValide">Vous devez saisir un nombre positif ou nul</b-form-invalid-feedback>
</b-form-group>
<!-- dördüncü satır, 5 sütunlu [submit] düğmesi -->
<b-col cols="5">
<b-button type="submit" variant="primary" :disabled="formInvalide">Valider</b-button>
</b-col>
</b-form>
</template>
<!-- komut dosyası -->
<script>
export default {
// iç durum
data() {
return {
// evli mi değil mi
marié: "non",
// çocuk sayısı
enfants: "",
// yıllık maaş
salaire: ""
};
},
// hesaplanan iç durum
computed: {
// formun onaylanması
formInvalide() {
return (
// maaş geçersiz
!this.salaireValide ||
// veya geçersiz çocuk sayısı
!this.enfantsValide ||
// veya vergi verileri alınamadı
!this.$métier.taxAdminData
);
},
// maaş doğrulaması
salaireValide() {
// sayısal olmalı ve 0'dan büyük olmalı
return Boolean(this.salaire.match(/^\s*\d+\s*$/));
},
// çocuk bilgilerinin doğrulanması
enfantsValide() {
// sayısal olmalı ve 0'dan büyük olmalı
return Boolean(this.enfants.match(/^\s*\d+\s*$/));
}
},
// olay yöneticisi
methods: {
calculerImpot() {
// vergi, [métier] katmanı kullanılarak hesaplanır
const résultat = this.$métier.calculerImpot(
this.marié,
this.enfants,
this.salaire
);
// eslint-disable-next-line
console.log("résultat=", résultat);
// sonuç tamamlanır
résultat.marié = this.marié;
résultat.enfants = this.enfants;
résultat.salaire = this.salaire;
// [resultatObtenu] olayını tetiklenir
this.$emit("resultatObtenu", résultat);
}
}
};
</script>
Açıklamalar
- 4-51. satırlar: Bootstrap formu;
- 11-17. satırlar: etiketleriyle birlikte bir grup radyo düğmesi;
- 14-15. satırlar: <b-form-radio> etiketi, bir radyo düğmesinin görüntülenmesini sağlar:
- 14. satır: [v-model] yönergesi, düğmeye tıklandığında 61. satırdaki [marié] özniteliğinin [oui] değerini almasını sağlar ([value="oui"] özniteliği);
- 15. satır: [v-model] yönergesi, düğmeye tıklandığında 61. satırdaki [marié] özniteliğinin [non] değerini ([value="non"] özniteliği) almasını sağlar;
- 19-29. satırlar: çocuk sayısının girildiği bölüm:
- 24. satır: Çocuk sayısı girişi, 63. satırdaki [enfants] özniteliğiyle bağlantılıdır;
- satır 25: Girişin geçerliliği, 87-89. satırlardaki hesaplanan [enfantsValide] özniteliği ile kontrol edilir;
- 28. satır: Giriş geçersizse bir hata mesajının görüntülenmesini sağlar;
- 31-45. satırlar: yıllık maaşın girilen kısmı:
- satır 35: giriş alanının hemen altında bir yardım mesajı görüntüler;
- satır 41: maaş girişi, 65. satırdaki [salaire] özniteliğiyle bağlantılıdır;
- satır 42: Girişin geçerliliği, 82-85. satırlardaki hesaplanan [salaireValide] özniteliği ile kontrol edilir;
- satır 45: giriş geçersizse bir hata mesajının görüntülenmesini sağlar;
- 48-50. satırlar: [submit] türünde bir düğme. Bu düğmeye tıklandığında veya [Entrée] tuşuyla bir giriş onaylandığında, [calculerImpot] yöntemi yürütülür (94. satır);
- satır 49: Düğmenin etkin/etkin olmayan durumu, 71-80. satırlardaki [formInvalide] hesaplanmış özniteliği tarafından kontrol edilir;
- 71-80. satırlar: Form, aşağıdaki durumlarda geçerlidir:
- çocuk sayısı geçerliyse;
- maaş geçerliyse;
- Uygulama, vergi hesaplamasına olanak tanıyan vergi idaresi verilerini sunucudan almıştır. Bu verinin [$métier.taxAdminData] özelliğinde kaydedildiği hatırlatılmaktadır. [FormCalculImpot] görünümü, bu veriler alınmadan önce görüntülenebilir; çünkü bu veriler, görünümün görüntülenmesi ile eşzamanlı olarak asenkron bir şekilde talep edilir. Burada, veriler alınana kadar kullanıcının [Valider] düğmesine tıklayamayacağından emin olunur;
- 94-109. satırlar: vergi hesaplama yöntemi:
- 96-100. satırlar: Bu hesaplamayı [métier] katmanı yapar. Bu senkron bir hesaplamadır. [taxAdminData] verisi alındıktan sonra, [Vue] istemcisinin sunucuyla iletişim kurmasına gerek kalmaz. Her şey yerel olarak gerçekleştirilir. [impôt, décôte, surcôte, réduction, taux] özelliklerine sahip bir [résultat] nesnesi elde edilir;
- 104-106. satırlar: Sonuca [marié, enfants, salaire] özellikleri eklenir;
- 108. satır: sonuç, [resultatObtenu] olayı aracılığıyla üst görünüm olan [CalculImpot]'e aktarılır. Bu görünüm, sonucu görüntülemekle görevlidir;
18.4.7. [ListeSimulations] görünümü
[ListeSimulations] görünümü, kullanıcı tarafından yapılan simülasyonların listesini görüntüler:

Görünümün kodu şöyledir:
<!-- HTML görünümünün tanımı -->
<template>
<div>
<!-- sayfa düzeni -->
<Layout :left="true" :right="true">
<!-- sağ sütundaki simülasyonlar -->
<template slot="right">
<template v-if="simulations.length==0">
<!-- simülasyon yok -->
<b-alert show variant="primary">
<h4>Votre liste de simulations est vide</h4>
</b-alert>
</template>
<template v-if="simulations.length!=0">
<!-- simülasyonlar var -->
<b-alert show variant="primary">
<h4>Liste de vos simulations</h4>
</b-alert>
<!-- simülasyon tablosu -->
<b-table striped hover responsive :items="simulations" :fields="fields">
<template v-slot:cell(action)="data">
<b-button variant="link" @click="supprimerSimulation(data.index)">Supprimer</b-button>
</template>
</b-table>
</template>
</template>
<!-- sol sütundaki gezinme menüsü -->
<Menu slot="left" :options="options" />
</Layout>
</div>
</template>
<script>
// içe aktarmalar
import Layout from "./Layout";
import Menu from "./Menu";
export default {
// bileşenler
components: {
Layout,
Menu
},
// iç durum
data() {
return {
// gezinti menüsü seçenekleri
options: [
{
text: "Calcul de l'impôt",
path: "/calcul-impot"
},
{
text: "Fin de session",
path: "/fin-session"
}
],
// HTML tablosunun parametreleri
fields: [
{ label: "#", key: "id" },
{ label: "Marié", key: "marié" },
{ label: "Nombre d'enfants", key: "enfants" },
{ label: "Salaire", key: "salaire" },
{ label: "Impôt", key: "impôt" },
{ label: "Décôte", key: "décôte" },
{ label: "Réduction", key: "réduction" },
{ label: "Surcôte", key: "surcôte" },
{ label: "", key: "action" }
]
};
},
// hesaplanan iç durum
computed: {
// Vuex mağazasından alınan simülasyon listesi
simulations() {
return this.$store.state.simulations;
}
},
// yöntemler
methods: {
supprimerSimulation(index) {
// eslint-disable-next-line
console.log("supprimerSimulation", index);
// [index] numaralı simülasyonun silinmesi
this.$store.commit("deleteSimulation", index);
}
}
};
</script>
Açıklamalar
- 5. satır: görünüm, yönlendirilmiş görünümlerin [Layout] sayfa düzenindeki iki sütunu kaplar;
- 7-26. satırlar: Simülasyonlar sağ sütuna yerleştirilir;
- 28. satır: gezinme menüsü sol sütuna yerleştirilir;
- 8, 14, 20, 75. satırlar: simülasyonlar [Vuex] [$this.store] deposundan alınır;
- satır 8-13: simülasyon listesi boş olduğunda görüntülenen uyarı;
- satır 14-25: simülasyon listesi boş olmadığında görüntülenen HTML tablosu;
- satır 20-24: HTML tablosu bir <b-table> etiketiyle oluşturulur;
- satır 20: simülasyon tablosu, 74-76. satırlardaki [simulations] hesaplanmış özniteliği tarafından sağlanır;
- satır 20: HTML tablosunun yapılandırması, 58-69. satırlardaki [fields] hesaplanmış özniteliği ile yapılır. 67. satır: [action] anahtar sütunu, HTML tablosunun son sütunudur;
- 21-23. satırlar: HTML tablosunun son sütununun şablonu;
- 22. satır: buraya bağlantı türü bir düğme yerleştirilir. Bu düğmeye tıklandığında, [supprimerSimulation(data.index)] yöntemi çağrılır; burada [data], geçerli satırı (satır 21) temsil eder. [data.index] ise görüntülenen satırlar listesindeki bu satırın numarasını temsil eder;
- satır 28: gezinme menüsünün oluşturulması. Bu menünün seçenekleri, 47-56. satırlardaki [options] özniteliği tarafından sağlanır;
- satır 80-85: HTML sayfasındaki [Supprimer] bağlantısına tıklandığında tepki veren yöntem;
- 84. satır: [Vuex] mağazasındaki [deleteSimulation] dönüşümü çağrılır (bkz. |vuejs-15| paragrafı);
18.5. Projenin yürütülmesi

Vergi hesaplama sunucusunun çevrimiçi olması için [Laragon] sunucusunu da başlatmanız gerekir (bkz. |https://tahe.developpez.com/tutoriels-cours/php7| belgesi).
18.6. Uygulamanın yerel bir sunucuya dağıtılması
Şu anda, [Vue] istemcimiz, URL ve [http://localhost:8080] adreslerindeki bir test sunucusuna dağıtılmıştır. Bunu, URL ve [http://localhost:80] sunucularındaki [Laragon] sunucusuna dağıtacağız. Bunu gerçekleştirmek için atılması gereken birkaç adım vardır.
1. adım
Öncelikle, [Vue] istemcisinin URL ve [http://localhost:8080/client-vuejs-impot/] test sunucularına dağıtılmasını sağlayacağız.
Mevcut [VSCode] projemizin kök dizinine [vue.config.js] adlı bir dosya oluşturuyoruz:

[vue.config.js] [1] dosyası aşağıdaki içeriğe sahip olacaktır:
// vue.config.js
module.exports = {
// vergi hesaplama sunucusunun [vuejs] müşteri hizmetinin URL
publicPath: '/client-vuejs-impot/'
}
Ayrıca [router.js] ve [2] yönlendirme dosyalarını da değiştirmemiz gerekiyor:
// ithalatlar
import Vue from 'vue'
import VueRouter from 'vue-router'
// görünümler
import Authentification from './views/Authentification'
import CalculImpot from './views/CalculImpot'
import ListeSimulations from './views/ListeSimulations'
// yönlendirme eklentisi
Vue.use(VueRouter)
// uygulama yolları
const routes = [
// kimlik doğrulama
{
path: '/', name: 'authentification', component: Authentification
},
// vergi hesaplaması
{
path: '/calcul-impot', name: 'calculImpot', component: CalculImpot
},
// simülasyon listesi
{
path: '/liste-des-simulations', name: 'listeSimulations', component: ListeSimulations
},
// oturum sonlandırma
{
path: '/fin-session', name: 'finSession', component: Authentification
}
]
// yönlendirici
const router = new VueRouter({
// yollar
routes,
// tarayıcıda rotaların görüntülenme modu
mode: 'history',
// uygulamanın temel URL'i
base: '/client-vuejs-impot/'
})
// yönlendiricinin dışa aktarımı
export default router
- 39. satır: Yönlendiriciye, 13-30. satırlarda tanımlanan yol yollarının 39. satırda tanımlanan yola göre göreceli olduğu belirtilir. Örneğin, 20. satırdaki [/calcul-impot] yolu, [/client-vuejs-impot/calcul-impot] olacaktır;
Ardından, [vuejs-20] projesini yeniden test ederek uygulama yollarındaki değişikliği doğrulayabiliriz:

2. adım
Şimdi [vuejs-20] projesinin üretim sürümünü derliyoruz:

- [1-2]'te, [package.json] [1] dosyasında [build] [2] görevini yapılandırıyoruz;
- [3-5]'te bu görevi çalıştırıyoruz. [vuejs-20] projesinin üretim sürümünü oluşturacak olan bu görevdir;
[build] görevinin yürütülmesi, [VSCode] terminalinde gerçekleşir:


- [3-6]'te, uyarılar bize oluşturulan kodun çok büyük olduğunu ve [8]'e bölünmesi gerektiğini bildiriyor. Bu, burada ele almayacağımız kod mimarisinin optimizasyonuyla ilgilidir;
- [7]'te, [dist] klasörünün oluşturulan üretim sürümünü içerdiği belirtiliyor:

- [3]'te, [index.html] dosyası, URL [https://localhost:80/client-vue-js-impot/] istendiğinde kullanılacak dosyadır;
Burada, herhangi bir sunucuya dağıtılabilen statik bir web sitesi var. Bunu yerel Laragon sunucusuna dağıtacağız (bkz. |https://tahe.developpez.com/tutoriels-cours/php7| belgesi). [dist] [2] klasörü, <laragon>'un Laragon sunucusunun kurulum klasörü olduğu [<laragon>/www] [4] klasörüne kopyalanır. Üretim sürümünü URL [/client-vuejs-impot/]'te çalışacak şekilde yapılandırdığımız için bu klasörü [client-vuejs-impot] [5] olarak yeniden adlandırıyoruz.
3. adım
Az önce oluşturulan [client-vuejs-impot] klasörüne aşağıdaki [.htaccess] dosyasını ekliyoruz:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /client-vuejs-impot/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /client-vuejs-impot/index.html [L]
</IfModule>

Bu dosya, Apache web sunucusunun bir yapılandırma dosyasıdır. Bu dosyayı yerleştirmezsek ve önce URL [https://localhost/client-vuejs-impot/] adresinden geçmeden doğrudan URL [https://localhost/client-vuejs-impot/calcul-impot] adresini çağırırsak, 404 hatası alırız. Bu dosya sayesinde [CalculImpot] sayfasını başarıyla görüntüleyebiliyoruz.
Bunu yaptıktan sonra, henüz başlatılmamışsa Laragon sunucusunu başlatıyoruz ve URL [https://localhost/client-vuejs-impot/] adresini çağırıyoruz:

Okuyucunun, uygulamamızın üretim sürümünü test etmesi önerilir.
Vergi hesaplama sunucusunda bir noktada değişiklik yapabiliriz: sunucunun müşterilerine sistematik olarak gönderdiği CORS başlıkları. Bu, [localhost:8080] etki alanından çalıştırılan istemci sürümü için gerekliydi. Artık hem istemci hem de sunucu [localhost:80] etki alanında çalıştığına göre, CORS başlıkları gereksiz hale gelmiştir.
Sunucunun 14. sürümündeki [config.json] dosyasını şu şekilde değiştiriyoruz:

- olarak değiştiriyoruz ve bundan sonra CORS isteklerinin reddedileceğini belirtiyoruz;
Bu değişikliği kaydedip URL ve [https://localhost/client-vuejs-impot/] isteklerini yeniden gönderelim. Sistem çalışmaya devam etmelidir.
18.7. Manuel URL yönetimi
Kullanıcı, gezinme menüsündeki bağlantıları akıllıca kullanmak yerine, uygulamanın URL adreslerini tarayıcının adres çubuğuna manuel olarak girmek isteyebilir. Örneğin, kimlik doğrulama aşamasını atlayarak URL [https://client-vuejs-impot/calcul-impot] adresini deneyelim. Bir hacker kesinlikle bunu denerdi. Aşağıdaki ekran karşımıza çıkar:

Vergi hesaplama sayfası doğru bir şekilde görüntüleniyor. Şimdi giriş alanlarını doldurup göndermeyi deneyelim:

Görüyoruz ki, girişler doğru olsa bile [1] [Valider] düğmesi hala devre dışı kalıyor. [FormCalculImpot] görünümünün koduna bakalım:
<b-col cols="5">
<b-button type="submit" variant="primary" :disabled="formInvalide">Valider</b-button>
</b-col>
- satırda, etkin/etkin olmayan durumunun [formInvalide] özelliğine bağlı olduğu görülüyor. Bu, aşağıdaki hesaplanan özelliktir:
formInvalide() {
return (
// geçersiz maaş
!this.salaireValide ||
// veya geçersiz çocuk bilgileri
!this.enfantsValide ||
// veya vergi verileri alınamadı
!this.$métier.taxAdminData
);
},
- satırda, formun geçerli olabilmesi için vergi verilerinin alınmış olması gerektiği görülmektedir. Ancak bu veriler, kullanıcının ‘atladığı’ [Authentification] görünümünün doğrulanması sırasında elde edilir. Dolayısıyla kullanıcı formu doğrulayamayacaktır. Bunu yapabilseydi, sunucudan kimliği doğrulanmadığını belirten bir hata mesajı alırdı. Doğrulamalar her zaman sunucu tarafında yapılmalıdır. Tarayıcı tarafındaki doğrulamalar her zaman atlanabilir. Sunucuya ham istekler gönderecek [Postman] türünde bir istemci kullanmak yeterlidir.
Şimdi URL ve [https://localhost/client-vuejs-impot/liste-des-simulations]’i isteyelim. Aşağıdaki görünümle karşılaşırız:

Şimdi de URL ve [https://localhost/client-vuejs-impot/fin-session]'i deneyelim. Aşağıdaki görünümle karşılaşırız:

Şimdi ise mevcut olmayan bir görünüm olan [https://localhost/client-vuejs-impot/abcd]:

Uygulamamız, elle girilen URL'lere karşı oldukça dayanıklıdır. Bunlar çağrıldığında, uygulama yönlendiricisi bunu bilir. Dolayısıyla, görünüm nihai olarak görüntülenmeden önce müdahale etmek mümkündür. Bu konuyu [vuejs-21] projesinde inceleyeceğiz.
Dikkat edilmesi gereken bir başka nokta da şudur. Kullanıcının kurallara uygun olarak birkaç simülasyon yaptığını varsayalım:

Şimdi sayfayı bir F5 ile yenileyelim:

Tavsiye edilmeyen bir şey yaptık: URL kodunu elle girdik (F5 yapmak da buna eşdeğerdir). Böylece simülasyonlarımızı kaybettik.
Bir sonraki proje olan [vuejs-21], iki iyileştirme getirmeyi amaçlamaktadır:
- kullanıcı tarafından girilen URL değerlerini kontrol etmek;
- kullanıcı bir URL girse bile uygulamanın hafızasını korumak. Yukarıda, simülasyon listesini kaybettiğimizi görüyoruz;