Project background

Because the department business developed rapidly, and the cooperation of the contract number growth speed, in the past by artificial process was carried out on the contract entry, find contract management mode gradually couldn’t keep up with the required processing speed, so will be offline disorderly multifarious contract upgrade for specification of the growing demand of online contract, based on the business demands was born out of the contract system.

The birth of the contract system has greatly solved the problem of input and management of contracts, which consumes a lot of human energy. While reducing the workload of business students, it also provides better service to partners and reduces errors and omissions in contract data filling.

Technology selection

Technology selection why
Front-end JS framework Vue2.0 High community popularity, a variety of components, mature technology stack, HTML template familiar with low cost, routing (VUE-Router), state manager (VUEX) and other highly practical components maintained by the VUE core team, is the official recommended components
UI library Iview Provide UI component library based on Vue development, relatively mature technology, component types, the current infrastructure system using IView for development (* main reason), to avoid the introduction of other UI libraries resulting in code clutter
CSS development Tools SCSS Improve CSS writing efficiency. CSS code written using functional programming has high scalability and readability, and low maintenance cost

Screenshot of some function pages of the contract project

Manual entry of contract – Basic information:

Manual entry of contracts – details

Contract query

Contract Query – Works list information

Contract review

Development catalog division and its significance?

If a project has a reasonable project directory structure, it can greatly reduce the cost of familiarizing the developer and the maintenance personnel with the project

As shown in the figure above, multiple different folders are created under the general directory ContractV2. The corresponding functions and descriptions are as follows:

The advantages of componentized and modular development and how to define them?

In the new project requirements reach, don’t rush to immediately for development, properly first comb over the project structure, process, interaction, to inductive classification of function point, and repeated communication products, the demand side, clear project business process is smooth, whether the function point have reusability, on the one hand, in order to be in the development, function division has a big picture thinking of the project, On the other hand, in order to avoid useless efforts during development, components and modules that do not need to be encapsulated are encapsulated, but components and modules that need to be encapsulated are directly implemented by business code, resulting in the later code reconstruction.

advantages disadvantages
Componentized and modular development Low maintenance cost

High scalability

Low coupling degree, easy to delete

Reusability is strong

Transformation cost

Low learning cost
The extension based on the status code or service logic must be compatible with multiple application scenarios

In the early stage of the project, it is not convenient to determine whether modular and componentized development is needed

Low degree of customization

Communication between components is not convenient
Write the business logic implementation directly Fast implementation

Low function encapsulation makes single function module less redundant code

Suitable for highly customized development
High maintenance costs

Low scalability

Low reusability

Transformation cost

Cost of learning

The code between services and components is highly coupled and cannot be deleted or modified

Iview Based on the Tabs component of the second component encapsulation code and instance code:


Componentized package start

<style lang="scss" scoped>
    .contract-tabs {
        margin-top: 20px;
        /deep/.can-not-close+.ivu-icon-ios-close {
            display: none;
        }
        /deep/.ivu-tabs-bar {
            margin-bottom: 0;
        }
        .ivu-page {
            text-align: right;
        }
    }
<style>
Copy the code
<template>
    <Tabs ref="tabs" type="card" :value="curTab" :animated="false" closable @on-tab-remove="handleTabRemove" :before-remove="removeBefore" class="contract-tabs">
        <template v-for="(tab,index) in tabColumns">
            <TabPane v-if="tab.show" :label="tab['closeable'] ? tab.label : (h) => tab.renderLabel(h, tab)" :class="{'disable-close': ! tab.closeable}" :key="randomKey(index)" :name="tab.name">
                <Table v-if="tab.type == 'table'" :loading="tab.isLoading" height="630" border :columns="tab.columns" :data="tab.data"></Table>
                <Page :current="tab.page.currentPage" :total="tab.page.total" size="small" show-total @on-change="(page)=>{tab.page.onChange(page,index)}" show-elevator :page-size="tab.page.page_size" v-if="tab.page"></Page>
            </TabPane>
        </template>
    </Tabs>
</template>
Copy the code
import Vue from 'vue';
import {Tabs} from 'iview';

/ / the Tab properties
const tabsNativeProps = Tabs.props,
    tabsNativePropNames = Object.keys(tabsNativeProps);

// The Tab property listens
const originProxyWatcher = tabsNativePropNames.reduce((watch, name) = > {
    watch[name] = function (val) {
        Vue.set(this.wrapProps, name, val);
    };
    return watch;
}, {});

/ / the Tab to the original method
const tabNativeMethods = Tabs.methods,
    tabNativeMethodsName = Object.keys(tabNativeMethods);

//Tab original method proxy
const originMethodsProxy = tabNativeMethodsName.reduce((proxy, name) = > {
    proxy[name] = function (. args) {
        const $tabs = this.refs['tabs'];
        $tabs[name].apply($tabs, args);
    };
    return proxy;
}, {});


export default {
    pageAuthor: 'janwardchen'.components: {Tabs},
    props: Object.assign({
        'curTab': {
            type: String.default: 'tabIndex1',},'tabPaneColumns': {
            type: Array.default: () = > {
                return []
            }
        },
    }, tabsNativeProps),
    watch: Object.assign({}, originProxyWatcher),
    methods: Object.assign({
        renderLabel: (createElement, params) = > {
            let {label=' ',closeable,show=true} = params;
            if (closeable == false || closeable == 'false') {
                return createElement('span', {attrs: {class: 'can-not-close'}}, label);
            }
            if(! show) {return null; }},removeBefore(idx) {
            const {closeable} = this.tabPaneColumns[idx];
            if (closeable == false || closeable == 'false') {
                return new Promise((resolve, reject) = >{}); }},handleTabRemove(name, arg) {
            this.$emit("removeTab",name);
        },
        randomKey(idx) {
            const _t = new Date(),
                _key = idx.toString() + _t.getTime().toString();
            return _key;
        }
    }, originMethodsProxy),
    computed: {
        tabColumns() {
            let _columns = this.tabPaneColumns || [];
            const self = this;
            if (_columns) {
                _columns.forEach((item, idx) = > {
                    item['renderLabel'] = self.renderLabel;
                    if(item.page && JSON.stringify(item.page) ! ='{}') {
                        let {currentPage=1,total=0,page_size=20,onChange=() = >{return false; }} = item.page; item.page.currentPage =Number(currentPage);
                        item.page.total = Number(total);
                        item.page.page_size = Number(page_size);
                        item.page.onChange = onChange;
                    }
                    if(item.show==undefined) {
                        item.show = true; }}); }return_columns; }}}Copy the code

Componentization encapsulates end


Component example example:

<template> <ContractTabs :tab-pane-columns="tabPaneColumns" :cur-tab="curTab" @removeTab="removeTab"></ContractTabs> </template> <script> import ContractTabs from '~/components/contract-tabs.vue'; Export default {components: {ContractTabs,}, data() {return {tabPaneColumns: [{label: 'Contract ',type: 'table',columns: [{title: 'contract number ',minWidth: 170, key: 'pact_no',align: 'center',}, {title:' protocol number ',minWidth: 140,key: 'is_signed_protocol',align: 'center',render: this.rendercolumn (),}, {title: ' 'works_list',align: 'center',render: this.rendercolumn (),}, {title: 'we ', minWidth: 110, key: 'ou',align: 'center'}, {title: 'vendor name', minWidth: 175, key: 'cp_name', align: 'center',}, {title:' valid time ', minWidth: 180, key: 'align: 'center',}, {title:' status ', minWidth: 130, key: 'state', align: 'center',}, {title: 'status ', minWidth: 130, key: 'state', align: 'center',}, {title: MinWidth: 100, key: 'head_person', align: 'center',}, {title: 'head_person', minWidth: 100, key: 'head_person', align: }, {title: 'auditor ',minWidth: 100, key: 'head_person', align: 'center',}, {title:' contract details ',minWidth: 110,key: 'show_dfixed: 'right',render: this.rendercolumn (),}, {title: 'operation ',minWidth: 145,key: 'sign_protocol_list',align: 'center',fixed: 'right',render: this.renderColumn(),} ], isLoading: true, page: {currentPage: 1, total: 0, page_size: 20, onChange: this.fetchIndexData}, data: [], closeable: false, name: 'tabIndex1' }, ], } }, methods: {// Render table column renderColumn() {..... } } } </script>Copy the code

As shown in the code above, in tabPaneColumns, you just need to process the corresponding TAB data, pass in the button click processing event, and leave the rest of the processing to the ContractTabs. By simply deleting


, you remove this functionality without affecting the code of other modules.

Sample component diagram:

In addition, the modular processing of functions has also made a certain package, some examples are posted below:

/** * Perform the Ajax request *@param Url Request interface address *@param Data request parameter *@param Cb Callback performed after a successful request *@param Type The request mode is POST or GET@param Ignore Whether the callback ignores res.status judgment */
async fetchData(url, data, cb, type, ignore) {
    let method = type || 'get';
    const res = await this.ajax({url, data, method});
    if (ignore) {
        cb(res);
        return false
    } else {
        if (res.status == 2) {
            cb(res);
            return false;
        } else if (res.status == 1) {
            this.$Message.warning(res.msg);
            return false;
        } else {
            console.log(res.status);
            this.$Message.warning(res.msg);
            return false; }}},/** * Format the form data before submission to ensure the universality of data *@param Data Indicates the data to be processed. After processing, the data is returned. */
formatFormData(data) {
    let formData = {};
    if (JSON.stringify(data) ! ='{}') {
        let ObjArr = Object.keys(data);
        ObjArr.forEach((keys) = > {
            data[keys].forEach((item) = > {
                formData[item.key] = item.value;
            });
        });
    }
    return formData;
},
Copy the code

As above, all of these components, function modules need to reuse in a number of places and if you don’t do any packaging processing, every need to use manual directly to write again, one can imagine the amount of repeat code is a lot of, and at the time of change, and change leak of correction is likely to happen, this is the programmer’s nightmare.

Contract project development summary

  • Project function, page number, interface, the function of each interface, data, structure, diversity, so in the development, the background for each interface transition to complete the front-end, must organize, record interface into the parameter, function and call mode, call local, return the data in different fields, or when the amount of the interface is much, find or easily confused about when checking.
  • In the early stage, it is necessary to have enough understanding of the requirements, communicate with the product and demander more, confirm the strict degree of each function interaction and data verification, avoid too much componentization encapsulation, and avoid multiple modification and adjustment of functions, components and modules due to lack of communication and understanding.
  • The popular TypeScript was not used in this project, which caused a lot of errors caused by data formats and types during component development and debugging interface. We need to slowly locate and solve them step by step, and the debugging time is not well controlled.