برنامه نویسی

با vue-element-admin به سرعت SPAهای پیچیده بسازید

نوشته امانوئل جان✏️

معماری اپلیکیشن تک صفحه ای (SPA) یکی از بزرگترین گرایش ها در توسعه وب برای مدتی در حال حاضر بوده است. به عنوان توسعه دهندگان، فریم ورک های جاوا اسکریپت مانند Angular، React، Svelte و Vue.js به شما امکان می دهند با استفاده از یک رابط خط فرمان (CLI) یک SPA ایجاد کنید. برخی از مواردی که ممکن است به SPA نیاز داشته باشید عبارتند از:

  • هنگامی که در حال ساخت یک برنامه تجارت الکترونیک مانند Jumia یا یک سرویس پخش فیلم مانند Netflix هستید
  • معمولاً وقتی در حال ساخت داشبورد مدیریت هستید

داشبوردهای مدیریت پیچیده هستند زیرا درخواست های شبکه، نمودارها، ارتباطات بلادرنگ، جداول داده، اجزای رابط کاربری و موارد دیگر را مدیریت می کنند. به دلیل پیچیدگی، تیم های توسعه دهنده تمایل به خرید قالب های از پیش ساخته شده و سفارشی سازی آنها دارند.

اما اگر یک توسعه‌دهنده frontend هستید که عمدتاً با Vue.js کار می‌کنید، vue-element-admin یکی از بهترین قالب‌های مدیریت رایگان و منبع باز Vue است. در این مقاله، نحوه استفاده از vue-element-admin را یاد می گیریم و برخی از ویژگی های آن را بررسی می کنیم.

پرش به جلو:

چیست vue-element-admin?

vue-element-admin سلف vue-admin-template است. این یک راه حل آماده تولید و فرانت اند برای رابط های مدیریت است که از آن استفاده می کند element-ui ابزار. این الگو دارای بسیاری از ویژگی های پیچیده است که می توانید برای داشبوردهای سطح سازمانی استفاده کنید.

بیایید برخی از ویژگی های اصلی این پروژه را بررسی کنیم.

احراز هویت

داشبورد مدیریت باید قابلیت ورود به سیستم را برای تأیید یک کاربر داشته باشد. در vue-admin-template، عمل ورود اطلاعات کاربر (نام کاربری و رمز عبور) را به سرور ارسال می‌کند و سرور رمزی را برمی‌گرداند که در یک کوکی ذخیره می‌شود:

login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login({ username: username.trim(), password: password }).then(response => {
        const { data } = response
        commit('SET_TOKEN', data.token)
        setToken(data.token)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مجوز

یکی دیگر از ویژگی های مهم داشبورد مدیریت، تعیین نقش ها و مجوزهای کاربر است. در این الگو، نقش کاربر از توکن برگشتی از سرور به دست می آید. نقش کاربر تعیین می کند که به کدام مسیر می توانند دسترسی داشته باشند. این به صورت پویا محاسبه می شود:

const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done() 
    } else {
      const hasRoles = store.getters.roles && store.getters.roles.length > 0
      if (hasRoles) {
        next()
      } else {
        try {
          const { roles } = await store.dispatch('user/getInfo')
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)

          router.addRoutes(accessRoutes)

          next({ ...to, replace: true })
        } catch (error) {
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    /* has no token*/
    if (whiteList.indexOf(to.path) !== -1) {      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قطعه کد بالا نحوه اجرای مجوزهای سطح صفحه در این الگو را نشان می دهد. کاربران با مجوزهای مختلف، نوارهای کناری متفاوتی را مشاهده می کنند و فقط می توانند تعداد محدودی از صفحات را وارد کنند. اگر جدول مسیریابی شما به صورت پویا در backend تولید می شود، ممکن است ترجیح دهید این را سفارشی کنید.

اجزای رابط کاربری

همانطور که قبلاً گفته شد، vue-element-admin از این استفاده می کند element-ui جعبه ابزار UI. داشبوردهای مدیریت به اجزای قابل استفاده مجدد زیادی نیاز دارند و این الگو بیش از 25 مؤلفه رابط کاربری از جمله DragSelect، ErrorDialog، MarkdownEditor، Pagination، Charts، و بیشتر.

Layout جزء

یکی از اولین مؤلفه‌ها یا نماهایی که هنگام ساخت داشبورد پیاده‌سازی می‌کنید، این است Layout جزء. vue-element-admin شامل:

  • یک نوار کناری
  • یک نوار ناوبری
  • TagsView
  • محتوا

طرح بندی یک صفحه در پیکربندی روتر تعریف شده است:

{
    path: '/401',
    component: () => import('@/views/error-page/401'),
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index'),
        name: 'Dashboard',
        meta: { title: 'Dashboard', icon: 'dashboard', affix: true }
      }
    ]
  },
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این پیکربندی سفارشی کردن نوار کناری را برای مطابقت با نقش کاربر آسان می کند.

درخواست های شبکه

داشبوردهای مدیریت بسیاری از نقاط پایانی API را از باطن مصرف می‌کنند، بنابراین داشتن یک جریان سازمان‌یافته مهم است. vue-element-admin این را به شیوه ای بسیار واضح و مختصر مدیریت می کند، بنابراین بیایید آن را تجزیه کنیم.

اول، ما یک request ابزاری که می پیچد axios. request همه را اداره می کند POST، GETو سایر پارامترهای درخواست؛ سرصفحه های درخواست؛ و پیام های خطا درخواست‌های API ماژول‌هایی هستند src/api; یک درخواست معمولی به این صورت است:

export function getInfo(token) {
  return request({
    url: '/vue-element-admin/user/info',
    method: 'get',
    params: { token }
  })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس ماژول شبکه وارد می شود vue-store اقدامات، مانند این:

import { getInfo } from '@/api/user'

getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then(response => {
        const { data } = response

        if (!data) {
          reject('Verification failed, please Login again.')
        }

        const { roles, name, avatar, introduction } = data

        // roles must be a non-empty array
        if (!roles || roles.length <= 0) {
          reject('getInfo: roles must be a non-null array!')
        }

        commit('SET_ROLES', roles)
        commit('SET_NAME', name)
        commit('SET_AVATAR', avatar)
        commit('SET_INTRODUCTION', introduction)
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  },
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نمودار

داشبوردها نمودارهای زیادی برای فرم ها و داده های مختلف دارند. این یکی دیگر از نیازهای رایج است. این الگو Apache ECharts را توصیه می کند، یک کتابخانه تجسم جاوا اسکریپت قدرتمند، با استفاده آسان و انعطاف پذیر.

من شخصاً این کتابخانه را به کتابخانه‌های نمودارهای دیگر ترجیح می‌دهم، زیرا گالری گسترده‌اش را دارد، که هنگام پیاده‌سازی نمودارها برای مشتریانی که مطمئن نیستند می‌خواهند داده‌ها را نشان دهند، مفید است. ما در این مقاله به طور عمیق به ECcharts نخواهیم پرداخت. شما می توانید مستندات آنها را در اینجا بررسی کنید.

ساختمان با vue-admin-template

تا اینجا، ویژگی های آن را بررسی کرده ایم vue-admin-template; بیایید پیش برویم و نحوه راه‌اندازی سریع داشبورد مدیریت با قالب را نشان دهیم.

پیش نیازها

مطمئن شوید که موارد زیر را نصب کرده اید:

این پروژه همچنین از یک سرور ساختگی استفاده می کند که با آن پیاده سازی شده است mockjs. می توانید پیاده سازی ساختگی را در آن پیدا کنید ~/mock.

پس از شبیه سازی مخزن، اجرا کنید npm install برای نصب وابستگی های پروژه هدایت به src/router و اصلاح کنید index.js با این قطعه کد:

//src/router
/* eslint-disable */
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

import Layout from '@/layout'
export const constantRoutes = [
  {
    path: '/redirect',
    component: Layout,
    hidden: true,
    children: [
      {
        path: '/redirect/:path(.*)',
        component: () => import('@/views/redirect/index')
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },
  {
    path: '/auth-redirect',
    component: () => import('@/views/login/auth-redirect'),
    hidden: true
  },
  {
    path: '/404',
    component: () => import('@/views/error-page/404'),
    hidden: true
  },
  {
    path: '/401',
    component: () => import('@/views/error-page/401'),
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index'),
        name: 'Dashboard',
        meta: { title: 'Dashboard', icon: 'dashboard', affix: true }
      }
    ]
  },
  {
    path: '/posts',
    component: Layout,
    children: [
      {
        path: 'index',
        component: () => import('@/views/posts/index'),
        name: 'Posts',
        meta: { title: 'Posts', icon: 'post', affix: true }
      }
    ]
  },

  {
    path: '/profile',
    component: Layout,
    redirect: '/profile/index',
    hidden: true,
    children: [
      {
        path: 'index',
        component: () => import('@/views/profile/index'),
        name: 'Profile',
        meta: { title: 'Profile', icon: 'user', noCache: true }
      }
    ]
  }
]

/**
 * asyncRoutes
 * the routes that need to be dynamically loaded based on user roles
 */
export const asyncRoutes = [

  // 404 page must be placed at the end !!!
  { path: '*', redirect: '/404', hidden: true }
]

const createRouter = () => new Router({
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نصب روتر Vue

این پروژه از Vue Router 3 استفاده می کند. در اینجا، ما دو نوع مسیر داریم: asyncRoutes و constantRoutes.

مسیرهای ثابت جهانی هستند. هر کاربر به این مسیرها دسترسی دارد، در حالی که مسیرهای غیر همگام به صورت پویا بر اساس نقش کاربر بارگذاری می شوند. یک مسیر معمولی مدیریت به صورت زیر خواهد بود:

{
    path: '/transactions',
    component: Layout,
    redirect: '/transactions/info',
    alwaysShow: true, // will always show the root menu
    name: 'Transactions',
    meta: {
      title: 'Transactions',
      icon: 'lock',
      roles: ['superadmin', 'admin']    },
    children: [
      {
        path: 'info',
        component: () => import('@/views/transactions/info/index'),
        name: 'TransactionInfo',
        meta: {
          title: 'Transactions Info',
          roles: ['superadmin']
        }
      },
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس به src/dashboard/views/index بروید و کد را با این تغییر دهید:

//src/dashboard/views/index
<template>
  <div class="dashboard-editor-container">
    <panel-group @handleSetLineChartData="handleSetLineChartData" />

    <el-row style="background: #fff; padding: 16px 16px 0; margin-bottom: 32px">
      <line-chart :chart-data="lineChartData" />
    </el-row>

    <el-row :gutter="32">
      <el-col :xs="24" :sm="24" :lg="8">
        <div class="chart-wrapper">
          <raddar-chart />
        </div>
      </el-col>
      <el-col :xs="24" :sm="24" :lg="8">
        <div class="chart-wrapper">
          <pie-chart />
        </div>
      </el-col>
      <el-col :xs="24" :sm="24" :lg="8">
        <div class="chart-wrapper">
          <bar-chart />
        </div>
      </el-col>
    </el-row>

    <el-row>
      <el-col
        :xs="{ span: 24 }"
        :sm="{ span: 24 }"
        :md="{ span: 24 }"
        :lg="{ span: 24 }"
        :xl="{ span: 24 }"
        style="padding-right: 8px; margin-bottom: 30px"
      >
        <post-table />
      </el-col>
    </el-row>
  </div>
</template>

<script>
import PanelGroup from "./components/PanelGroup";
import LineChart from "./components/LineChart";
import RaddarChart from "./components/RaddarChart";
import PieChart from "./components/PieChart";
import BarChart from "./components/BarChart";
import PostTable from "./components/PostTable";
const lineChartData = {
  newVisitis: {
    expectedData: [100, 120, 161, 134, 105, 160, 165],
    actualData: [120, 82, 91, 154, 162, 140, 145],
  },
  messages: {
    expectedData: [200, 192, 120, 144, 160, 130, 140],
    actualData: [180, 160, 151, 106, 145, 150, 130],
  },
  purchases: {
    expectedData: [80, 100, 121, 104, 105, 90, 100],
    actualData: [120, 90, 100, 138, 142, 130, 130],
  },
  shoppings: {
    expectedData: [130, 140, 141, 142, 145, 150, 160],
    actualData: [120, 82, 91, 154, 162, 140, 130],
  },
};

export default {
  name: "DashboardAdmin",
  components: {
    PanelGroup,
    LineChart,
    RaddarChart,
    PieChart,
    BarChart,
    PostTable,
  },
  data() {
    return {
      lineChartData: lineChartData.newVisitis,
    };
  },
  methods: {
    handleSetLineChartData(type) {
      this.lineChartData = lineChartData[type];
    },
  },
};
</script>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

افزودن اجزای رابط کاربری

اگر با آن کار کرده اید element-ui قبل از این، این اجزای رابط کاربری بسیار آشنا به نظر می رسند. در بالا، یک کامپوننت view جدید را معرفی کردیم، PostTable. بیایید جلوتر برویم و این را بررسی کنیم.

یک فایل Vue جدید با نام ایجاد کنید PostTable.vue که در src/views/dashboard/admin/components و این خطوط کد را اضافه کنید:

//src/views/dashboard/admin/components/PostTable.vue
<template>
  <el-table
    v-loading="listLoading"
    :data="list"
    border
    fit
    highlight-current-row
    style="width: 100%"
  >
    <el-table-column align="center" label="ID" width="">
      <template slot-scope="{ row }">
        <span>{{ row.id }}</span>
      </template>
    </el-table-column>

    <el-table-column width="" align="center" label="Date">
      <template slot-scope="{ row }">
        <span>{{ row.timestamp | parseTime("{y}-{m}-{d} {h}:{i}") }}</span>
      </template>
    </el-table-column>

    <el-table-column width="" align="center" label="Author">
      <template slot-scope="{ row }">
        <span>{{ row.author }}</span>
      </template>
    </el-table-column>

    <el-table-column width="" label="Importance">
      <template slot-scope="{ row }">
        <svg-icon
          v-for="n in +row.importance"
          :key="n"
          icon-class="star"
          class="meta-item__icon"
        />
      </template>
    </el-table-column>

    <el-table-column class-name="status-col" label="Status" width="">
      <template slot-scope="{ row }">
        <el-tag :type="row.status | statusFilter">
          {{ row.status }}
        </el-tag>
      </template>
    </el-table-column>
  </el-table>
</template>

<script>
import { fetchList } from "@/api/article";

export default {
  name: "PostTable",
  filters: {
    statusFilter(status) {
      const statusMap = {
        published: "success",
        draft: "info",
        deleted: "danger",
      };
      return statusMap[status];
    },
  },
  data() {
    return {
      list: null,
      listLoading: true,
      listQuery: {
        page: 1,
        limit: 5,
      },
    };
  },
  created() {
    this.getList();
  },
  methods: {
    async getList() {
      this.listLoading = true;
      const { data } = await fetchList(this.listQuery);
      const items = data.items;
      this.list = items.map((v) => {
        return v;
      });
      this.listLoading = false;
    },
  },
};
</script>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در نهایت در جدول مسیرمان مسیر را تعریف کردیم @/views/posts/index; بیایید آن را ایجاد کنیم. ایجاد یک post/index.vue در دایرکتوری views و این خطوط کد را اضافه کنید:

//src/post/index.vue
<template>
  <div class="app-container">
    <el-table
      v-loading="listLoading"
      :data="list"
      border
      fit
      highlight-current-row
      style="width: 100%"
    >
      <el-table-column align="center" label="ID" width="80">
        <template slot-scope="{ row }">
          <span>{{ row.id }}</span>
        </template>
      </el-table-column>

      <el-table-column width="180px" align="center" label="Date">
        <template slot-scope="{ row }">
          <span>{{ row.timestamp | parseTime("{y}-{m}-{d} {h}:{i}") }}</span>
        </template>
      </el-table-column>

      <el-table-column width="120px" align="center" label="Author">
        <template slot-scope="{ row }">
          <span>{{ row.author }}</span>
        </template>
      </el-table-column>

      <el-table-column width="100px" label="Importance">
        <template slot-scope="{ row }">
          <svg-icon
            v-for="n in +row.importance"
            :key="n"
            icon-class="star"
            class="meta-item__icon"
          />
        </template>
      </el-table-column>

      <el-table-column class-name="status-col" label="Status" width="110">
        <template slot-scope="{ row }">
          <el-tag :type="row.status | statusFilter">
            {{ row.status }}
          </el-tag>
        </template>
      </el-table-column>

      <el-table-column min-width="300px" label="Title">
        <template slot-scope="{ row }">
          <template v-if="row.edit">
            <el-input v-model="row.title" class="edit-input" size="small" />
            <el-button
              class="cancel-btn"
              size="small"
              icon="el-icon-refresh"
              type="warning"
              @click="cancelEdit(row)"
            >
              cancel
            </el-button>
          </template>
          <span v-else>{{ row.title }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="Actions" width="120">
        <template slot-scope="{ row }">
          <el-button
            v-if="row.edit"
            type="success"
            size="small"
            icon="el-icon-circle-check-outline"
            @click="confirmEdit(row)"
          >
            Ok
          </el-button>
          <el-button
            v-else
            type="primary"
            size="small"
            icon="el-icon-edit"
            @click="row.edit = !row.edit"
          >
            Edit
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import { fetchList } from "@/api/article";

export default {
  name: "Posts",
  filters: {
    statusFilter(status) {
      const statusMap = {
        published: "success",
        draft: "info",
        deleted: "danger",
      };
      return statusMap[status];
    },
  },
  data() {
    return {
      list: null,
      listLoading: true,
      listQuery: {
        page: 1,
        limit: 10,
      },
    };
  },
  created() {
    this.getList();
  },
  methods: {
    async getList() {
      this.listLoading = true;
      const { data } = await fetchList(this.listQuery);
      const items = data.items;
      this.list = items.map((v) => {
        this.$set(v, "edit", false); // https://vuejs.org/v2/guide/reactivity.html
        v.originalTitle = v.title; //  will be used when user click the cancel botton
        return v;
      });
      this.listLoading = false;
    },
    cancelEdit(row) {
      row.title = row.originalTitle;
      row.edit = false;
      this.$message({
        message: "The title has been restored to the original value",
        type: "warning",
      });
    },
    confirmEdit(row) {
      row.edit = false;
      row.originalTitle = row.title;
      this.$message({
        message: "The title has been edited",
        type: "success",
      });
    },
  },
};
</script>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

عالی. اکنون سرور توسعه دهنده خود را با آن اجرا کنید npm run dev: برنامه آزمایشی ما، با npm run dev شروع شد برنامه آزمایشی ما، با شروع npm run dev کلیک وارد شدن و شما باید چیزی شبیه به این را ببینید: نمای داشبورد پس از ورود ما این خانه داشبورد است. کلیک نوشته ها، و شما باید چیزی شبیه به این را ببینید: نمای پست های داشبورد ما این شبیه یک داشبورد انتشار وبلاگ است، درست است؟ vue-element-admin همچنین ویرایشگرهایی مانند TinyMCE و MarkdownEditor را به عنوان کامپوننت ارائه می دهد، بنابراین می توانید آنها را مستقیماً در این داشبورد ادغام کنید.

بیشتر بدانید vue-element-admin از اسناد آن در اینجا.

نتیجه

داشبوردهای مدیریت یکی از وظایف اجتناب ناپذیری است که به شما به عنوان یک توسعه دهنده فرانت اند انجام می شود. vue-element-admin یک قالب کاملا رایگان و منبع باز برای شما فراهم می کند تا بتوانید داشبورد را به راحتی بوت استرپ کنید. گردش کار، فهرست پروژه و پیکربندی مسیریابی به اندازه کافی برای برنامه های تک صفحه ای سطح سازمانی استاندارد هستند.

در این مقاله، ویژگی های اصلی را بررسی کردیم vue-element-admin و به ساخت داشبورد مدیریت انتشارات وبلاگ ادامه داد.


برنامه های Vue خود را دقیقاً همانطور که یک کاربر انجام می دهد تجربه کنید

اشکال زدایی برنامه های Vue.js می تواند دشوار باشد، به خصوص زمانی که ده ها، اگر نه صدها جهش در طول یک جلسه کاربر وجود داشته باشد. اگر علاقه مند به نظارت و ردیابی جهش های Vue برای همه کاربران خود در تولید هستید، LogRocket را امتحان کنید.

ثبت نام LogRocket

LogRocket مانند یک DVR برای برنامه های وب و تلفن همراه است که به معنای واقعی کلمه هر چیزی را که در برنامه های Vue شما اتفاق می افتد از جمله درخواست های شبکه، خطاهای جاوا اسکریپت، مشکلات عملکرد و موارد دیگر ضبط می کند. به جای حدس زدن چرایی مشکلات، می توانید در مورد وضعیتی که برنامه شما در هنگام بروز مشکل در آن قرار داشت، جمع آوری کرده و گزارش دهید.

پلاگین LogRocket Vuex جهش‌های Vuex را در کنسول LogRocket ثبت می‌کند، و زمینه‌ای را در مورد اینکه چه چیزی منجر به خطا شده است، و اینکه برنامه در چه وضعیتی در هنگام بروز مشکل بوده است را به شما می‌دهد.

نحوه اشکال زدایی برنامه های Vue خود را مدرن کنید – نظارت را به صورت رایگان شروع کنید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا