erp前端項目總結

太極ERP前端開發總結

1、項目目錄(vue-cli2)

├── build
├── config
├── dist //打包後生成的靜態頁面文件
├── src  
│   ├── assets //全局靜態圖片 
│   ├── components //全部組件(項目核心部分)
│   │   ├── element //業務代碼
│   │   ├── layout //佈局代碼(導航,頭,和主體部分)
│   │   ├── login //登陸代碼
│   │   ├── public//公用功能組件 
│   ├── filters //全局過濾器
│   ├── my-theme //全局樣式表
│   ├── router //路由
│   ├── sever
│   │   ├── api.js //全局API
│   │   ├── method // API方法
│   │   ├── https.js // axios封裝,路由請求響應攔截等
│   ├── store //狀態管理
│   ├── utils //公用方法
│   │   ├── public_function //公用api增刪改查審覈做廢等方法
│   │   ├── fetch.js //簽名生成方法
│   │   ├── rules.js //封裝驗證方法
│   ├── App.vue //父組件
│   ├── main.js //入口文件
└── static //靜態資源

項目目錄是採用 vue-cli 自動生成的,如需添加依賴,"$ npm install 依賴名" 便可(若是僅用於開發階段,則使用 $ npm install --save-dev )。html

注:-save 和 save-dev 能夠省掉你手動修改 package.json 文件的步驟。前端

npm install module-name -save 自動把模塊和版本號添加到 dependencies 部分。
npm install module-name -save-dev 自動把模塊和版本號添加到 devdependencies 部分。vue

​現已將 vue-cli2 升級到 vue-cli3 ,大大加快了項目編譯及打包速度, 但 src 下的目錄結構與業務代碼幾乎沒有變更。

ios

2、開發實踐

(一) 權限

  1. 權限需求定位到每一個頁面的每一個按鈕(主要是列表頁): 添加、修改、查看、審覈、做廢、反審覈、打印等,如無權限直接屏蔽該按鈕。
  2. 當路由改變時,咱們須要動態的監聽該頁面是否具備權限,可在 router.afterEach 鉤子函數中設置( router/index.js )
  3. 將獲取到的權限存放到本地存儲裏,相應在頁面裏獲取本地存取的權限值而後控制頁面上按鈕的顯示狀態。
  • 頁面使用方法git

    /**
    1. 在 created 或者 mounted 鉤子函數初始化時獲取
    2. 在template模板中給按鈕綁定權限
    **/
    data(){
        return {
            authority: {},
        }
    },
    created() {
        this.authority = JSON.parse(localStorage.getItem("authority")); 
    }
    <!-- template -->
    <el-button icon="el-icon-circle-plus-outline" @click="salesAddF" v-if="authority.b_add == 1">新增</el-button>
    <el-button icon="el-icon-delete" @click="deleteF" v-if="authority.b_del == 1">刪除</el-button>
    // router/index.js
    import Vue from "vue";
    import axios from "@/sever/https";
    import Router from "vue-router";  
    Vue.use(Router);
    const router = new Router({ 
        base: '/tomcat/',
        routes: [{
                path: "/login",
                name: "登陸頁面",
                component: login,
            },
            ...],
        ]
    });
    router.beforeEach((to, from, next) => { 
        var objArr = str.split("/", 3); 
        if (to.matched.some(r => r.meta.requireAuth)) {
            if (store.state.system_app.token) {  
                //登陸狀態當監測到路由變化,則去請求進入頁面的權限接口
                    axios({
                        method: "get",
                        url: "xxxRuleApiUrl",
                        params: { code: objArr[2] }
                    }).then(res => {
                        if (res.data.code == 200) { 
                            if (res.data.data.is_role == 'Y') {
                               //若是該頁面設有權限則將權限詳情先存於本地
                               window.localStorage.removeItem("authority")
                                let authority = JSON.stringify(res.data.data.role_list); 
                                localStorage.authority = authority;                                next();
                            } else {
                                // 未有頁面訪問權限
                                next({
                                    path: "/isRole",
                                    query: {
                                        redirect: to.fullPath
                                    }
                                });
                            } 
                            next();
                        }
                    });
    
            } else {
                next({
                    path: "/login",
                    query: {
                        redirect: to.fullPath
                    }
                });
            }
        } else {
            next();
        }
    }); 
    export default router;

(二) 各組件間傳遞數據

  1. 點擊 vuex官網
  2. 項目中引入了vuex,使組件間傳值更加方便,避免使用中間件 bus 等
  3. 若是是簡單的子父傳參,建議使用 $parent / $children / $emit / $attrs / $refs.name 來減小代碼量,並更易於讀寫,避免污染全局(靈活運用)
  4. 項目中大量運用到 vuex 的主要是解決列表頁、新增頁、修改頁、查看頁的來回跳轉

例,使用vuex存儲變量,切換新增修改查看頁面github

  • 頁面調用方法ajax

    // 使用方法:調用 ACTION 的salesorderIndexACT方法,改變modulename爲Index,
    // 切換組件到首頁並更新視圖
    // salesorderAdd.vue
    import { mapActions, mapGetters } from "vuex";
    methods:{
        ...mapActions([
          "salesorderAdd",
          "salesorderIndexACT"
        ]),
        goBack() {
        // 回到模塊首頁,使用 ACTION salesorderIndexACT
          this.salesorderIndexACT({
            moduleName: "Index"
          });
        }
    }

  • 初始化頁面算法

    <!-- template 中默認綁定列表頁 -->
    <template>
      <div>
        <component v-bind:is="commont"></component>
      </div>
    </template>
  • 頁面中間件vue-router

    //組件中引入各模塊
    <script>
    import Index from "@/components/element/sales/salesordermanage/salesorder/salesorderIndex";
    import salesorderAdd from "@/components/element/sales/salesordermanage/salesorder/salesorderAdd";
    import salesorderSee from "@/components/element/sales/salesordermanage/salesorder/salesorderSee"; 
    import { mapGetters } from "vuex";
    export default {
      data() {
        return {
          commont: "Index"
        };
      },
      components: {
        Index, //初始化首頁
        salesorderAdd, //新增頁  
      },
      computed: {
        ...mapGetters([
          "salesorderIndexGet",
          "salesorderAddGet",
          "salesorderSeeGet"
        ])
      },
      watch: {
        salesorderIndexGet() {
          this.commont = this.salesorderIndexGet.moduleName;
        },
        salesorderAddGet() {
          this.commont = this.salesorderAddGet.moduleName;
        },
        salesorderSeeGet() {
          this.commont = this.salesorderSeeGet.moduleName;
        }
      }
    };
    </script>
  • 定義狀態的方法

    //state.js
    export default {
        // 銷售訂單
        salesorderIndex: {},
        salesorderAdd: {},
        salesorderSee: {},
        ...
    };
    /** 
    * 最好不要直接使用,state主要用於存取變量,例如:
    ...mapState({
        aa: state => state.system_app.user,
      })
    **/
    
    // ============= 分割線 ============= //
    
    //getter.js
    export default {
        // 銷售訂單
        salesorderIndexGet(state) {
            return state.salesorderIndex;
        },
        salesorderAddGet(state) {
            return state.salesorderAdd;
        },
        salesorderSeeGet(state) {
            return state.salesorderSee;
        }
    };
    
    /**
    * 用法:寫到watch或者computed中,ShowCon相似於data中的一個對象,getShowCon是getters中的一個方法,例如:
    ...mapGetters({
        ShowCon:"getShowCon"
    })
    **/
    
    // ============= 分割線 ============= //
    
    //mutations.js
    export default {
        // 銷售訂單
        salesorderIndex(state, info) {
            state.salesorderIndex = info
        },
        salesorderSee(state, info) {
            state.salesorderSee = info
        },
        salesorderAdd(state, info) {
            state.salesorderAdd = info
        }
    }
    /**
    * 獲取方法,clickF是本地調用的方法,@click="clickF(...arg)",salesorderIndex是寫到mutations裏邊的方法,例如:
    ...mapMutations({   
       clickF:"salesorderIndex"
    })
    **/
    // ============= 分割線 ============= //
    
    //actions.js
    export default { 
        salesorderIndexACT(context, data) {
            context.commit("salesorderIndex", data);
        },
        salesorderSee(context, data) {
            context.commit("salesorderSee", data);
        },
        salesorderAdd(context, data) {
            context.commit("salesorderAdd", data);
        } 
    };
    /** 
    * 執行或者多個mutations的方法
    **/

    (三) 過濾器

  1. 項目中大量使用到了對數值等的格式化,如處理價格的千分位分隔符過濾器,對價格保留兩位小數的過濾器,處理正負數的過濾器等。使用方法:
  • 頁面上使用

    <template slot-scope="scope">
      {{scope.row.price | priceFormat}}
    </template>
  • 引入寫好的過濾器模塊,遍歷全部過濾器並掛載到Vue實例上,組件裏便可以直接使用

    // main.js
    import filters from '@/filters/index.js'
    Object.keys(filters).forEach(key => {
        Vue.filter(key, filters[key])
    });
  • 自定義過濾器

    //filter.js 
    export default {
        //價格保留兩位小數並使用千分位分隔符
        priceFormat(s, n) { //能夠小於0
            if (!s && s !== 0) {
                return s
            }
            var numberS = Number(s)
            if (numberS) {
                n = n > 0 && n <= 20 ? n : 2;
                numberS = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(n) + "";
                var l = numberS.split(".")[0].split("").reverse(),
                    r = numberS.split(".")[1];
                var t = "";
                for (var i = 0; i < l.length; i++) {
                    t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? "," : "");
    
                }
                if (s < 0 && Math.abs(s) < 1000) {
                    return s
                } else {
                    return t.split("").reverse().join("") + "." + r;
                }
            } else {
                return s
            } 
        },
        ...
    };

(四) 路由

  1. 官網:Vue Router
  2. 根據接口 system/menu/getLeftMenuList 命名文件
  3. 模塊化路由文件,在 router/index.js 中導入各模塊路由的路徑
  4. 避免首次加載頁面的時候加載整個模塊代碼(致使幾秒白屏),加入了路由懶加載

    //router/index.js
    {
        path: "xx",
        name: "xx",
        component: resolve =>
        require([
        "@/components/element/xx/xx/xx.vue"
        ], resolve),//路由懶加載
        meta: {
        _meauname: '自定義標題'
        }
    }

    (五) axios

  5. 可加入默認自定義參數

    // sever/https.js
    axios.defaults.timeout = 10000; //可等待時長10s
    axios.defaults.baseURL = "http://60.205.214.105"; //接口baseURL
    axios.defaults.headers.post["Content-Type"] =
        "application/x-www-form-urlencoded; charset=UTF-8";//請求默認頭信息
  6. 可在axios攔截中自定義功能。
  7. 在請求攔截 axios.interceptors.request.use 中加入 loading 效果、 post 和 get 請求對參數進行格式化。
  8. 在響應攔截 axios.interceptors.response.use 裏若是接口報錯,根據狀態碼給出相應提示語和項目公用方法規範

    import axios from "axios"; 
    import {
        Message,
        Loading
    } from "element-ui";
    
    // 請求攔截器
    axios.interceptors.request.use(
        config => { 
            loading = Loading.service({
                fullscreen: true,
                lock: true,
                text: '正在加載,請稍等……'
            });  
    
            if (config.method == "post") {
                removePending(config); //在一個ajax發送前執行一下取消操做
                config.cancelToken = new cancelToken((c) => {
                    // 這裏的ajax標識我是用請求地址&請求方式拼接的字符串,固然你能夠選擇其餘的一些方式
                    pending.push({
                        u: config.url + '&' + config.method,
                        f: c
                    });
                });
                let postData = config.data;
                config.data.sign = serverMoudle(postData);
            } else if (config.method == "get") {
                let getData = config.params;
                config.params = {
                    sign: serverMoudle(getData),
                    ...config.params
                };
            }
            if (store.state.system_app.token) {
                config.headers.Token = `${store.state.system_app.token}`;
            }
            // 每次請求添加sign
            return config;
        },
        err => {
            if (loading) { loading.close(); }
            Message({
                showClose: true,
                message: err.msg,
                type: 'error'
            })
            return Promise.reject(err);
        }
    );
    
    // 響應攔截器
    axios.interceptors.response.use(
        response => {
            if (loading) { loading.close(); }
            const data = response.data;
            if (response.config.method == 'post') {
                removePending(response.config);
            }
            if (data.code == 401) {
                store.dispatch('LOGOUT')
                router.replace({
                    path: "/login"
                });
            } else if (data.code == 400) {
                Message.error(data.msg)
    
            } else if (data.code == 503) {
                Message.error(data.msg)
                store.dispatch('LOGOUT')
                router.replace({
                    path: "/login"
                });
            }
            return response;
        },
        error => {
            Message({
                message: "網絡錯誤",
                type: "error",
                duration: 5 * 1000
            });
            return Promise.reject(error);
        }
    );
    export default axios;

    (六) 請求列表數據(帶分頁)

    查詢、重置、切換頁碼、切換列表長度四個功能封裝成爲一個公用的方法。這四者有着密切的關係,又有各自的不一樣點(默認:列表30項,顯示第一頁),它們的傳遞值分別爲:

功能 params(搜索參數) page(頁碼) per_page(分頁長度)
查詢 已填搜索項 1 已選的分頁長度
重置 搜索項置空(根據需求) 1 30
切換頁碼 已填搜索項 已選的頁碼 已選的分頁長度
切換分頁長度 已填搜索項 已選的頁碼 已選的分頁長度
  • 使用方法

    import { getData } from "@/utils/public_function/commonUtils";//初始化getData方法參數
    this.$set(this, "params", { defaultParams: "xx" });
    this.$set(this, "searchParams", {
          params: this.params, //默認參數
          TableData: this.TableData, //列表數據
          API: "getList", //列表接口名
          vm: this //Vue
    });
    getData(this.searchParams); //獲取列表
    
    //在實現相應功能時,能夠直接使用組合方法改變this.searParams.params的值,便可。
    onSearch(){
        Object.assign(this.searchParams.params, {
            per_page: val,//分頁長度
            page: 1//第一頁
        });
        getData(this.searchParams); //獲取列表
    }
  • getData 方法

    //commonUtils.js
    export function getData(arg, type) {
        /* 
        * @Date: 2018-07-06 10:23:10 
        * 初始化  搜索,重置,切換頁碼,切換頁碼長度 都須要調取該方法從新初始化列表 
        *  arg格式:
        * { 
        *   params:{},//請求參數
            TableData: this.TableData, //列表數據
            API: "getxxx", //接口名
            vm: this //傳遞的this
        }
        */
    
        if (arg["params"]) {
            arg["vm"]["goPage"] = "page" in arg["params"] ? arg["params"]["page"] : 1; //改變當前頁
            arg["vm"]["pageSize"] =
                "per_page" in arg["params"] ? arg["params"]["per_page"] : 30; //改變當前頁size
        }
    
        arg.vm.loading = true;
        axios({
            method: "get",
            url: api[arg.API],
            params: arg.params
        }).then(res => {
            if ([200, 304].includes(res.data.code)) {
                arg.TableData.tableData = res.data.data.data;
                arg.TableData.total = Number(res.data.data.total);
                arg.TableData.allList = res.data.data.desc; 
            }
            arg.vm.loading = false;
        });
    }

(七) 組織部門業務員三級聯動

需求:

  1. 選擇上一級須要清除下級綁定的數據和列表
  2. 頁面默認賦值當前登錄者的信息
//使用方法
    import {
      getCompanyListCommon,
      getDepartmentListCommon,
      getEmployeeListCommon
    } from "@/utils/public_function/commonUtils"; //引入 獲取組織、部門、業務員 方法 
    
    getCompanyListCommon(this); //便可獲取組織 其餘同理。其中三者的聯動可在各自的回調完成以後再調取下一級數據

(八) 優化性能,手動綁定下拉框數據

  • 使用方法

    import { bindIdsF } from "@/utils/public_function/commonUtils";
    //在詳情接口回調中,綁定
    this.$axios({
        method: "get",
        url: xxx,
        params: { order_id: xxx }
      }).then(({ data }) => {
        if (data.code == 200) {
          /**
           * data.data:下拉綁定的字段可能和詳情接口返回的字段不同,須要在這裏用assign作下處理,多數狀況下不須要
           * assign方法以下:
           */
          Object.assign(data.data, {
            employee_id: data.data.business_id,
            employee_name: data.data.business_name
          });
          // 列表下有 options 則傳入,沒有可不傳入
          bindIdsF(this, data.data, [
            ["customer_id", "customer_name", "customerList", "options"],
            ["department_id", "department_name", "departmentList", "options"],
            ["employee_id", "employee_name", "employeeList", "options"]
          ]); 
        }
      });
  • 綁定下拉框數據方法

    // commonUtils.js
    // 初始化綁定返回的id 下拉
    export function bindIdsF(self, oParent, arrList) {
        if (Array.isArray(arrList)) {
            for (const item of arrList) {
                let arr = [];
                if (item[3]) {
                    arr = [{
                        [item[0]]: Number(oParent[item[0]]),
                        [item[1]]: oParent[item[1]],
                    }];
                    self.$set(self[item[2]], item[3], arr)
                } else {
                    arr = [{
                        [item[0]]: Number(oParent[item[0]]),
                        [item[1]]: oParent[item[1]],
                    }];
                    self.$set(self, item[2], arr)
                }
            }
        }
    }

注意:遇到特殊狀況(如選客戶等帶出其餘下拉字段(業務員等)),需在相應接口回調裏繼續使用 bindIdsF 去初始化下拉數據

(九) 驗證

驗證模塊(utils/rules.js)中定義了組件上全部用到的驗證,而後將rules 掛載的Vue原型上便可。

  • 使用方法

    /** 
    * 組件中直接調用rules下的對應方法便可
    * 若是僅加星號,不作限制,只用prop屬性便可
    **/
    <el-form  :model="param" ref="paramsRef">
          <el-form-item label="庫存組織" prop="address" :rules="rules.address">
          <el-input v-model="params.address"></el-input>
          </el-form-item>
    </el-form>
  • 將驗證方法掛載到vue實例上

    // main.js
    import rules from './utils/rules' //全局定義表單驗證規則
    Vue.prototype.rules = rules;
  • rules.js 頁面定義方法,經常使用的有:

    // 自定義方法,做爲 validator 的屬性值
    // 第一位不能爲0,保留兩位小數
    const checkNumPot2 = (rule, value, callback) => {
        const reg = /(^[0-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/;
        if (!value && value !== 0) {
            return callback(new Error("請填寫數字"));
        } else if (!reg.test(value)) {
            return callback(new Error("請填寫數字,最多2位小數"));
        } else {
            callback();
        }
    };
    // 驗證身份證
    const checkIdNum = (rule, value, callback) => {
        const reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
        if (!reg.test(value)) {
            return callback(new Error("證件號碼不正確"));
        } else {
            callback();
        }
    };
    
    // 驗證整數
    const checkInterNum = (rule, value, callback) => {
        const reg = /^[0-9]*[1-9][0-9]*$/;
        if (!value) {
            return callback(new Error("請填寫整數"));
        } else if (!reg.test(value)) {
            return callback(new Error("請輸入整數"));
        } else {
            callback();
        }
    };
    //驗證座機
    const telephone = (rule, value, callback) => {
        const reg = /^([0-9]{3,4}-)?[0-9]{7,8}$/;
        if (!reg.test(value) && value != undefined && value != "") {
            return callback(new Error('請正確輸入固定電話,格式爲"區號-座機號碼"'));
        } else {
            callback();
        }
    };
    //輸入不可爲空
    const empty = (rule, value, callback) => {
        if (value == "") {
            return callback(new Error("輸入不能爲空"));
        } else {
            callback();
        }
    };
    //郵箱驗證 
    const emailNew = (rule, value, callback) => {
        const reg = /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,5}$/;
        if (!reg.test(value)) {
            return callback(new Error('4~18個字符,可以使用字母、數字、下劃線,需以字母開頭@xxx.com/cn'));
        } else {
            callback();
        }
    }; 
    
    /**
    * requited: 是否可爲空
    * pattern:自定義正則
    * validator:驗證方法
    * message:驗證不經過消息
    * trigger:觸發方式
    * 
    * pattern 和 validator 寫一個便可
    **/
    export default {
        validatePhone: [{
            required: true,
            pattern:: /^1[0123456789]\d{9}$/,
            message: "目前只支持中國大陸的手機號碼",
            trigger: "blur"
        }],  
        validateEmpty: [{
            required: true,
            validator: empty,
            trigger: "blur"
        }], //不可輸入爲空
        numPot2: [{
            required: true,
            validator: checkNumPot2,
            trigger: "blur"
        }], // 第一位不能爲0,保留兩位小數  
        interNum: [{
            required: true,
            validator: checkInterNum,
            trigger: "blur"
        }], // 整數 
        telephone: [{
            required: false,
            validator: telephone,
            trigger: "blur"
        }], // 驗證座機  
        emailNew: [{
            required: false,
            validator:emailNew, 
            message: "請輸入正確的郵箱格式:xxx@xxx.com/cn",
            trigger: "blur"
        }],
        company_id: [{
            required: true,
            message: "請選擇組織",
            trigger: "change"
        }], 
        ...
    };

(十) 打印

自定義打印模板後,以組件的形式引入並調用其打印方法便可。
打印官網

  • 使用方法

    // template
    <el-button type="text" @click="printF(scope, scope.row)"> 打印揀貨單 </el-button>
    
    <!-- 打印組件 -->
    <div>
      <printPage ref="printRef"></printPage>
    </div>
    
    <script>
    import printPage from "@/components/public/pickingList.vue"; //打印組件
    // 觸發打印組件
    methods:{
        printF(scope, row) { 
          this.$nextTick(function() {
            this.$refs.printRef.printPdf(row.outstock_id);
          });
        },
    }
    </script>
  • 打印內容頁面(自定義的格式)

    // 引用打印插件:@/utils/LodopFuncs.js
    
    // pickingList.vue 
    <template> 
      <div v-show="false">
        <div v-if="params!=null">
          <div id="print-header">
            <table class="header" cellpadding="0" cellspacing="0" style="width: 100%;">
              <tr>
                <td>
                  表頭
                </td>
              </tr>
            </table>
          </div>
          <div id="print-body">
            <table class="body" cellpadding="0" cellspacing="0" style="width: 100%;"> 
                <tr v-for="item in tableData" :key="item.id">
                  <td>表體</td>
                </tr> 
            </table>
          </div>
          <div id="print-footer">
            <table class="footer" cellpadding="0" cellspacing="0" style="width: 100%;">
              <tr>
                <td>
                  頁腳
                </td>
              </tr>
            </table>
          </div>
          <div id="print-pages">
            <p style='text-align:center;font-size:9pt;'>
              頁碼:
              <span tdata='pageNO'>第 ## 頁</span> /
              <span tdata='pageCount'>共 ## 頁</span>
            </p>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import { getLodop } from "@/utils/LodopFuncs";
    let LODOP = getLodop;
    export default {
      data() {
        return {
          params: null,
          tableData: null
        };
      },
      methods: {
        list(val) {
          let vm = this;
          this.$axios({
            method: "get",
            url: this.$api.printPick,
            params: { outstock_id: val }
          }).then(res => {
            if (res.data.code == 200) {
              vm.params = res.data.data;
              vm.tableData = res.data.data.goods_info;
              setTimeout(function() {
                vm.CreatePrintPages();
              }, 0);
            }
          });
        },
        printPdf(val) {
          this.list(val);
        },
        CreatePrintPages() {
          var LODOP = getLodop();
          if (!LODOP) {
            return false;
          }
          var strStyle = LODOP.strStyle;
          LODOP.PRINT_INIT("");
          LODOP.SET_PRINT_PAGESIZE(1, "205mm", "93.3mm", "CreateCustomPage"); // 0 操做者自行決定或打印機缺省設置 1 縱向打印,固定紙張;2 橫向打印,固定紙張
          LODOP.SET_PREVIEW_WINDOW(1, 0, 0, 1000, 600, ""); // 初始預覽窗口大小
          LODOP.SET_SHOW_MODE("LANDSCAPE_DEFROTATED", 1); // 橫向打印時正向顯示
          LODOP.SET_SHOW_MODE("HIDE_PAPER_BOARD", 1); // 去除背景滾動線
          LODOP.SET_PRINT_MODE("AUTO_CLOSE_PREWINDOW", 1); // 打印後自動關閉預覽
          LODOP.SET_PRINT_MODE("CUSTOM_TASK_NAME", "揀貨單"); // 打印隊列中的文檔名
          // 追加打印頭部
          LODOP.ADD_PRINT_TABLE(
            "2mm",
            "10mm",
            "185mm",
            "30mm",
            strStyle + document.getElementById("print-header").innerHTML
          );
          LODOP.SET_PRINT_STYLEA(0, "ItemType", 1); // 頁眉頁腳項
          // 追加打印主體:分頁、循環表格
          LODOP.ADD_PRINT_TABLE(
            "28mm",
            "10mm",
            "185mm",
            "20mm",
            strStyle + document.getElementById("print-body").innerHTML
          );
          // 追加打印底部
          LODOP.ADD_PRINT_TABLE(
            "63mm",
            "10mm",
            "185mm",
            "30mm",
            strStyle + document.getElementById("print-footer").innerHTML
          );
          LODOP.SET_PRINT_STYLEA(0, "ItemType", 1); // 頁眉頁腳項
          // 追加頁碼
          LODOP.ADD_PRINT_HTM(
            "80mm",
            "10mm",
            "185mm",
            "5mm",
            document.getElementById("print-pages").innerHTML
          );
          LODOP.SET_PRINT_STYLEA(0, "ItemType", 1);
          LODOP.PREVIEW();
        }
      }
    };
    </script>

(十一) 簽名

簽名算法先後臺要保持一致

  • 使用

    //簽名使用方法 在 axios 請求攔截中,給發送的數據用簽名算法格式化
    config.data.sign = serverMoudle(config.data) //post請求
    config.params.sign = serverMoudle(config.params) //get請求
  • 簽名算法

    import md5 from 'js-md5';
    import Qs from "qs";
    const unchangeable = 'xxxxxxx'; //根據該字符串加密
    import { objKeySort } from '@/utils/public_function/commonUtils.js';
    
    //將接口參數排序並將null或undefined的數據設爲空
    export function objKeySort(obj) { 
        var newkey = Object.keys(obj).sort(); 
        var newObj = {};   
        for (var i = 0; i < newkey.length; i++) {  
            //過濾參數中null或者undefined的值,並使之默認爲空,不然報錯
            if ([null, undefined].includes(obj[newkey[i]])) obj[newkey[i]] = '';
            newObj[newkey[i]] = obj[newkey[i]];  
        }
        return newObj;
    }
    
    
    export function serverMoudle(params) {
      let newUrl = ''
      if (params == undefined) {
        newUrl = "key=" + unchangeable;
        return md5(newUrl)
      } else {
        if (Qs.stringify(objKeySort(params)) != '') {
          newUrl = decodeURIComponent(Qs.stringify(objKeySort(params))) + "&key=" + unchangeable;
        } else {
          newUrl = "key=" + unchangeable;
        }
        return md5(newUrl)
      }
    }

3、代碼規範

(一) JS規範

1.直接只用對象、數組字面量初始化變量,不用new它的實例(官方推薦,簡單明瞭)

let arr = [] , obj = {};

2.儘可能用全等判斷是否相等(===);

null === false; //false

3.用變量自己的布爾類型去作判斷,必須轉布爾值時可使用雙感嘆號轉布爾類型。

!!null === false //true

4.用其餘代碼優雅替換if else語句(三目運算符、switch、邏輯判斷等)

5.寫有意義的註釋,瞭解一些特殊標記,e.g.
TODO: 有功能待實現。
FIXME: 該處代碼運行沒問題,但可能因爲時間趕或者其餘緣由,須要修正。
HACK: 爲修正某些問題而寫的不太好或者使用了某些詭異手段的代碼。
XXX: 該處存在陷阱。

6.避免代碼冗餘,能循環則不去單個操做變量

(二) Vue規範

1. 過濾器:

  1. v-for 要配合key使用,標註惟一性,這將會讓 Vue 知道如何使行爲更容易預測。
    ② 避免 v-if 和 v-for 同時用在同一個元素上。

2. 父子組件相互傳遞數據:

  1. 在父組件傳子組件數據上,prop 起着重要的做用,其中prop的定義應該儘可能詳細,至少須要指定其類型,同時可指定該數據的默認值(避免控制檯報錯),注意,在聲明 prop 的時候,其命名應該始終使用 camelCase,而在模板和 JSX 中應該始終使用 kebab-case
  2. 組件間傳遞數據要靈活使用,如使用vue自帶的實例屬性( $parent/$children/$emit/$attrs/$refs.name)
  3. 若是涉及不相關組件間的傳值,可以使用狀態管理 vuex官網 點擊

3. 樣式:

  1. 組件裏的 <style> 要避免污染全局,請設置屬性 scope

4. data

  1. 每一個組件對應的 data ,要用 function 來 return 出去,這樣每一個 data 的屬性就不是掛載到 vue 的原型上,而是有它本身的上下文,當 data 的值是一個對象時,它會在這個組件的全部實例之間共享

5. $set

  1. 由於 Vue 沒法探測普通的新增屬性,因此向響應式對象添加屬性需用$set賦值

6. JSX語法

如需渲染較爲複雜的 DOM ,可以使用Jsx語法簡化代碼 GITHUB 點擊

7. 儘可能使用Vue自帶API

  1. 使用 vue 自帶的 API 替代 js 原生方法 如 $remove Vue API 點擊

8. 性能優化:

  1. 組件模板應該只包含簡單的表達式,複雜的表達式則應該重構爲計算屬性或方法( computed )(如表格中的數據計算或格式化等)

(三) 命名規範

  1. 小駝峯式命名(格式:myVariale
    • 組件名規範:
      新增頁面:xxxAdd.vue / 修改頁面:xxxModify.vue / 主頁面:xxxIndex.vue / 查看頁面:xxxSee.vue

    • 方法名規範:
      查詢:onSearch / 重置:onReset / 修改某個功能:changeXxx / 新增:addF / 修改:modifyF / 查看:seeF

    • 變量名規範:
      列表數據:xxxList / 表格數據:TableData.tableData / 表頭參數:params / 默認值:defaultXxx

    • 狀態管理變量名規範:
      states:xxxIndex
      mutations:xxxIndex
      getters:xxxIndexGet
      actions:xxxIndexACT

  2. 良好的命名也能致使維護性更強,可讀性更好,同時更大程度避免變量重複致使的bug

(四)文本編輯器規範

  • 縮進:兩個空格

  • 去除eslint語法檢測,雖然能使代碼更規範,可是及其影響工做效率,不推薦。

  • 建議使用編輯器vscode或sublime Text,學會利用編輯器的有用插件。

    插件推薦

    ① 提升效率:HTML Snippets(html snippets)、Javascript (ES6) Code Snippets(ES6 snippets)、Auto Close Tag(自動補全)、Emmet(css snippets)、Vue2 Snippets

    ② 功能加強:Color Highlight(識別代碼的顏色)、Bracket Pair
    Colorizer(識別代碼中的括號,標記不一樣的顏色)

    ③ 設置編輯器自帶的自動保存、自動格式化代碼功能

    ④ 等等

  1. 運用好各編輯器的快捷鍵,工做效率會事半功倍
  2. 學會使用控制檯調試代碼

4、優化內容

該項目還在不斷的完善當中,避免不了存在着一些不足,如下是目前規劃的開發計劃。

  • [x] 緩存功能
  • [ ] 完善項目細節
  • [x] 優化性能,減小接口調用次數
  • [x] 升級Vue3.0,極大提高編譯與打包速度
相關文章
相關標籤/搜索