<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          搭建后臺管理系統(tǒng)的思路

          共 14631字,需瀏覽 30分鐘

           ·

          2021-05-01 10:00

          從零開始搭建后臺管理系統(tǒng)

          當(dāng)然,這是一個簡易版本的,你可以在這兩個基礎(chǔ)上加以改造

          搭建后臺管理系統(tǒng)最基礎(chǔ)的是什么呢?個人的體會是整體的基礎(chǔ)框架,這個是指最基礎(chǔ)的框架,比如根 router-view, 側(cè)邊欄以及側(cè)邊欄的router-view,以及頂部欄,等基礎(chǔ)布局的控制。

          root

          首先 App.vue 只有展示 rouer-view, 這個就是 root, 所謂的根。

          <template>
            <router-view></router-view>
          </template>

          ok 有了根之后,我們需要有一個 layout 頁面,這個頁面來承載我們的側(cè)邊欄,頂部欄

          layout 頁面他是兩欄布局的,一欄是我們的側(cè)邊導(dǎo)航欄,

          側(cè)邊欄

          如何完成這個兩欄布局

          • 可以使用 float
          • 可以使用彈性布局 display: flex
          • 也可以使用定位

          側(cè)邊導(dǎo)航欄,可能我們需要來研究 element-ui 的組件 NavMenu 導(dǎo)航菜單

          側(cè)邊導(dǎo)航欄需要我們路由的一些信息,比如路由對應(yīng)的組件,就像 router-link 對應(yīng)的 router-view

          如果菜單是二級菜單,三級菜單,需要怎么處理

          如果需要折疊菜單,需要怎么處理,這里就需要閱讀 折疊菜單組件

          也就是說側(cè)邊菜單來其實就是一個個的 router-link

          然后擴展菜單項是否有 icon 小圖標,是否有標題存在,那么路由就需要設(shè)置 meta 屬性了

          頂部欄

          接下來就是頂部欄,頂部導(dǎo)航欄有什么呢?

          • 面包屑
          • 消息通知
          • 下拉菜單
          • 關(guān)閉展開側(cè)邊欄按鈕

          面包屑

          需要注意什么呢?需要注意是否需要點擊跳轉(zhuǎn)的,定位到那一級菜單的問題

          需要研究 Breadcrumb 面包屑

          關(guān)閉展開側(cè)邊欄按鈕

          需要使用 vuex 來存儲打開與否的這個狀態(tài)值,通過 vuex 來更改狀態(tài)

          AppMain.vue

          <template>
            <section class="app-main">
              <!-- 內(nèi)部應(yīng)該顯示子路由頁面信息 -->
              <router-view v-slot="{ Component }">
                <component :is="Component" />
              </router-view>
            </section>
          </template>

          <script>
          import { defineComponent } from "vue";

          export default defineComponent({
            name"AppMain",
          });
          </script>

          <style lang="scss" scoped>
          .app-main {
            /*50 = navbar  */
            min-heightcalc(100vh - 50px);
            width100%;
            position: relative;
            overflow: hidden;
          }
          </style>

          用來展示側(cè)邊欄的 router-link 對應(yīng)的 router-view

          所以,又回到 layout 頁面整體框架如下:

          <template>
            <div class="app-wrapper">
              <!-- 側(cè)邊欄 -->
              <side-bar class="sidebar-container"></side-bar>
              <!-- 內(nèi)容容器 -->
              <div class="main-container">
                <!-- 頂部導(dǎo)航欄 -->
                <nav-bar />
                <!-- 內(nèi)容區(qū) -->
                <app-main />
              </div>
            </div>
          </template>

          <script setup>
          import AppMain from "layout/components/AppMain.vue";
          import NavBar from "layout/components/NavBar.vue";
          import SideBar from "layout/components/Sidebar/index.vue";
          </script>

          <style lang="scss" scoped>
          @import "styles/mixin.scss";
          .app-wrapper {
            @include clearfix;
            position: relative;
            height: 100%;
            width: 100%;
          }
          </style>

          請求封裝

          • token 處理
          • 響應(yīng)處理
          • 請求處理
          import axios from 'axios';

          import { Message, Msgbox } from 'element3';

          import store from 'store/index.js';

          // 創(chuàng)建 axios 實例

          const service = axios.create({
              // 在請求地址前面加上 baseURL
              baseURLimport.meta.env.VITE_BASE_API,
              // 當(dāng)前發(fā)送跨域請求時攜帶 cookie
              withCredentialstrue,
              timeout5000
          });

          // 請求攔截
          service.interceptors.request.use(
              (config) => {
                  // 指定請求令牌
                  // if (store.getters.token) {
                  // // 自定義令牌的字段名為X-Token,根據(jù)咱們后臺再做修改
                  // config.headers["X-Token"] = store.getters.token;
                  // }
                  config.headers["X-Token"] = "my token";
                  return config;
              },
              (error) => {
                  // 請求錯誤的統(tǒng)一處理
                  console.log(error); // for debug
                  return Promise.reject(error);
              }
          );

          // 響應(yīng)攔截器
          service.interceptors.response.use(
              /**
               * If you want to get http information such as headers or status
               * Please return  response => response
               */


              /**
               * 通過判斷狀態(tài)碼統(tǒng)一處理響應(yīng),根據(jù)情況修改
               * 同時也可以通過HTTP狀態(tài)碼判斷請求結(jié)果
               */

              (response) => {
                  const res = response.data;

                  // 如果狀態(tài)碼不是20000則認為有錯誤
                  if (res.code !== 20000) {
                      Message.error({
                          message: res.message || "Error",
                          duration5 * 1000,
                      });

                      // 50008: 非法令牌; 50012: 其他客戶端已登入; 50014: 令牌過期;
                      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
                          // 重新登錄
                          Msgbox.confirm("您已登出, 請重新登錄""確認", {
                              confirmButtonText"重新登錄",
                              cancelButtonText"取消",
                              type"warning",
                          }).then(() => {
                              store.dispatch("user/resetToken").then(() => {
                                  location.reload();
                              });
                          });
                      }
                      return Promise.reject(new Error(res.message || "Error"));
                  } else {
                      return res;
                  }
              },
              (error) => {
                  console.log("err" + error); // for debug
                  Message({
                      message: error.message,
                      type"error",
                      duration5 * 1000,
                  });
                  return Promise.reject(error);
              }
          );

          export default service;

          多語言

          多語言會用到 vue-i18n 這樣的插件

          就需要研究官網(wǎng)文檔了 vue-i18n

          有一個 vite 多語言插件

          intlify/vite-plugin-vue-i18n

          vite.config.js 配置

          import path from 'path'
          import { defineConfig } from 'vite'
          import vue from '@vitejs/plugin-vue'
          import vueI18n from '@intlify/vite-plugin-vue-i18n'

          export default defineConfig({
            plugins: [
              vue(), // you need to install `@vitejs/plugin-vue`
              vueI18n({
                // if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false`
                // compositionOnly: false,

                // you need to set i18n resource including paths !
                include: path.resolve(__dirname, './path/to/src/locales/**')
              })
            ]
          })

          模板這樣使用多語言

          <template>
            <form>
              <label>{{ t('language') }}</label>
              <select v-model="locale">
                <option value="en">en</option>
                <option value="ja">ja</option>
              </select>
            </form>
            <p>{{ t('hello') }}</p>
          </template>

          <script>
          import { useI18n } from 'vue-i18n'

          export default {
            name'App',
            setup() {
              const { locale, t } = useI18n({
                inheritLocaletrue
              })

              return { locale, t }
            }
          }
          </script>

          <i18n>
          {
            "en": {
              "language": "Language",
              "hello": "hello, world!"
            },
            "ja": {
              "language": "言語",
              "hello": "こんにちは、世界!"
            }
          }
          </i18n>

          當(dāng)然,可以在 main.js 引入多語言

          import { createApp } from 'vue'
          import { createI18n } from 'vue-i18n'
          /*
           * The i18n resources in the path specified in the plugin `include` option can be read
           * as vue-i18n optimized locale messages using the import syntax
           */

          import en from './src/locales/en.json'
          import ja from './src/locales/ja.yaml'
          import fr from './src/locales/fr.json5'

          const i18n = createI18n({
            locale'en',
            messages: {
              en,
              ja,
              fr
            }
          })

          const app = createApp()
          app.use(i18n).mount('#app)

          element3 組件

          // 完整引入
          import element3 from "element3";
          import "element3/lib/theme-chalk/index.css";

          export default function (app{
              // 完整引入
              app.use(element3);
          };

          我們要如何組建自己的樣式目錄

          • var.scss 用于提取顏色值,字體大小值,字體權(quán)重值等
          • mixin.scss 寫一些公用的樣式

          目錄的重定向問題

          import { defineConfig } from 'vite';
          import vue from '@vitejs/plugin-vue';
          import vueJsx from '@vitejs/plugin-vue-jsx';
          import { viteMockServe } from 'vite-plugin-mock';
          import path from 'path';
          import vueI18n from '@intlify/vite-plugin-vue-i18n'
          // https://vitejs.dev/config/
          export default defineConfig({
            plugins: [
              vue(),
              vueJsx(),
              viteMockServe({ supportTsfalse }),
              vueI18n({
                // if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false`
                // compositionOnly: false,

                // you need to set i18n resource including paths !
                include: path.resolve(__dirname, './src/locales/**')
              })
            ],
            resolve: {
              alias: {
                "@": path.resolve(__dirname, "src"),
                "comps": path.resolve(__dirname, "src/components"),
                "api": path.resolve(__dirname, "src/api"),
                "views": path.resolve(__dirname, "src/views"),
                "styles": path.resolve(__dirname, "src/styles"),
                "locales": path.resolve(__dirname, "src/locales"),
                "layout": path.resolve(__dirname, "src/layout"),
                "utils": path.resolve(__dirname, "src/utils"),
                "dirs": path.resolve(__dirname, "src/dirs"),
                "plugins": path.resolve(__dirname, "src/plugins"),
                "config": path.resolve(__dirname, "src/config"),
                "router": path.resolve(__dirname, "src/router"),
                "store": path.resolve(__dirname, "src/store"),
                "model": path.resolve(__dirname, "src/model")
              }
            }
          });


          瀏覽 76
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文资源在线天堂的功能介绍 | 射死你天天日 | 国产久久这里只有精品视频 | 在线免费看不卡黄色视频 | av手机在线观看 AV天堂免费观看 av天堂手机在线 |