Crudcustom

Introduction #

CRUD customization enables highly personalized UI through template overrides, including custom column display (conditional styles, progress bars), multi-level headers, custom detail pages (drawer/dialog), custom validation rules, custom functions, and custom default values.

Custom Area Block Template
Custom Column body_table_column
Multi-level Header body_table_columns
Custom Detail Page dialog_card
Custom Add Page dialog_modify
Custom Indicator Card body_extHead

Overriding Component Areas #

Custom Column Display #

Custom Column

  • Show “sales” column in red when value > 100:
<!-- Custom table column -->
{% block body_table_column %}
<span v-else-if="item==='sales'" :style="scope.row[item]>100?'color:red':''">{[ scope.row[item]]}</span>
{% endblock %}
  • Display “rate” field as a progress bar:
{% block body_table_column %}
<el-progress v-else-if="item==='rate'" :percentage="scope.row[item]"></el-progress>
{% endblock %}
  • Show up/down arrows based on value:
<!-- Style area -->
.red{color:red}
.green{color:green}
<!-- Template area -->
<span v-else-if="item==='sales'"><i class="scope.row[item]>0?'el-icon-top red':'el-icon-bottom green'"></i>{[ Math.abs(scope.row[item]) ]}</span>

Note: Use actual field names, not display names

Multi-level Headers #

Multi-level Header

<!-- Style area: define header borders -->
.el-table--border th.el-table__cell,.el-table__fixed-right-patch{border:0.5px solid;)
<!-- Template area: override headers -->
{% block body_table_columns %}
<el-table-column prop="date" label="Date" width="150"></el-table-column>
<el-table-column label="Delivery Info">
    <el-table-column prop="name" label="Name"></el-table-column>
    <el-table-column label="Address">
        <el-table-column prop="province" label="Province"></el-table-column>
        <el-table-column prop="city" label="City"></el-table-column>
    </el-table-column>
</el-table-column>
{% endblock %}

Custom Detail Page #

  • detail is bound to data when clicking view:
<!-- Custom detail page -->
{% block dialog_card %}
<el-dialog :visible.sync="dialogTableVisible" :width="dialogWidth">
    <el-table :data="detail" style="width: 100%" size="mini">
      <el-table-column prop="date" label="Date"></el-table-column>
      <el-table-column prop="name" label="Name" width="180"></el-table-column>
    </el-table>
</el-dialog>
{% endblock %}
  • Define detail page as drawer:
{% block dialog_card %}
<el-drawer :visible.sync="dialogTableVisible" id="partCard" size="98%"></el-drawer>
{% endblock %}

Other Overrides #

<!-- Override search area -->
{% block body_search %}
<div class="search_base_div" v-if="searchHead"><div class="search_div"><div style="display:flex;" v-if="startRender" id="partSearch1"></div></div></div>
{% endblock %}

<!-- Filter area component -->
{% block body_filter %}
<template v-if="!searchHead" id="partSearch2"></template>
{% endblock %}

<!-- Custom add/modify dialog -->
{% block dialog_modify %}
<el-dialog :title="title" :visible.sync="dialogFormVisible">
 <el-form ref="form" :model="form" :rules="rules">
    <el-form-item prop="displayName" label="Display Name" :label-width="formLabelWidth">
      <el-input v-model.trim="form.fieldName" autocomplete="off"></el-input>
    </el-form-item>
 </el-form>
</el-dialog>
{% endblock %}

Custom Area — Indicator Cards #

Indicator Card

  • Template override:
{% block body_extHead %}
<div class="search_base_div"><div class="search_div"><div style="display:flex;" v-if="startRender">
  <el-statistic group-separator="," :precision="2" :value=diyVars.d1.value :title=diyVars.d1.title></el-statistic>
  <el-statistic title="Ratio"><template slot="formatter">456/2</template></el-statistic>
</div></div></div>
{% endblock %}
  • JS assignment:
vapp.diyVars.d1={value:12345, title:'Metric A'}
  • From dataset (e.g., #4):
select 'Metric A' as title, 12345 as value from xxx;
select 'Metric B' as title, 1245 as value from xxx
ds_refresh(4);
vapp.diyVars.d1=ds_createMap_all(data4.df0);
vapp.diyVars.d2=ds_createMap_all(data4.df1);

Custom Validation Rules #

vapp.rules = {
    username: [
      { required: true, message: 'Please enter username', trigger: 'blur' },
      { min: 3, max: 15, message: 'Username must be 3-15 characters', trigger: 'blur' }
    ],
    qty: [{
        required: true,
        validator: (rule, value, callback) => {
            if (value === '') {
                callback(new Error('Please enter quantity'));
            } else if (isNaN(value)) {
                callback(new Error('Must be a number'));
            } else {
                callback();
            }
        },
        trigger: 'blur'
    }],
};

Custom Functions #

// Table row class for highlighting
vapp.tableRowClassName=({row, rowIndex})=>{
    if (row.fieldName > 100) {
         return 'warning-row';
    }
    return '';
}
// Auto-calc on input
vapp.calcChange=(k)=>{
    if(k=='food_price'){vapp.form.totalPrice=vapp.form.food_price*100}
};
// Row click action
vapp.handleRowClickChange=(row)=>{}
// Data export action
vapp.daochu=()=>{}
// Custom function
vapp.diyFun=(param)=>{}
// Custom variables
vapp.diyVars={}
// Refresh table
vapp.refreshTable();
// Refresh filter
vapp.data_filter();

Custom Config Keys #

Specify which configs use custom definitions instead of page-saved configs:

vapp.diyKeys='nameDict,optionDict';

Custom Default Values #

vapp.defaultDict={
    "calcDate":new Date().format('yyyy-MM-dd'),
    "orderDate":[new Date().setDate(new Date().getDate()-30),new Date()]
}
  • To apply params on page open:
ds_setParam('calcDate', vapp.defaultDict.calcDate);
ds_setParam('orderDate_s', vapp.defaultDict.orderDate[0]);
ds_setParam('orderDate_e', vapp.defaultDict.orderDate[1]);