Wednesday, May 7, 2025

TEMPLATE VUETIFY, VUE 2 DAN VUE-ROUTER versi 2

Selamat datang di blog ali-mahdali.blogstpot.com, kali ini penulis memposting artikel yang berjudul TEMPLATE VUETIFY, VUE 2 DAN VUE-ROUTER versi 2 yang mana artikel ini dapat kalian akses melalui alamat : https://ali-mahdali.blogspot.com/2025/05/template-vuetify-vue-2-dan-vue-router.html,
tanpa basa-basi yuk disimak artikelnya dibawah ini. Selamat membaca

Berikut ini adalah skrip template vue dengan kecepatan dan keandalan yang luar biasa, karna pemanggilan dan penggunaan skrip sangatlah mudah.










<!DOCTYPE html>
<html lang="en" style="overflow: hidden;">

<head>
  <meta charset="utf-8">
  <title>Vue Vuetify Dialog Dynamic</title>
  <link href="https://cdn.jsdelivr.net/npm/@mdi/font/css/materialdesignicons.min.css" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.5.10/dist/vuetify.min.css" rel="stylesheet">
</head>

<body>
  <div id="app">
    <v-app>
      <!-- Global Snackbar -->
      <v-snackbar v-model="snackbar.show" :timeout="snackbar.timeout" :color="snackbar.color">
        {{ snackbar.text }}
        <template v-slot:action="{ attrs }">
          <v-btn text v-bind="attrs" @click="snackbar.show = false">Tutup</v-btn>
        </template>
      </v-snackbar>

      <!-- Dialog Stack -->
      <template v-for="(dialog, index) in dialogs">
        <v-dialog
          :key="dialog.id"
          v-model="dialog.show"
          :max-width="dialog.width"
          :persistent="dialog.persistent"
          :z-index="500 + index * 10"
        >
          <v-card v-if="dialog.type !== 'loading'">
            <v-toolbar dark class="mb-4" color="primary">
              <v-toolbar-title>{{ dialog.title }}</v-toolbar-title>
            </v-toolbar>
            <v-card-text>
              <div v-if="dialog.type === 'info'">{{ dialog.content }}</div>
              <component v-else :is="dialog.template" v-model="dialog.data"></component>
            </v-card-text>
            <v-card-actions>
              <v-spacer></v-spacer>
              <v-btn
                color="primary"
                @click="() => {
                  dialog.actions.ok.action(dialog.data);
                  if (dialog.actions.ok.close !== false) {
                    closeDialog(dialog.id);
                  }
                }"
              >
                {{ dialog.actions.ok.title || 'OK' }}
              </v-btn>
              <v-btn
                v-if="dialog.actions.cancel"
                text
                @click="() => {
                  if (dialog.actions.cancel.action) {
                    dialog.actions.cancel.action(dialog.data);
                  }
                  closeDialog(dialog.id);
                }"
              >
                {{ dialog.actions.cancel.title || 'Cancel' }}
              </v-btn>
            </v-card-actions>
          </v-card>
          <v-card v-else flat tile>
            <v-card-text>
              <div style="display: flex; justify-content: center; align-items: center; height: 100px;">
                <v-progress-circular indeterminate color="primary" size="40" />
              </div>
            </v-card-text>
          </v-card>
        </v-dialog>
      </template>

      <router-view />
    </v-app>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue-router@3.6.5/dist/vue-router.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.5.10/dist/vuetify.js"></script>

  <script>
    const crud = (op, sheetName, params) => {
      const db = JSON.parse(localStorage.getItem('localDB')) || {};
      if (!db[sheetName]) db[sheetName] = { headers: [], rows: [] };
      const sheet = db[sheetName];
      const res = { status: '', data: [], message: '' };

Sebelum melanjutkan membaca artikel ini, berikut adalah artikel yang berkaitan dengan judul artikel TEMPLATE VUETIFY, VUE 2 DAN VUE-ROUTER versi 2 :
ok, silahkan lanjut membaca.
      if (op === 'create') {
        JSON.parse(params).forEach(d => {
          if (!sheet.headers.length) sheet.headers = Object.keys(d);
          sheet.rows.push(sheet.headers.map(h => d[h] || ''));
        });
        localStorage.setItem('localDB', JSON.stringify(db));
        res.status = 'success';
      } else if (op === 'read') {
        res.data = sheet.rows.map(row => sheet.headers.reduce((o, h, i) => (o[h] = row[i], o), {}));
        res.status = 'success';
      }
      return JSON.stringify(res);
    };

    const Dashboard = {
      template: `<v-container>
        <h2>Dashboard</h2>
        <p>Selamat datang di dashboard.</p>
      </v-container>`
    };

    const ListView = {
      data: () => ({ items: [], username: '' }),
      created() {
        const user = localStorage.getItem('aktif');
        this.username = user;
        const key = `items-${user}`;
        if (!JSON.parse(crud("read", key)).data.length)
          crud("create", key, JSON.stringify([{ name: "Item A", email: "" }]));
        this.items = JSON.parse(crud("read", key)).data;
      },
      methods: {
        async editItem(item) {
          const editDialogId = this.$root.showDialog({
            type: 'form',
            title: 'Edit Data',
            content: `
              <v-text-field label="Nama" v-model="value.name"></v-text-field>
              <v-text-field label="Email" v-model="value.email"></v-text-field>
            `,
            data: { name: item.name, email: item.email },
            actions: {
              ok: {
                title: 'Simpan',
                close: false,
                action: async (data) => {
                  const confirm = await new Promise(resolve => {
                    const confirmDialogId = this.$root.showDialog({
                      type: 'info',
                      title:'Simpan',
                      content: 'Yakin ingin menyimpan perubahan?',width: '300px',
                      actions: {
                        ok: {
                          title: 'Ya',
                          action: () => {
                            this.$root.closeDialog(confirmDialogId);
                            resolve(true);
                          }
                        },
                        cancel: {
                          title: 'Tidak',
                          action: () => {
                            this.$root.closeDialog(confirmDialogId);
                            resolve(false);
                          }
                        }
                      }
                    });
                  });

                  if (confirm) {
                    const loadingDialogId = this.$root.showDialog({
                      type: 'loading',
                      content: '',
                      width: '100px',
                      persistent: true,
                      actions: { ok: { show: false, action: () => {} } }
                    });

                    try {
                      await new Promise(resolve => setTimeout(resolve, 1000));
                      const key = `items-${this.username}`;
                      const all = JSON.parse(crud("read", key)).data;
                      const index = all.findIndex(x => x.name === item.name);
                      if (index !== -1) {
                        all[index] = data;
                        localStorage.setItem('localDB', JSON.stringify({
                          ...JSON.parse(localStorage.getItem('localDB')),
                          [key]: {
                            headers: ['name', 'email'],
                            rows: all.map(d => [d.name, d.email])
                          }
                        }));
                        this.items = all;
                        this.$root.showSnackbar("Data berhasil disimpan!", "success");
                      }
                    } finally {
                      this.$root.closeDialog(loadingDialogId);
                      this.$root.closeDialog(editDialogId);
                    }
                  }
                }
              },
              cancel: {
                title: 'Batal',
                action: () => {
                  this.$root.showSnackbar("Edit dibatalkan", "info");
                }
              }
            }
          });
        }
      },
      template: `<v-container>
        <v-simple-table dense>
          <thead><tr><th>Nama</th></tr></thead>
          <tbody>
            <tr v-for="(item, i) in items" :key="i" @click="editItem(item)">
              <td>{{ item.name }}</td>
            </tr>
          </tbody>
        </v-simple-table>
      </v-container>`
    };

    const Login = {
      data: () => ({ isRegister: false, user: { username: '', password: '' } }),
      methods: {
        toggleMode() { this.isRegister = !this.isRegister },
        submit() {
          const { username, password } = this.user;
          if (!username || !password) {
            this.$root.showSnackbar('Username dan password harus diisi','warning');
            return;
          }

          const users = JSON.parse(crud("read", "users")).data;
          if (this.isRegister) {
            if (users.find(u => u.username === username)) {
              this.$root.showSnackbar('Username sudah terdaftar!','warning');
              return;
            }
            crud("create", "users", JSON.stringify([this.user]));
            this.$root.showSnackbar('Registrasi berhasil', 'success');
            this.toggleMode();
          } else {
            const match = users.find(u => u.username === username && u.password === password);
            if (match) {
              localStorage.setItem('aktif', username);
              this.$root.showSnackbar('Login berhasil', 'success');
              this.$router.push("/app/dashboard");
            } else {
              this.$root.showSnackbar('Login Gagal, username/password salah!', 'error');
            }
          }
        }
      },
      template: `<v-container fill-height>
        <v-row align="center" justify="center">
          <v-col cols="12" md="10">
            <v-card class="mx-auto" :style="{ maxWidth: '290px' }">
              <v-toolbar dark  color="primary">
                <v-toolbar-title>{{ isRegister ? 'Register' : 'Login' }}</v-toolbar-title>
              </v-toolbar>  
              <v-card-text>
                <v-text-field v-model="user.username" label="Username" />
                <v-text-field v-model="user.password" label="Password" type="password" />
              </v-card-text>
              <v-card-actions>
                <v-btn color="primary" @click="submit">{{ isRegister ? 'Register' : 'Login' }}</v-btn>
                <v-spacer></v-spacer>
                <v-btn text @click="toggleMode">
                  {{ isRegister ? 'Sudah punya akun?' : 'Belum punya akun?' }}
                </v-btn>
              </v-card-actions>
            </v-card>
          </v-col>
        </v-row>
      </v-container>`
    };

    const appChildRoutes = [
      { path: 'dashboard', title: 'Dashboard', icon: 'mdi-view-dashboard', component: Dashboard },
      { path: 'list', title: 'List View', icon: 'mdi-format-list-bulleted', component: ListView }
    ];

    const AppLayout = {
      data: () => ({ drawer: false, links: appChildRoutes }),
      computed: {
        currentTitle() {
          return (this.links.find(r => '/app/' + r.path === this.$route.path) || {}).title || "My App";
        }
      },
      methods: {
        go(path) {
          const full = '/app/' + path;
          if (this.$route.path !== full) this.$router.push(full);
          this.drawer = false;
        },
        logout() {
          localStorage.removeItem('aktif');
          this.$root.showSnackbar("Berhasil logout", "info");
          this.$router.push('/');
        }
      },

      template: `<v-app>
        <v-navigation-drawer app v-model="drawer">
          <v-list dense>
            <v-list-item v-for="r in links" :key="r.path" link @click="go(r.path)">
              <v-list-item-icon><v-icon>{{ r.icon }}</v-icon></v-list-item-icon>
              <v-list-item-title>{{ r.title }}</v-list-item-title>
            </v-list-item>
            <v-divider></v-divider>
            <v-list-item link @click="logout">
              <v-list-item-icon><v-icon>mdi-logout</v-icon></v-list-item-icon>
              <v-list-item-title>Logout</v-list-item-title>
            </v-list-item>
          </v-list>

        </v-navigation-drawer>
        <v-app-bar app color="primary" dark>
          <v-app-bar-nav-icon @click="drawer = !drawer" />
          <v-toolbar-title>{{ currentTitle }}</v-toolbar-title>
        </v-app-bar>
        <v-main><router-view /></v-main>
      </v-app>`
    };

    const router = new VueRouter({
      routes: [
        { path: '/', component: Login },
        { path: '/app', component: AppLayout, children: appChildRoutes }
      ]
    });

    router.beforeEach((to, from, next) => {
      const loggedIn = localStorage.getItem('aktif');
      if (to.path.startsWith('/app') && !loggedIn) {
        next('/');
      } else {
        next();
      }
    });

    new Vue({
      el: '#app',
      vuetify: new Vuetify(),
      router,
      data: () => ({
        dialogs: [],
        snackbar: {
          show: false,
          text: '',
          color: 'success',
          timeout: 3000
        },
        defaultDialog: {
          type: '',
          title: '',
          template: null,
          content: null,
          data: null,
          width: '500px',
          persistent: true,
          actions: {
            ok: { title: 'OK', action: () => {} },
            cancel: null
          }
        }
      }),
      created() {
        localStorage.removeItem('aktif');
      },
      methods: {
        showDialog(typeOrOptions, title = '', content = '') {
          let options = {};
          if (typeof typeOrOptions === 'string') {
            options = { type: typeOrOptions, title, content };
          } else {
            options = typeOrOptions;
          }

          const dialog = {
            ...this.defaultDialog,
            ...options,
            id: 'dialog-' + Date.now() + Math.random().toString(36).substr(2, 5),
            show: true
          };

          if (options.type === 'form') {
            const componentId = 'dynamic-form-' + dialog.id;
            Vue.component(componentId, {
              props: ['value'],
              template: `<div>${options.content}</div>`
            });
            dialog.template = componentId;
          } else if (options.type === 'loading') {
            const componentId = 'loading-dialog-' + dialog.id;
            Vue.component(componentId, {
              template: `
                <div style="display: flex; justify-content: center; align-items: center; height: 100px;">
                  <v-progress-circular indeterminate color="primary" size="40" />
                </div>
              `
            });
            dialog.template = componentId;
          }

          this.dialogs.push(dialog);
          return dialog.id;
        },
        closeDialog(id) {
          this.dialogs = this.dialogs.filter(dialog => dialog.id !== id);
        },
        showSnackbar(text, color = 'success') {
          this.snackbar.text = text;
          this.snackbar.color = color;
          this.snackbar.show = true;
        }
      }
    });
  </script>
</body>

</html>

Terimakasih atas kunjungan Anda dan Karena telah sudi membaca artikel yang berjudul TEMPLATE VUETIFY, VUE 2 DAN VUE-ROUTER versi 2.Tak Lengkap Rasanya Jika Kunjungan Anda di Blog ini Tanpa Meninggalkan Komentar, untuk Itu Silahkan Berikan Kritik dan saran Pada Kotak Komentar di bawah. Anda boleh menyebarluaskan atau mengcopy artikel TEMPLATE VUETIFY, VUE 2 DAN VUE-ROUTER versi 2 ini jika memang bermanfaat bagi anda, namun jangan lupa untuk mencantumkan link sumbernya. Terima Kasih, Happy Blogging :)

Related Posts


EmoticonEmoticon

:)
:(
hihi
:-)
:D
=D
:-d
;(
;-(
@-)
:P
:o
:>)
(o)
:p
(p)
:-s
(m)
8-)
:-t
:-b
b-(
:-#
=p~
x-)
(k)