1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 14631字,需瀏覽 30分鐘

         ·

        2021-05-01 10:00

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

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

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

        root

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

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

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

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

        側(cè)邊欄

        如何完成這個(gè)兩欄布局

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

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

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

        如果菜單是二級(jí)菜單,三級(jí)菜單,需要怎么處理

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

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

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

        頂部欄

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

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

        面包屑

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

        需要研究 Breadcrumb 面包屑

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

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

        AppMain.vue

        <template>
          <section class="app-main">
            <!-- 內(nèi)部應(yīng)該顯示子路由頁(yè)面信息 -->
            <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 對(duì)應(yīng)的 router-view

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

        <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>

        請(qǐng)求封裝

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

        import { Message, Msgbox } from 'element3';

        import store from 'store/index.js';

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

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

        // 請(qǐng)求攔截
        service.interceptors.request.use(
            (config) => {
                // 指定請(qǐng)求令牌
                // if (store.getters.token) {
                // // 自定義令牌的字段名為X-Token,根據(jù)咱們后臺(tái)再做修改
                // config.headers["X-Token"] = store.getters.token;
                // }
                config.headers["X-Token"] = "my token";
                return config;
            },
            (error) => {
                // 請(qǐng)求錯(cuò)誤的統(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ù)情況修改
             * 同時(shí)也可以通過HTTP狀態(tài)碼判斷請(qǐng)求結(jié)果
             */

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

                // 如果狀態(tài)碼不是20000則認(rèn)為有錯(cuò)誤
                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("您已登出, 請(qǐng)重新登錄""確認(rèn)", {
                            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;

        多語(yǔ)言

        多語(yǔ)言會(huì)用到 vue-i18n 這樣的插件

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

        有一個(gè) vite 多語(yǔ)言插件

        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/**')
            })
          ]
        })

        模板這樣使用多語(yǔ)言

        <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": "言語(yǔ)",
            "hello": "こんにちは、世界!"
          }
        }
        </i18n>

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

        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
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            被女同学用玉足踩玩jiji | 亚洲久久天堂 | 国产成人三级一区二区在线观看一 | 人人九九精品 | 欧美黑大粗 | 天堂中文网 | 伊人色色综合 | 男生操女生的逼 | 肉丝祙做爰少妇黑裤袜 | 少妇高潮hd |