Skip to content

19. Vue.js istemcisinde yapılan iyileştirmeler

19.1. Introduction

[vuejs-21] projesini geliştirme sunucusuyla test edeceğiz. Bu nedenle, sunucunun CORS başlıklarını göndermesi gerekecek. Bu nedenle, vergi hesaplama sunucusunun 14. sürümündeki [config.json] dosyasının bu başlıkları desteklemesi gerekir:

Image

[vuejs-21] projesi, başlangıçta [vuejs-20] projesinin kopyalanmasıyla oluşturulmuştur. Daha sonra [3] olarak değiştirilmiştir.

Yeni dosyalar ortaya çıkar:

  • [session.js]: mevcut oturumla ilgili bilgileri içerecek olan [session] nesnesini dışa aktarır;
  • [pluginSession]: önceki [session] nesnesini, görünümlerin [$session] özelliğinde kullanılabilir hale getirir;
  • [NotFound.vue]: Kullanıcı, mevcut olmayan bir URL nesnesini manuel olarak talep ettiğinde görüntülenen yeni bir görünüm;

Aşağıdaki dosyalar değiştirilecektir:

  • [main.js]: mevcut oturumu başlatacak ve kullanıcı URL'i manuel olarak girdiğinde oturumu geri yükleyecektir;
  • [router.js]: Kullanıcı tarafından girilen URL durumlarını işlemek için denetimler eklenir;
  • [store.js]: Yeni bir değişiklik eklenir;
  • [config.js]: yeni bir yapılandırma eklenmiştir;
  • uygulamanın işleyişi sırasında önemli anlarda mevcut oturumu kaydetmek amacıyla çeşitli görünümler eklenmiştir. Bu oturum, kullanıcı URL kodlarını manuel olarak her girdiğinde geri yüklenir;

19.2. [Vuex] mağazası

[./store] komut dosyası şu şekilde gelişir:


// 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) {
      ...
    },
    // simülasyon ekleme
    addSimulation(state, simulation) {
      ...
    },
    // durum temizleme
    clear(state) {
      // simülasyon kalmadı
      state.simulations = [];
      // simülasyon numaralandırması 0'dan yeniden başlıyor
      state.idSimulation = 0;
    }
  }
});
// [store] nesnesinin dışa aktarımı
export default store;
  • 24-29. satırlar: [clear] değişikliği, kaydedilmiş simülasyon listesini siler ve son simülasyon numarasını 0'a sıfırlar.

19.3. Oturum

Bir oturumun gerekli olmasının nedeni, kullanıcının tarayıcının adres çubuğuna bir URL yazdığında, [main.js] komut dosyasının yeniden çalıştırılmasıdır. Oysa bu komut dosyası şu talimatı içerir:


// Vuex deposu
import store from './store'

Bu komut, aşağıdaki [./store] dosyasını içe aktarır:


// 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: {
    ...
  }
});
// [store] nesnesinin dışa aktarımı
export default store;

7-13. satırlarda, boş bir simülasyon tablosunun içe aktarıldığı görülmektedir. Dolayısıyla, kullanıcı tarayıcının adres çubuğuna URL yazmadan önce simülasyonlar varsa, sonrasında bunlar kaybolur. Buradaki fikir şudur:

  • kullanıcı URL kodunu manuel olarak girdiğinde, saklamak istediğimiz bilgileri depolayacak bir oturum kullanmak;
  • uygulamanın önemli anlarında bu bilgileri kaydetmek;
  • URL manuel olarak girildiğinde her zaman çalıştırılan [main.js]'te bu bilgileri geri yüklemek;

[./session] komut dosyası şu şekildedir:


// Vuex mağazasını içe aktarıyoruz
import store from './store'
// yapılandırma içe aktarılıyor
import config from './config';

// [session] nesnesi
const session = {
  // oturum başlatıldı
  started: false,
  // kimlik doğrulama
  authenticated: false,
  // kaydetme saati
  saveTime: "",
  // katman [métier]
  métier: null,
  // Vuex durumu
  state: null,

  // oturumun bir dizede kaydedilmesi jSON
  save() {
    // oturuma bazı özellikler ekleniyor
    this.saveTime = Date.now();
    this.state = store.state;
    // jSON'e dönüştürülür
    const json = JSON.stringify(this);
    // tarayıcıda saklanıyor
    localStorage.setItem("session", json);
    // eslint-disable-next-line no-console
    console.log("session save", json);
  },

  // oturumun geri yüklenmesi
  restore() {
    // tarayıcıdan jSON oturumunu alıyoruz
    const json = localStorage.getItem("session")
    // eğer bir şey alınmışsa
    if (json) {
      // oturumun tüm anahtarları geri yüklenir
      const restore = JSON.parse(json);
      for (var key in restore) {
        if (restore.hasOwnProperty(key)) {
          this[key] = restore[key];
        }
      }
      // oturumun başlangıcından itibaren belirli bir süre boyunca hiçbir işlem yapılmamışsa, sıfırdan başlanır
      let durée = Date.now() - this.saveTime;
      if (durée > config.duréeSession) {
        // oturumu boşaltırız - oturum yine de kaydedilir
        session.clear();
      } else {
        // Vuex deposu yeniden oluşturulur
        store.replaceState(JSON.parse(JSON.stringify(this.state)));
      }
    }
    // eslint-disable-next-line no-console
    console.log("session restore", this);
  },

    // oturumu temizliyoruz
  clear() {
    // eslint-disable-next-line no-console
    console.log("session clear");
    // oturumun belirli alanları sıfırlanıyor
    this.authenticated = false;
    this.saveTime = "";
    this.started = false;
    if (this.métier) {
      // [taxAdminData] alanını sıfırlıyoruz
      this.métier.taxAdminData = null;
    }
    // Vuex deposu da temizlenir
    store.commit("clear");
    // yeni oturum kaydediliyor
    this.save();
  },
}

// [session] nesnesinin dışa aktarılması
export default session;

Açıklamalar

  • 2. satır: oturum, [Vuex] depolama alanını da kapsayacaktır (simülasyon listesi, yapılan son simülasyonun numarası);
  • 7-17. satırlar: oturum tarafından saklanan bilgiler:
    • [started]: Sunucu ile jSON oturumu başlatıldı mı, başlatılmadı mı;
    • [authenticated]: Kullanıcının kimlik doğrulaması yapıp yapmadığı;
    • [saveTime]: son yedeklemenin milisaniye cinsinden tarihi;
    • [métier]: [métier] katmanına ilişkin bir referans. Bu referans, vergi hesaplamasına olanak tanıyan [taxAdminData] verisini içerir;
    • [state]: [Vuex] deposunun durumu (simülasyon listesi, yapılan son simülasyonun numarası);
  • 20-30. satırlar: [save] yöntemi, oturumu uygulamayı çalıştıran tarayıcıda yerel olarak kaydeder;
    • 22. satır: Kaydetme saati kaydedilir;
    • satır 23: [Vuex] mağazasından [state] alınır;
    • 25. satır: oturumun jSON dizesi oluşturulur;
    • 27. satır: Bu dize, [session] anahtarıyla ilişkilendirilerek tarayıcıda yerel olarak saklanır;
  • 33-57. satırlar: [restore] yöntemi, tarayıcıdaki yerel yedekten bir oturumu geri yüklemeye olanak tanır;
    • 35. satır: Yerel jSON yedeklemesi alınır;
    • 37. satır: Bir şey yüklendiyse;
    • satır 39-44: [session] nesnesi yeniden oluşturulur;
    • satır 46: son yedeklemeden bu yana geçen süre hesaplanır;
    • satır 47-50: bu süre, yapılandırma ile belirlenen [config.duréeSession] değerinden büyükse, oturum sıfırlanır (satır 49) ve bu sırada yedeklenir;
    • satır 52: aksi takdirde, [Vuex] deposunun [state] özniteliği yeniden oluşturulur;
  • satır 60-75: [clear] yöntemi oturumu sıfırlar;
    • satır 64-70: oturum özellikleri başlangıç değerlerine sıfırlanır;
    • 72. satır: [Vuex] depolama alanı da sıfırlanır;
    • 74. satır: yeni oturum kaydedilir;

19.4. [config] yapılandırma dosyası

[./config] dosyası şu şekilde değişir:


// [axios] kütüphanesi kullanılıyor
const axios = require('axios');
// istek zaman aşımı HTTP
axios.defaults.timeout = 2000;
...

// yapılandırmanın dışa aktarılması
export default {
  // [axios] nesnesi
  axios: axios,
  // oturumun maksimum hareketsizlik süresi: 5 dakika = 300 saniye = 300000 milisaniye
  duréeSession: 300000
}
  • 12. satır: Uygulama oturumunu, bir web oturumunu yönettiğimiz gibi yöneteceğiz. Burada maksimum 5 dakikalık bir hareketsizlik süresi belirliyoruz;

19.5. [pluginSession] eklentisi

Daha önce birçok kez yapıldığı gibi, [pluginSession] eklentisi, görünümlerin [this.$session] özelliği aracılığıyla oturuma erişmesine olanak sağlayacaktır:


export default {
  install(Vue, session) {
    // görünüm sınıfına [$session] özelliğini ekler
    Object.defineProperty(Vue.prototype, '$session', {
      // Vue.$session referanslandığında, ikinci parametre [session] olarak ayarlanır
      get: () => session,
    })
  }
}

19.6. Ana komut dosyası [main]

Ana komut dosyası [./main.js] şu şekilde güncellenmiştir:


// başlangıç günlüğü
// eslint-disable-next-line no-console
console.log("main started");

// ithalatlar
import Vue from 'vue'

...

// katman örneklendirme [métier]
import Métier from './couches/Métier';
const métier = new Métier();

// eklenti [métier]
import pluginMétier from './plugins/pluginMétier'
Vue.use(pluginMétier, métier)

// Vuex depolama
import store from './store'

// oturum
import session from './session';
import pluginSession from './plugins/pluginSession'
Vue.use(pluginSession, session)

// yeniden başlatmadan önce oturum geri yüklenir
session.restore();

// [métier] katmanı geri yükleniyor
if (session.métier && session.métier.taxAdminData) {
  métier.setTaxAdminData(session.métier.taxAdminData);
}

// UI'in başlatılması
new Vue({
  el: '#uygulama,
  // yönlendirici
  router: router,
  // Vuex panosu
  store: store,
  // ana ekran
  render: h => h(Main),
})

// son kayıt
// eslint-disable-next-line no-console
console.log("main terminated, session=", session);
  • 19. satır: oturum içe aktarılır;
  • 20. satır: eklentisi içe aktarılır;
  • 21. satır: [pluginSession] eklentisi, [Vue]'e entegre edilir. Bu komuttan sonra tüm görünümler, [$session] özniteliğinde oturuma sahip olur;
  • 27. satır: oturum geri yüklenir. 11. satırda içe aktarılan oturum, son yedeklemesinin içeriğiyle başlatılır;
  • 16. satırdan sonra, görünümler 12. satırda başlatılan [$métier] özelliğine sahip olur. Bu özellik, vergiyi hesaplamaya olanak tanıyan [taxAdminData] bilgisini içermez;
  • 30-32. satırlar: Az önce yapılan geri yükleme işlemi [session.métier.taxAdminData] özelliğini geri yüklediyse, görünümlerin [$métier] özelliği bu değerle başlatılır;

19.7. [router] yönlendirme dosyası

[./router] yönlendirme dosyası şu şekilde değişir:


// 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'
import NotFound from './views/NotFound'
// oturum
import session from './session'

// yönlendirme eklentisi
Vue.use(VueRouter)

// uygulama rotaları
const routes = [
  // kimlik doğrulama
  { path: '/', name: 'authentification', component: Authentification },
  { path: '/authentification', name: 'authentification', component: Authentification },
  // vergi hesaplaması
  {
    path: '/calcul-impot', name: 'calculImpot', component: CalculImpot,
    meta: { authenticated: true }
  },
  // simülasyon listesi
  {
    path: '/liste-des-simulations', name: 'listeSimulations', component: ListeSimulations,
    meta: { authenticated: true }
  },
  // oturum sonlandırma
  {
    path: '/fin-session', name: 'finSession'
  },
  // bilinmeyen sayfa
  {
    path: '*', name: 'notFound', component: NotFound,
  },
]

// yönlendirici
const router = new VueRouter({
  // yollar
  routes,
  // URL görüntüleme modu
  mode: 'history',
  // uygulamanın temel URL'i
  base: '/client-vuejs-impot/'
})

// yolların doğrulanması
router.beforeEach((to, from, next) => {
  // eslint-disable-next-line no-console
  console.log("router to=", to, "from=", from);
  // kimliği doğrulanmış kullanıcılara özel yol mu?
  if (to.meta.authenticated && !session.authenticated) {
    next({
      // kimlik doğrulamaya geçiliyor
      name: 'authentification',
    })
    // olay döngüsüne geri dönüş
    return;
  }
  // oturum sonu özel durumu
  if (to.name === "finSession") {
    // oturum temizleniyor
    session.clear();
    // [authentification] görünümüne geçiliyor
    next({
      name: 'authentification',
    })
    // olay döngüsüne geri dönülür
    return;
  }
  // diğer durumlar - normal yönlendirme sonraki görünümü
  next();
})

// yönlendiricinin dışa aktarımı
export default router

Açıklamalar

  • 16-38. satırlar: Bazı rotalar ek bilgilerle zenginleştirilmiştir;
  • 19. satır: [Authentification] manzarasına giden yeni bir rota oluşturulmuştur;
  • 21-24. satırlar: [CalculImpot] görünümüne giden yol artık [meta] özelliğine sahiptir (bu ad zorunludur). Bu nesnenin içeriği herhangi bir şey olabilir ve geliştirici tarafından belirlenir;
  • 23. satır: [meta] nesnesine [authenticated] özelliğini ekliyoruz (bu ad herhangi bir şey olabilir). Bu, bizim için [CalculImpot] görünümüne gitmek için kullanıcının kimlik doğrulamasından geçmesi gerektiği anlamına gelir;
  • 26-29. satırlar: [ListeSimulations] görünümüne giden yol için de aynısını yapıyoruz. Burada da kullanıcının kimlik doğrulaması yapılmış olması gerekir;
  • [meta.authenticated] özelliği, [CalculImpot, ListeSimulations] görünümlerinin URL adreslerini manuel olarak giren bir kullanıcının, kimlik doğrulaması yapılmamışsa bu adreslere erişemeyeceğini kontrol etmemizi sağlayacaktır;
  • 51-76. satırlar: [beforeEach] yöntemi, bir görünüm yönlendirilmeden önce yürütülür. Bu, doğrulamaları yapmak için uygun bir zamandır;
    • [to]: Hiçbir işlem yapılmazsa bir sonraki yönlendirme;
    • [from]: en son görüntülenen rota;
    • [next]: görüntülenecek bir sonraki rotayı değiştirmeye yarayan işlev;
  • 55. satır: Bir sonraki rotanın kullanıcının kimlik doğrulamasını gerektirip gerektirmediğine bakılır;
  • satır 56-59: eğer öyleyse ve kullanıcı kimliği doğrulanmamışsa, bir sonraki rota [Authentification] görünümüne değiştirilir;
  • satır 64-73: satır 30-32'deki [finSession] rotasının özel durumu işlenir. Bu rotanın ilişkili bir görünümü yoktur;
    • satır 66: oturum, başlangıç değerine sıfırlanır;
    • satır 68-70: [Authentification] görünümünü bir sonraki görünüm olarak programlıyoruz;
  • satır 75: Yukarıdaki iki durumdan biri geçerli değilse, yönlendirme dosyasında belirtilen rotaya geçilir;
  • satır 35-37: Kullanıcı tarafından girilen rota bilinen hiçbir rotaya uymuyorsa, [NotFound] görünümü hazırlanır. Bu görünüm 8. satırda içe aktarılır. Rotalar, rota dosyasında belirtilen sırayla kontrol edilir. Dolayısıyla, 36. satıra gelinmişse, bu, istenen rotanın 18-33. satırlardaki rotalardan hiçbiri olmadığı anlamına gelir;

19.8. [NotFound] görünümü

Kullanıcı tarafından girilen rota bilinen hiçbir rotaya uymuyorsa [NotFound] görünümü görüntülenir:

Image

Görünümün kodu şöyledir:


<!-- HTML görünüm tanımı -->
<template>
  <!-- sayfa düzeni -->
  <Layout :left="true" :right="true">
    <!-- sağ sütundaki uyarı -->
    <template slot="right">
      <!-- sarı arka planlı mesaj -->
      <b-alert show variant="danger" align="center">
        <h4>Cette page n'existe pas</h4>
      </b-alert>
    </template>
    <!-- sol sütundaki gezinme menüsü -->
    <Menu slot="left" :options="options" />
  </Layout>
</template>

<script>
// ithalatlar
import Layout from "./Layout";
import Menu from "./Menu";
export default {
  // bileşenler
  components: {
    Layout,
    Menu
  },
  // bileşenin dahili durumu
  data() {
    return {
      // gezinme menüsü seçenekleri
      options: [
        {
          text: "Authentification",
          path: "/"
        }
      ]
    };
  },
  // yaşam döngüsü
  created() {
    // eslint-disable-next-line
    console.log("NotFound created");
    // hangi menü seçeneklerinin sunulacağına bakıyoruz
    if (this.$session.authenticated && this.$métier.taxAdminData) {
      // kullanıcı simülasyonlar yapabilir
      Array.prototype.push.apply(this.options, [
        {
          text: "Calcul de l'impôt",
          path: "/calcul-impot"
        },
        {
          text: "Liste des simulations",
          path: "/liste-des-simulations"
        }
      ]);
    }
  }
};
</script>

Açıklamalar

  • 4. satır: yönlendirilmiş görünümlerin iki sütununu kullanır;
  • 6-11. satırlar: bir hata mesajı;
  • 13. satır: gezinme menüsü sol sütunu kaplıyor;
  • 31-36. satırlar: menünün varsayılan seçenekleri;
  • satır 40-57: görünüm oluşturulduğunda yürütülen kod;
  • 44. satır: Kullanıcının simülasyon yapıp yapamayacağına bakılır;
  • 45-55. satırlar: eğer yapabiliyorsa, gezinme menüsüne iki seçenek eklenir; bu seçenekler için oturum açılmış olması ve [métier] katmanının çalışır durumda olması gerekir (46-55. satırlar);

19.9. [Authentification] görünümü

[Authentification] görünümü şu şekilde değişir:


<!-- görünümün HTML tanımı -->
<template>
  <Layout :left="false" :right="true">
    ...
  </Layout>
</template>

<!-- görünümün dinamikleri -->
<script>
import Layout from "./Layout";
export default {
  // bileşenin durumu
  data() {
    return {
      // kullanıcı
      user: "",
      // şifresi
      password: "",
      // hata mesajının görüntülenmesini kontrol eder
      showError: false,
      // hata mesajı
      message: ""
    };
  },

  // kullanılan bileşenler
  components: {
    Layout
  },

  // hesaplanan özellikler
  computed: {
    // geçerli girdiler
    valid() {
      return this.user && this.password && this.$session.started;
    }
  },

  // olay işleyicileri
  methods: {
    // ----------- kimlik doğrulama
    async login() {
      try {
        // bekleme başlangıcı
        this.$emit("loading", true);
        // henüz kimlik doğrulaması yapılmadı
        this.$session.authenticated = false;
        // sunucuda engelleyici kimlik doğrulama
        const response = await this.$dao.authentifierUtilisateur(
          this.user,
          this.password
        );
        // yükleme bitti
        this.$emit("loading", false);
        // sunucunun yanıtı analiz ediliyor
        if (response.état != 200) {
          // hata görüntüleniyor
          this.message = response.réponse;
          this.showError = true;
          // olay döngüsüne geri dönüş
          return;
        }
        // hata yok
        this.showError = false;
        // kimlik doğrulaması yapıldı
        this.$session.authenticated = true;
        // --------- şimdi vergi idaresinden veriler isteniyor
        // başlangıçta veri yok
        this.$métier.setTaxAdminData(null);
        // bekleme başladı
        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;
          // olay döngüsüne geri dönüş
          return;
        }
        // hata yok
        this.showError = false;
        // alınan veri [métier] katmanında saklanıyor
        this.$métier.setTaxAdminData(response2.réponse);
        // vergi hesaplamasına geçilebilir
        this.$router.push({ name: "calculImpot" });
      } catch (error) {
        // hata ana bileşene iletilir
        this.$emit("error", error);
      } finally {
        // oturum güncellenir
        this.$session.métier = this.$métier;
        // oturum kaydediliyor
        this.$session.save();
      }
    }
  },
  // yaşam döngüsü: bileşen az önce oluşturuldu
  created() {
    // eslint-disable-next-line
    console.log("Authentification created");
    // kullanıcı simülasyon yapabilir mi?
    if (
      this.$session.started &&
      this.$session.authenticated &&
      this.$métier.taxAdminData
    ) {
      // o halde kullanıcı simülasyon yapabilir
      this.$router.push({ name: "calculImpot" });
      // olay döngüsüne geri dön
      return;
    }
    // jSON oturumu daha önce başlatılmışsa, yeniden başlatılmaz
    if (!this.$session.started) {
      // bekleme başlar
      this.$emit("loading", true);
      // sunucu ile oturum başlatılır - asenkron istek
      // [dao] katmanındaki yöntemler tarafından döndürülen söz kullanılır
      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;
            // olay döngüsüne geri dönülüyor
            return;
          }
          // oturum başlatıldı
          this.$session.started = true;
        })
        // hata durumunda
        .catch(error => {
          // hata, [Main] görünümüne iletilir
          this.$emit("error", error);
        })
        // her durumda
        .finally(() => {
          // oturum kaydedilir
          this.$session.save();
        });
    }
  }
};
</script>

Açıklamalar

  • [Vue.js] istemci sürümüne eklenen oturumu kullanan komutlar sarı renkle vurgulanmıştır;
  • 97. ve 148. satırlar: [login, created] yöntemlerinin sonunda, bu yöntemlerde gerçekleştirilen HTTP sorgularının sonucu ne olursa olsun oturum kaydedilir (her iki durumda da [finally] cümlesi);
  • 102-150. satırlardaki [created] yöntemi, [Authentification] görünümü her oluşturulduğunda yürütülür. Görünümün URL kodunu kullanıcı girmişse, oturum ne yapılması gerektiğini bize gösterecektir;
  • 106-115. satırlar: jSON oturumu başlatılmışsa, kullanıcı kimliği doğrulanmışsa ve [this.$métier.taxAdminData] verisi başlatılmışsa, kullanıcı doğrudan vergi hesaplama formuna (112. satır) gidebilir;
  • satır 117: [created] yöntemi, önceki sürümde sunucu ile bir jSON oturumu başlatmak için kullanılıyordu. Bu aşama, daha önce gerçekleştirilmişse gereksizdir;
  • satır 42-66: kimlik doğrulama yöntemi;
  • satır 66: kimlik doğrulama başarılı olursa, bu durum oturuma kaydedilir;
  • 67-92. satırlar: vergi idaresi verileri için sunucuya yapılan istek ([taxAdminData]);
  • 95. satır: Bu aşamanın sonunda, işlemin başarılı olup olmadığına bakılmaksızın oturumun [métier] özelliği güncellenir;

19.10. [CalculImpot] görünümü

[CalculImpot] görünümünün kodu şu şekilde değişir:


<!-- HTML görünümünün tanımı -->
<template>
  ...
</template>

<script>
// ithalatlar
import FormCalculImpot from "./FormCalculImpot";
import Menu from "./Menu";
import Layout from "./Layout";

export default {
  // iç durum
  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önetim yöntemleri
  methods: {
    // vergi hesaplama sonucu
    handleResultatObtenu(résultat) {
      // sonuç, HTML dizisi olarak oluşturulur
      ...
      // bir simülasyon +
      this.$store.commit("addSimulation", résultat);
      // oturum kaydediliyor
      this.$session.save();
    }
  },
  // yaşam döngüsü
  created() {
    // eslint-disable-next-line
    console.log("CalculImpot created");
  }
};
</script>

Açıklamalar

  • 45. satır: Hesaplanan simülasyon, [Vuex] depolama alanına eklenir. Bu, depolama alanının [state] özelliğini içeren oturumu etkiler. Bu nedenle oturum kaydedilir (47. satır);
  • 51. satır: Günlüklerde görünüm oluşturma işlemlerini takip etmek için [created] yöntemi oluşturulur;

19.11. [ListeSimulations] görünümü

[ListeSimulations] görünümü şu şekilde gelişir:


<!-- HTML görünümünün tanımı -->
<template>
  ...
  </div>
</template>

<script>
// ithalatlar
import Layout from "./Layout";
import Menu from "./Menu";
export default {
  // bileşenler
  components: {
    Layout,
    Menu
  },
  // iç durum
  data() {
    ...
  },
  // hesaplanan iç durum
  computed: {
    // Vuex deposundan 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);
      // oturum kaydediliyor
      this.$session.save();
    }
  },
  // yaşam döngüsü
  created() {
    // eslint-disable-next-line
    console.log("ListeSimulations created");
  }
};
</script>

Açıklamalar

  • 36. satır: 34. satırdaki bir simülasyonun silinmesinden sonra, bu durum değişikliğini yansıtmak için oturum kaydedilir;
  • 40-43. satırlar: Görünümlerin oluşturulmasını takip etmeye devam ediyoruz;

19.12. Projenin çalıştırılması

Image

Testler sırasında aşağıdaki noktaları kontrol edin:

  • kullanıcı, gezinme menüsündeki bağlantılar ve eylem düğmeleri/bağlantıları aracılığıyla uygulamayı "kullanıyorsa", uygulama çalışıyor mu;
  • kullanıcı URL kodlarını manuel olarak girerse, uygulama çalışmaya devam ediyor. Özellikle aşağıdaki testi yapın:
    • simülasyonlar yapın;
    • [ListeSimulations] görünümüne geldikten sonra, görünümü yeniden yükleyin (F5). Önceki uygulamada ([vuejs-20]), bu durumda simülasyonlar kayboluyordu. Burada durum böyle değildir: daha önce yapılmış simülasyonlar aynen korunur;
  • anlamak için loglara bakın:
    • [main] komut dosyasının ne zaman çalıştırıldığını kontrol edin. Kullanıcının URL'i elle her girdiğinde komut dosyasının çalıştırıldığını görmelisiniz;
    • görünümlerin ne zaman oluşturulduğunu. Bunların, görüntülenecekleri her seferinde oluşturulduğunu görmelisiniz;
    • yönlendirmenin nasıl çalıştığını. Her yönlendirmeden önce, size şunları gösteren bir günlük kaydı oluşturulur:
      • geldiğiniz yol;
      • gideceğiniz yol;

19.13. Uygulamanın yerel bir sunucuya dağıtılması

Alıştırma olarak, |Déploiement sur un serveur local| paragrafını takip ederek [vuejs-21] projesini yerel Laragon sunucusuna dağıtın. Ardından test edin.

19.14. Mobil sürümün geliştirilmesi

Teorik olarak, Bootstrap kullanımı sayesinde akıllı telefon, tablet, dizüstü ve masaüstü bilgisayarlar gibi farklı cihazlarda kullanılabilir bir uygulamaya sahip olmamız gerekir. Bu cihazları birbirinden ayıran şey ekran boyutlarıdır.

[vuejs-21] sürümünü bir mobil cihazda test ettiğimizde, görünümlerin görüntülenmesinde bir kaos olduğu görülür. [vuejs-22] sürümü bu sorunu giderir. Değişikliklerin tamamı görünüm şablonlarında yapılmıştır. Bu değişiklikler temel olarak bir akıllı telefon ekranı için uygun bir görüntüleme ayarlamasından ibarettir. Bu ayar yapıldığında, Bootstrap sayesinde daha büyük ekranlarda görüntüleme sorunsuz bir şekilde gerçekleşir.

Image

19.14.1. [Main] görünümü

[Main] görünümü şu şekilde değişiyor:


<!-- HTML görünümünün tanımı -->
<template>
  <div class="container">
    <b-card>
      <!-- jumbotron -->
      <b-jumbotron>
        <b-row>
          <b-col sm="4">
            <img src="../assets/logo.jpg" alt="Cerisier en fleurs" />
          </b-col>
          <b-col sm="8">
            <h1>Calculez votre impôt</h1>
          </b-col>
        </b-row>
      </b-jumbotron>
      ....
    </b-card>
  </div>
</template>

Açıklamalar

  • 8. satır: [cols=’4’] yazan yere [sm=’4’] yazılır. [sm], [small] anlamına gelir. Akıllı telefon ekranları bu kategoriye girer. Diğer kategoriler şunlardır: [xs=extra small, md=medium, lg=large, xl=extra large];
  • 11. satır: aynı;

19.14.2. [Layout] görünümü

[Layout] görünümü şu şekilde değişir:


<!-- yönlendirilmiş görünümün mizanpajının HTML tanımı -->
<template>
  <!-- satır -->
  <div>
    <b-row>
      <!-- sol tarafta üç sütunlu alan -->
      <b-col sm="3" v-if="left">
        <slot name="left" />
      </b-col>
      <!-- sağdaki dokuz sütunlu alan -->
      <b-col sm="9" v-if="right">
        <slot name="right" />
      </b-col>
    </b-row>
  </div>
</template>

19.14.3. [Authentification] görünümü

[Authentification] görünümü şu şekilde değişir:


<!-- 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" description="Tapez admin">
          <!-- kullanıcı giriş alanı -->
          <b-col sm="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" description="Tapez admin">
          <!-- şifre giriş alanı -->
          <b-col sm="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 sm="2">
            <b-button variant="primary" type="submit" :disabled="!valid">Valider</b-button>
          </b-col>
        </b-row>
      </b-form>
    </template>
  </Layout>
</template>

Açıklamalar

  • 11. ve 19. satırlar: Giriş etiketinin sütun sayısını belirleyen [label-cols] özniteliği kaldırılmıştır. Bu özniteliğin olmaması durumunda, etiket giriş alanının üstünde yer alır. Bu, akıllı telefon ekranları için daha uygundur;

19.14.4. [CalculImpot] görünümü

[CalculImpot] görünümü şu şekilde değiştirilmiştir:


<!-- HTML görünüm 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ında vergi hesaplama sonuçlarının görüntülendiği alan -->
    <b-row v-if="résultatObtenu" class="mt-3">
      <!-- boş üç sütunlu alan -->
      <b-col sm="3" />
      <!-- dokuz sütunlu alan -->
      <b-col sm="9">
        <b-alert show variant="success">
          <span v-html="résultat"></span>
        </b-alert>
      </b-col>
    </b-row>
  </div>
</template>

19.14.5. [FormCalculImpot] görünümü

[FormCalculImpot] görünümü şu şekilde değişir:


<!-- 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-row>
      <b-col sm="12">
        <b-alert show variant="primary">
          <h4>Remplissez le formulaire ci-dessous puis validez-le</h4>
        </b-alert>
      </b-col>
    </b-row>
    <!-- form öğeleri -->
    <!-- ilk satır -->
    <b-form-group label="Etes-vous marié(e) ou pacsé(e) ?">
      <!-- 5 sütunlu radyo düğmeleri-->
      <b-col sm="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-for="enfants">
      <b-form-input
        type="text"
        id="enfants"
        placeholder="Indiquez votre nombre d'enfants"
        v-model="enfants"
        :state="enfantsValide"
      ></b-form-input>
      <!-- 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 net imposable"
      label-for="salaire"
      description="Arrondissez à l'euro inférieur"
    >
      <b-form-input
        type="text"
        id="salaire"
        placeholder="Salaire annuel"
        v-model="salaire"
        :state="salaireValide"
      ></b-form-input>
      <!-- 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, düğme [submit] -->
    <b-col sm="3">
      <b-button type="submit" variant="primary" :disabled="formInvalide">Valider</b-button>
    </b-col>
  </b-form>
</template>

Açıklamalar

  • 15, 23, 35. satırlar: [label-cols] özniteliği kaldırılmıştır;

Ayrıca, geçerlilik testleri de şu şekilde değiştirilmiştir:


...
// hesaplanan dahili durum
  computed: {
    // formun onaylanması
    formInvalide() {
      return (
        // geçersiz maaş
        !this.salaire.match(/^\s*\d+\s*$/) ||
        // veya geçersiz çocuk sayısı
        !this.enfants.match(/^\s*\d+\s*$/) ||
        // 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*$/) || this.salaire.match(/^\s*$/)
      );
    },
    // çocuk bilgilerinin doğrulanması
    enfantsValide() {
      // sayısal olmalı >=0
      return Boolean(
        this.enfants.match(/^\s*\d+\s*$/) || this.enfants.match(/^\s*$/)
      );
    }
},
...

Yorumlar

  • 19. satır: Hiçbir şey girilmediğinde, giriş geçerli kabul edilir. Bu, sayfa ilk açıldığında geçerli bir girişin olmasını sağlar. Önceki sürümde, giriş başlangıçta hatalı olarak görünüyordu;
  • 26. satır: aynı şekilde;
  • 5-14. satırlar: Onay düğmesi, her iki alana da bir şeyler girilmiş ve bu girdiler geçerli ise etkin hale gelir;

19.14.6. [Menu] görünümü

[Menu] görünümü şu şekilde değişir:


<!-- HTML görünümünün tanımı -->
<template>
  <b-card class="mb-3">
    <!-- 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>
  </b-card>
</template>

Açıklamalar

  • 3. satır: Menüyü ince bir çerçeveyle çevrelemek için <b-card> etiketi eklenir. Bu, menünün akıllı telefonda daha iyi konumlandırılmasını sağlar;

19.14.7. [ListeSimulations] görünümü

[ListeSimulations] görünümü değişmeden kalır:


<!-- HTML görünüm 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>

Açıklamalar

  • 20. satır: Tablonun ekran boyutuna göre uyarlanmasını sağlayan [responsive] özniteliğine dikkat edin:

Image

  • [2]'te ise küçük ekranlarda, tabloyu görüntülemek için yatay bir kaydırma çubuğu kullanılır;

19.14.8. [NotFound] görünümü

Değişmez.

19.14.9. Mobil cihazlardaki görünümler

Image

Image

Not: Mobil cihazlara daha uygun görünümler elde etmenin kesinlikle bir yolu vardır. Özellikle gezinme menüsünün iyileştirilebileceğini düşünüyorum, ancak başka noktalar da var. Bu belgenin birincil amacı bir mobil uygulama oluşturmak değildi. Öyle olsaydı, belki de Ionic |https://ionicframework.com/| gibi bir çerçeveye yönelirdik.