Make writing a habit together! This is my first day to participate in the “Gold Digging Day New Plan · April More text challenge”, click to see the details of the activity.
The background,
ToB systems, or more commonly called mid-background systems, often use the Element-UI, iView component libraries to improve development efficiency
Such systems tend to have complex interaction logic and not much emphasis on THE UI (uI-rich teams never mention that), but UI or no UI, not as much emphasis on the UI as the C side
However, as a front-end, we pay attention to the page experience, UI interaction is our concern, this paper combined with their own experience on how to do the front-end ToB system UI& interactive experience
Second, problems encountered
It’s fine if you’re a system, it’s not messy; Multiple ToB systems can easily become multifarious without a specification
At that time, I handed over five systems, which were made by different departments before the handover. There were elements and Views. We combine these five systems into a complete digital operation solution product; The interaction, the UI was really overwhelming, and we didn’t have UI support
So I explored some specifications and implemented them myself
Iii. UI& interactive unification
In order to study which UI and interaction are relatively best, I refer to antD Pro, Element-UI and other mid-background systems. They fall into the following categories
- loading
- navigation
- The menu
- Bread crumbs
- TAB switching
- Table search form
- form
1, the loading
It can be divided into two types: loading during route switchover and loading during interface request. Details are as follows
- Route switching: Available in routing
Hook function
In the processing- Advantages:
Unified processing, low cost
- Disadvantages: Whether the data load is complete
Can't accurate
- Advantages:
- Interface request: Yes
Initialize the interface of the page
Loading is added. For example, loading is required for an interface to query a list, but loading is not required for an interface to delete a row- Advantages: Relatively accurate
- Disadvantages:
The high cost
Note: for adding, deleting, modifying, and checking tables, the tables should be uniformly encapsulated. Loading should be set in the local loading of the table, and related interfaces of the table should be used to set loading
Take ANTD Pro for example, when switching routes,The content area
The whole thing began to load,After the route switch,form
When loading a table, only the table is loadedAnother common route switching loading occurs in theBrowser header
In this way,iview
providesiview.LoadingBarYou can use it directly,element
You can usenprogressPackage, code as follows
step 1:
npm install --save nprogress
Copy the code
step 2:
// router/index.js
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
router.beforeEach((to, from, next) = > {
NProgress.start();
next()
})
router.afterEach(to= > {
NProgress.done();
})
Copy the code
step 3:
// App.vue// Change the loading color to theme color#nprogress .bar {
background: #2d8cf0 ! important; The \}Copy the code
2, navigation,
Navigation is a crucial part, including head navigation, side menu navigation, bread crumbs, and page switching selection cards
I’m going to post a couple of pictures to give you a sense of what it was like before we changed it
The menu
- The first level menu has ICONS
- There is no icon on the level 2 menu
- All systems have the same color and interaction
- On the current page, and the subpage menus of the current page are highlighted
- Menu naming and distribution of each system are partially adjusted. If there are any improper changes, please contact us
Bread crumbs
- Breadcrumbs are displayed according to the page menu hierarchy
- The current page is highlighted
- Click on the non-root node to return to the page. Click on the root node to return to the first menu of the root node
3. Table search form
Specification as follows
- Margin-bottom :16px; margin-bottom:16px
- The form has three form items per row
- When the number of forms is larger than 2, the buttons can be folded and expanded
- Margin :10px between each vertical line
- [query], [reset] after placing the last form item, it is always at the far right of its row
- The action classes (create, delete) are placed above the table header (see below) and are treated as a module with the table
- The spacing between buttons is unified as margin: 16px
- Query form [query] before [reset]; The query button is the theme color and the reset button is white
- Query form class operation class button unified copy is [query] [reset]; Don’t icon
- Query form without label add label
- Operation buttons. [New button] Delete button documents are unified into [Delete] and [Batch Delete] according to the specific service name.
- When the query class forms are larger than two, expand and shrink functions are added; Shrink by default, display a row after shrinking
- The TAB is placed at the top, as follows
Search form componentCode implementation
// form-search/index.vue
<template>
<div class="form-search-container">
<! Put tabs -->
<slot></slot>
<el-form
class="form-search-container__form"
:inline="true"
label-suffix=":"
:label-width="labelWidth"
:style="value.length % 3 == 0 && ! collapse ? 'padding-bottom: 40px; ':' '"
>
<form-search-item
v-for="(item, index) in formList"
:key="index"
:type="item.type"
:label="item.label"
:placeholder="item.placeholder"
:options="item.options"
:name="item.key"
:isSlot="item.isSlot"
:filterable="item.filterable"
v-bind:value.sync="item.value"
>
<template v-slot:[item.key] >
<slot :name="Array.isArray(item.key) ? item.key[0] : item.key"></slot>
</template>
</form-search-item>
<el-form-item
class="form-search-container__item form-search-container__btn"
>
<el-button type="primary" size="medium" @click="handleSearch"
>Query < / el - button ><el-button @click="resetForm" size="medium">reset</el-button>
<el-button v-if="value.length > 2 && ! hideCollapse" type="text" @click="handleCollapse"
>{{ collapse ? 'expand' : 'fold'}}<i v-if=! "" collapse" class="el-icon-arrow-up"></i>
<i v-else class="el-icon-arrow-down"></i>
</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import FormSearchItem from './components/form-search-item'
import bus from '.. /.. /utils/event-bus'
export default {
components: { FormSearchItem },
props: {
value: {
type: Array.default: () = >[],},labelWidth: {
type: String.default: '80px',},hideCollapse: {
type: Boolean.default: false}},computed: {
formList() {
this.$nextTick(() = > {
bus.emit('form-search-collapse')})return this.collapse ? this.value.slice(0.2) : this.value
},
},
data() {
return {
collapse: false.initObj:}}, {},created() {
this.value.map((item) = > {
Object.assign(this.initObj, { [item.key]: item.value })
})
},
methods: {
resetForm() {
this.value.map((item) = > {
item.value = this.initObj[item.key]
})
this.$emit('update:value'.this.value)
this.$emit('reset-click'.this.initObj)
},
handleSearch() {
const resObj = {}
this.value.map((item) = > {
if (Array.isArray(item.key)) {
item.key.map((keyItem, index) = > {
Object.assign(resObj, { [keyItem]: item.value[index] })
})
} else {
Object.assign(resObj, { [item.key]: item.value })
}
})
this.$emit('update:value'.this.value)
this.$emit('search-click', resObj)
},
handleCollapse() {
this.collapse = !this.collapse
bus.emit('form-search-collapse')}}},</script>
<style lang="less" scoped>
.form-search-container {
background-color: #fff;
margin-bottom: 8px;
padding: 16px 10px 0;
}
.form-search-container__form {
display: flex;
flex-wrap: wrap;
position: relative;
}
.form-search-container__item {
flex: 0 0 30%;
height: 32px;
}
.form-search-container__btn {
position: absolute;
right: 0;
bottom: 0;
margin-bottom: 10px;
}
</style>
Copy the code
// form-search/components/form-search-item.vue
<template>
<el-form-item
:label="label"
class="form-search-container__item"
:prop="Array.isArray(name) ? name[0] : name"
>
<slot v-if="isSlot" :name="name"></slot>
<el-input
v-if="type === 'input' && ! isSlot"
:placeholder="placeholder"
v-model="value2"
@change="handleChange"
></el-input>
<el-select
v-if="type === 'select'"
v-model="value2"
:placeholder="placeholder"
@change="handleChange"
:filterable="filterable"
>
<el-option
v-for="(item, index) in options.list"
:key="index"
:label="item[options.label || 'label']"
:value="item[options.value || 'value']"
></el-option>
</el-select>
<el-date-picker
v-if="type === 'time'"
type="daterange"
v-model="value2"
range-separator="To"
start-placeholder="Start Date"
end-placeholder="End Date"
:default-time="[' 00:00:00 ', '23:59:59]"
@change="getTime"
>
</el-date-picker>
</el-form-item>
</template>
<script>
export default {
props: {
formModel: {
type: Object.default: () = >{},},label: {
type: String.default: ' ',},type: {
type: String.default: 'input'.//select
},
placeholder: {
type: String.default: ' ',},value: {
type: [String.Number.Array].default: ' ',},options: {
type: Object.default: () = > {
return {
list: [].label: 'label'.value: 'value',}}},name: {
type: [String.Array].default: () = > ' ',},isSlot: {
type: Boolean.default: false,},filterable: {
type: Boolean.default: false,}},data() {
return {
value2: this.value,
}
},
watch: {
value(val) {
this.value2 = val
},
},
methods: {
handleChange() {
this.$emit('update:value'.this.value2)
},
getTime(timeVl) {
if (timeVl) {
this.value2 = [timeVl[0].getTime(),timeVl[1].getTime()]
}
!this.value2 && (this.value2 = ' ')
this.handleChange()
},
},
}
</script>
<style lang="less" scoped>
.form-search-container__item {
flex: 0 0 30%;
}
.el-range-editor.el-input__inner {
width: 245px;
}
.form-search-container__item /deep/ .el-form-item__content {
width: 242px;
}
.form-search-container__item /deep/ .el-form-item__content .el-select {
width: 100%;
}
.el-form-item {
margin-bottom: 10px;
display: flex;
}
</style>
Copy the code
4, tables,
- Sort: number, time type increment sort, other delete sort
- Align: center numbers and left text
- Add zebra stripes
- Unity of tables and pictures
- Unified processing width and height: 60px
- The table is displayed in the column
- When there is no picture, the unified copy is “No picture”.
- If an image fails to be loaded, it is processed as a custom image
- Add bright color style with jump
- Add flags to the front of different states
<el-badge is-dot class="item"></el-badge>
, left-aligned - Table added full screen function
- Table new button
- Operation class first [add class button] and then [delete class button]; Add a class button is the theme color, delete a class button is red
- Table operation buttons are unified
- Button list header text is unified as [Operation]
- The action column is fixed to the right of the table
- Unified use of text buttons
<el-button type="text"> </el-button>
- < el-Divider direction=”vertical”>
- When buttons <=3, display directly
- When the button >3, the first and second buttons are usually “Edit” or “Details” or “Delete”, and the other buttons are summarized into “More” buttons
- Copy is unified into [edit] [delete] [more]; Part of the Edit button document is now modified
- Dropdown menu < el-Dropdown -menu slot=”dropdown”> appears when the mouse is over more buttons or Click more buttons
- General button use theme color, delete button red – no permission to operate when the display gray, (or show the original color, just gray), is not displayed, not friendly
-
Information link
Write in the last
These are just some of my experiences, but there are many more
UI& interaction specification, often overlooked, but as pages and scenes proliferated; No specification will make the system difficult to use, after the UNIFICATION of UI& interaction, in fact, the cost is quite large, the benefit is also very difficult to measure, it is better to develop specifications from the beginning