Files
dvcp_v2_webapp/project/oms/apps/develop/AppAiCode/detailLayout.vue
2022-07-01 18:00:00 +08:00

1050 lines
31 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="detailLayout">
<el-scrollbar class="left">
<div class="left-item" v-for="(component, index) in components" :key="index">
<div class="left-item__title">
<h2>{{ component.label }}</h2>
<span>{{ component.tips }}</span>
</div>
<div class="left-item__list">
<draggable
class="components-draggable"
:list="component.children"
:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
:sort="false"
:move="onMove"
:clone="cloneComponent"
@end="onEnd">
<div class="left-item__item" v-for="(item, i) in component.children" :key="i" @click="clone(item)">
<i class="iconfont" :class="item.icon"></i>
<span>{{ item.fixedLabel }}</span>
</div>
</draggable>
</div>
</div>
</el-scrollbar>
<el-scrollbar class="middle">
<div class="middle-content">
<div class="middle-content__wrapper">
<el-form label-width="100px" label-position="right">
<draggable
class="middle-draggable"
style="height: 100%;"
:animation="340"
scroll
v-model="targetList"
element="div"
@end="onElEnd"
:sort="true">
<ai-card
:class="[groupIndex === j && isGroup ? 'active' : '']"
:data-index="j"
@click.native.stop="groupIndex = j, activeIndex = -1, isGroup = true"
:title="group.groupName" v-for="(group, j) in targetList"
:key="j">
<template #content>
<draggable
class="ai-form"
style="height: 100%;"
v-model="group.column"
:animation="340"
scroll
@end="onElEnd"
element="div"
filter=".components-filter"
draggable=".components-item"
group="componentsGroup"
:sort="true">
<div
class="components-item"
v-for="(item, i) in group.column"
:style="{width: item.grid * 100 + '%'}"
:class="{
'components-filter': item.isInit,
'active': groupIndex === j && activeIndex === i
}"
@click.stop="groupIndex = j, activeIndex = i, isGroup = false"
:key="i">
<div class="left-item__item--remove" title="删除字段" v-show="!item.isInit && groupIndex === j && activeIndex === i"
@click.stop="removeItem(j, i)">
<i class="iconfont iconDelete"></i>
<span>删除字段</span>
</div>
<el-form-item style="width: 100%;" :label="item.fieldName" :rules="[{ required: item.mustFill === '1' }]">
<template v-if="(item.type === 'textarea')">
<el-input :disabled="item.disable === '1'" :rows="item.lineNumber" size="small" type="textarea" :placeholder="item.fieldTips"
v-model="item.defaultValue"></el-input>
</template>
<template v-if="(item.type === 'resident')">
<el-input :disabled="item.disable === '1'" size="small" placeholder="请选择人员" v-model="item.defaultValue">
<template slot="append">选择人员</template>
</el-input>
</template>
<template v-if="(item.type === 'gird')">
<el-input :disabled="item.disable === '1'" size="small" placeholder="请选择网格" v-model="item.defaultValue">
<template slot="append">选择网格</template>
</el-input>
</template>
<template v-if="item.type === 'radio'">
<el-radio-group v-model="item.defaultValue" :disabled="item.disable === '1'">
<el-radio :label="field.label" v-for="(field, index) in item.options" :key="index">{{ field.label }}</el-radio>
</el-radio-group>
</template>
<template v-if="item.type === 'number'">
<el-input-number v-model="item.defaultValue" :placeholder="item.fieldTips"></el-input-number>
</template>
<template v-if="item.type === 'rtf'">
<ai-editor v-model="item.defaultValue" :placeholder="item.fieldTips" :instance="instance"/>
</template>
<template v-if="item.type === 'time'">
<el-date-picker
v-model="item.defaultValue"
size="small"
:placeholder="item.fieldTips">
</el-date-picker>
</template>
<template v-if="item.type === 'date'">
<el-date-picker
v-model="item.defaultValue"
size="small"
:placeholder="item.fieldTips">
</el-date-picker>
</template>
<template v-if="item.type === 'datetime'">
<el-date-picker
v-model="item.defaultValue"
size="small"
:placeholder="item.fieldTips">
</el-date-picker>
</template>
<template v-if="item.type === 'onOff'">
<el-switch active-value="1" inactive-value="0" v-model="item.defaultValue"></el-switch>
</template>
<template v-if="['input', 'phone', 'name', 'idNumber'].includes(item.type)">
<el-input :disabled="item.disable === '1'" :maxlength="Number(item.maxLength)" size="small" :placeholder="item.fieldTips"
v-model="item.defaultValue"></el-input>
</template>
<template v-if="item.type === 'area'">
<ai-area-get
:instance="instance"
always-show/>
</template>
<template v-if="item.type === 'select'">
<el-select :disabled="item.disable === '1'" style="width: 100%;" size="small" :placeholder="item.fieldTips"
v-model="item.defaultValue">
<el-option
v-for="(filed, index) in item.options"
:key="index"
:label="filed.label"
:value="filed.label">
</el-option>
</el-select>
</template>
<template v-if="item.type === 'checkbox'">
<el-checkbox-group v-model="item.defaultValue" :disabled="item.disable === '1'">
<el-checkbox :label="field.label" v-for="(field, index) in item.options" :key="index">{{ field.label }}</el-checkbox>
</el-checkbox-group>
</template>
<template v-if="item.type === 'upload'">
<div class="left-item__item--upload">
<i class="iconfont iconAdd"></i>
<span>添加附件</span>
</div>
</template>
</el-form-item>
</div>
</draggable>
</template>
</ai-card>
</draggable>
</el-form>
</div>
</div>
</el-scrollbar>
<el-scrollbar class="right">
<div class="right-item" v-if="isGroup">
<div class="right-item__title no-solid">
<h2>分组名称</h2>
</div>
<div class="right-item__content">
<el-input placeholder="请输入分组名称" :maxlength="32" show-word-limit v-model="currTarget.groupName"></el-input>
</div>
</div>
<div class="layout-right__del" @click="removeGroup" v-if="isGroup && targetList.length > 1 && !currTarget.isInit">
<span>删除分组</span>
</div>
<template v-if="activeIndex > -1">
<div class="right-item">
<div class="right-item__title no-solid">
<h2>绑定字段</h2>
</div>
<div class="right-item__content">
<ai-select v-model="currTarget.prop" :selectList="propOps" @change="handleAutoFieldName"/>
</div>
</div>
</template>
<div class="right-item" v-if="['select', 'date', 'time', 'input', 'datetime', 'textarea', 'rtf'].includes(currTarget.type)">
<div class="right-item__title no-solid">
<h2>占位符</h2>
</div>
<div class="right-item__content">
<el-input placeholder="请输入占位符" :maxlength="32" show-word-limit v-model="currTarget.fieldTips"></el-input>
</div>
</div>
<div class="right-item right-item__select" v-if="['select', 'checkbox', 'radio'].includes(currTarget.type)">
<div class="right-item__title no-solid">
<h2>选项设置</h2>
</div>
<div class="right-item__select--wrapper">
<draggable
v-model="currTarget.options"
:animation="340"
group="select"
handle=".mover"
:sort="true">
<div class="select-item" v-for="(item, index) in currTarget.options" :key="index">
<i class="iconfont iconjdq_led_show mover"></i>
<el-input placeholder="请输入选项名" :maxlength="30" show-word-limit v-model="item.label"></el-input>
<i class="iconfont iconDelete" @click="removeOptions(index)"></i>
</div>
</draggable>
</div>
<el-button type="text" class="add-select" @click="addOptions">添加选项</el-button>
</div>
<div class="right-item__group" v-if="activeIndex > -1" key="radio">
<div class="right-item" v-if="['select', 'radio'].includes(currTarget.type)">
<div class="right-item__title">
<h2>默认值</h2>
</div>
<div class="right-item__content">
<el-select placeholder="请选择默认值" style="width: 100%;" clearable v-model="currTarget.defaultValue">
<el-option
v-for="(filed, index) in currTarget.options"
:key="index"
:label="filed.label"
:value="filed.label">
</el-option>
</el-select>
</div>
</div>
<div class="right-item" v-if="['checkbox'].includes(currTarget.type)" key="checkbox">
<div class="right-item__title">
<h2>默认值</h2>
</div>
<div class="right-item__content">
<el-select placeholder="请选择默认值" style="width: 100%;" clearable collapse-tags :multiple="true" v-model="currTarget.defaultValue">
<el-option
v-for="(filed, index) in currTarget.options"
:key="index"
:label="filed.label"
:value="filed.label">
</el-option>
</el-select>
</div>
</div>
<div class="right-item__group">
<div class="right-item" v-if="['onOff'].includes(currTarget.type)">
<div class="right-item__title">
<h2>默认值</h2>
</div>
<div class="right-item__content">
<el-switch v-model="currTarget.defaultValue" active-value="1" inactive-value="0" active-text="是" inactive-text="否"></el-switch>
</div>
</div>
</div>
<div class="right-item__group">
<div class="right-item" v-if="['number'].includes(currTarget.type)">
<div class="right-item__title">
<h2>默认值</h2>
</div>
<div class="right-item__content">
<el-input-number style="width: 100%;" v-model="currTarget.defaultValue" label="默认值"></el-input-number>
</div>
</div>
</div>
<div class="right-item__group">
<div class="right-item" v-if="['number'].includes(currTarget.type)">
<div class="right-item__title">
<h2>最大值</h2>
</div>
<div class="right-item__content">
<el-input-number style="width: 100%;" v-model="currTarget.maxValue" label="最大值"></el-input-number>
</div>
</div>
</div>
<div class="right-item__group">
<div class="right-item" v-if="['number'].includes(currTarget.type)">
<div class="right-item__title">
<h2>最小值</h2>
</div>
<div class="right-item__content">
<el-input-number style="width: 100%;" v-model="currTarget.minValue" label="最小值"></el-input-number>
</div>
</div>
</div>
<div class="right-item__group">
<div class="right-item" v-if="['number'].includes(currTarget.type)">
<div class="right-item__title">
<h2>小数精度</h2>
</div>
<div class="right-item__content">
<el-input-number style="width: 100%;" step-strictly :min="0" v-model="currTarget.decimalPlaces" label="小数精度"></el-input-number>
</div>
</div>
</div>
<div class="right-item" v-if="['input', 'textarea', 'phone'].includes(currTarget.type)">
<div class="right-item__title">
<h2>最多输入字符</h2>
</div>
<div class="right-item__content">
<el-input placeholder="字符个数" v-model="currTarget.maxLength">
<span slot="append"></span>
</el-input>
</div>
</div>
<div class="right-item" v-if="['area'].includes(currTarget.type)">
<div class="right-item__title">
<h2>默认值</h2>
</div>
<div class="right-item__content">
<ai-area-get v-model="currTarget.defaultValue" :instance="instance" always-show></ai-area-get>
</div>
</div>
<div class="right-item" v-if="currTarget.type === 'upload'">
<div class="right-item__title">
<h2>文件大小MB</h2>
</div>
<div class="right-item__content">
<el-input-number style="width: 100%;" v-model="currTarget.fileChoseSize" :min="1" :max="40" label="文件大小"></el-input-number>
</div>
</div>
<div class="right-item" v-if="currTarget.type === 'upload'">
<div class="right-item__title">
<h2>最大上传数</h2>
</div>
<div class="right-item__content">
<el-input-number style="width: 100%;" step-strictly v-model="currTarget.fileMaxCount" :min="1" :max="20" label="最大上传数"></el-input-number>
</div>
</div>
<div class="right-item">
<div class="right-item__title">
<h2>是否整行显示</h2>
<el-switch v-model="currTarget.grid" :active-value="1" :inactive-value="0.5"></el-switch>
</div>
</div>
<div class="right-item" v-if="currTarget.type === 'textarea'">
<div class="right-item__title">
<h2>输入框行高</h2>
</div>
<div class="right-item__content">
<el-input-number style="width: 100%;" v-model="currTarget.lineNumber" :min="3" :max="10" label="输入框行高"></el-input-number>
</div>
</div>
<div class="right-item">
<div class="right-item__title no-solid">
<div class="right-item__title--left">
<h2>是否必填</h2>
</div>
<el-switch v-model="currTarget.mustFill" active-value="1" inactive-value="0"></el-switch>
</div>
</div>
<div class="right-item" v-if="currTarget.type !== 'rtf'">
<div class="right-item__title no-solid">
<div class="right-item__title--left">
<h2>是否禁用</h2>
</div>
<el-switch v-model="currTarget.disable" active-value="1" inactive-value="0"></el-switch>
</div>
</div>
</div>
</el-scrollbar>
</div>
</template>
<script>
import draggable from 'vuedraggable'
import options from './config'
export default {
name: 'detailLayout',
model: {
prop: 'value',
event: 'change',
},
props: {
instance: Function,
dict: Object,
form: {default: () => ({})},
value: Array
},
components: {draggable},
data() {
let {config: components} = options
return {
isGroup: false,
components,
groupIndex: -1,
activeIndex: -1,
currTarget: {},
targetList: []
}
},
watch: {
activeIndex() {
if (this.activeIndex > -1 && this.groupIndex > -1 && !this.isGroup) {
const filed = this.targetList[this.groupIndex].column[this.activeIndex]
this.currTarget = filed
if (filed.type === 'checkbox' && !filed.defaultValue.length) {
setTimeout(() => {
this.$set(this.currTarget, 'defaultValue', [2])
this.$forceUpdate()
this.$set(this.currTarget, 'defaultValue', [])
}, 100)
}
} else if (this.groupIndex > -1 && this.isGroup) {
this.currTarget = this.targetList[this.groupIndex]
} else this.currTarget = {}
},
groupIndex() {
if (this.activeIndex > -1 && this.groupIndex > -1 && !this.isGroup) {
const filed = this.targetList[this.groupIndex].column[this.activeIndex]
this.currTarget = filed
this.$set(this.currTarget, 'defaultValue', filed.defaultValue)
if (filed.type === 'checkbox' && !filed.defaultValue.length) {
this.$nextTick(() => {
this.$set(this.currTarget, 'defaultValue', [])
this.$nextTick(() => {
this.$forceUpdate()
})
})
}
} else if (this.groupIndex > -1 && this.isGroup) {
this.currTarget = this.targetList[this.groupIndex]
} else this.currTarget = {}
},
targetList: {
deep: true,
handler(v) {
this.$emit('change', v)
}
}
},
computed: {
currComponentTitle() {
return '表单设置'
},
propOps: v => v.form.props?.map(e => ({dictValue: e.prop, dictName: e.label})),
},
methods: {
initValue() {
let unwatch = this.$watch('value', (v) => {
if (this.targetList.length > 0) unwatch && unwatch()
else if (!!v) {
this.targetList = this.$copy(v)
unwatch && unwatch()
}
}, {immediate: true})
},
removeItem(j, i) {
this.groupIndex = -1
this.activeIndex = -1
this.targetList[j].column.splice(i, 1)
},
removeGroup() {
if (this.targetList.length === 1) {
return this.$message.error('分组不能小于1')
}
this.targetList.splice(this.groupIndex, 1)
this.groupIndex = 0
this.isGroup = true
this.activeIndex = -1
},
isUnique(type) {
const list = this.components[0].children.map(v => v.type)
return list.indexOf(type) > -1
},
addOptions() {
const len = this.targetList[this.groupIndex].column[this.activeIndex].options.length
let label = `选项${len + 1}`
const index = this.targetList[this.groupIndex].column[this.activeIndex].options.findIndex(v => label === v.label)
if (index > -1) {
label = `新选项${len + 1}`
}
this.targetList[this.groupIndex].column[this.activeIndex].options.push({
label: label,
value: ''
})
},
removeOptions(index) {
const len = this.targetList[this.groupIndex].column[this.activeIndex].options.length
if (len === 2) {
return this.$message.error('选项不能少于2个')
}
this.targetList[this.groupIndex].column[this.activeIndex].options.splice(index, 1)
},
onEnd(e) {
const el = e.to.parentElement.parentElement
this.isGroup = false
this.activeIndex = e.newIndex
this.groupIndex = Number(el.getAttribute('data-index'))
},
onElEnd(e) {
if (this.isGroup) {
this.groupIndex = e.newIndex
} else {
this.activeIndex = e.newIndex
}
},
clone(e) {
if (e.type === 'group') {
this.targetList.push(JSON.parse(JSON.stringify(e)))
this.$nextTick(() => {
this.isGroup = true
this.groupIndex = this.targetList.length - 1
this.activeIndex = -1
})
return
}
const list = []
this.targetList.map(v => {
v.column.forEach(item => {
list.push(item.type)
})
})
if (list.indexOf(e.type) > -1 && ['name', 'idNumber', 'phone', 'area'].includes(e.type)) {
return this.$message.error('信息组件不能重复添加')
}
if (this.isGroup) {
this.targetList[this.groupIndex].column.push(JSON.parse(JSON.stringify(e)))
} else {
if (this.targetList[0]) this.targetList[0]?.column.push(JSON.parse(JSON.stringify(e)))
else return this.$message.error("请先添加分组!")
}
this.$nextTick(() => {
this.groupIndex = this.isGroup ? this.groupIndex : 0
this.activeIndex = this.targetList[0].column.length - 1
})
},
onMove(e) {
const el = e.draggedContext.element
if (el.type === 'group') {
return false
}
const list = []
this.targetList.map(v => {
v.column.forEach(item => {
list.push(item.type)
})
})
return !(list.indexOf(el.type) > -1 && ['name', 'idNumber', 'phone', 'area'].includes(el.type));
},
cloneComponent(e) {
if (e.type === 'group') {
this.targetList.push(JSON.parse(JSON.stringify(e)))
return
}
const list = []
this.targetList.map(v => {
v.column.forEach(item => {
list.push(item.type)
})
})
if (list.indexOf(e.type) > -1 && ['name', 'idNumber', 'phone', 'area'].includes(e.type)) {
this.$message.error('信息组件不能重复添加')
}
return JSON.parse(JSON.stringify(e))
},
handleAutoFieldName(v) {
this.currTarget.fieldName = this.propOps.find(e => e.dictValue == v)?.dictName || this.currTarget.fieldName
}
},
created() {
this.initValue()
}
}
</script>
<style lang="scss" scoped>
.layout-right__del {
position: absolute;
bottom: 100px;
left: 0;
z-index: 11;
width: 100%;
padding: 0 16px;
span {
display: block;
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
border-radius: 6px;
color: #F46;
cursor: pointer;
border: 1px solid #F46;
&:hover {
opacity: 0.7;
}
}
}
.detailLayout {
display: flex;
.ai-form .el-form-item {
margin-bottom: 0;
}
.right-item__maintitle {
height: 62px;
line-height: 62px;
margin-bottom: 20px;
border-bottom: 1px solid #EEEEEE;
color: #222222;
h2 {
font-size: 14px;
}
}
::v-deep .ai-card {
cursor: move;
&.active {
background: #f6f7ff;
}
}
::v-deep .ai-detail__content {
height: calc(100% - 52px) !important;
padding: 0 !important;
overflow: hidden !important;
}
.ai-dialog__success {
::v-deep .ai-dialog__content {
max-height: initial !important;
}
}
.middle-draggable {
// display: flex;
// justify-content: space-between;
// flex-wrap: wrap;
.left-item__item {
width: 50%;
min-height: 73px;
}
.el-date-editor.el-input {
width: 100%;
}
& > span {
display: block;
width: 100%;
height: 100%;
min-height: 600px;
padding-bottom: 20px;
}
.components-item {
position: relative;
margin-bottom: 16px;
padding: 16px 16px;
cursor: move;
&::after {
position: absolute;
left: 0;
top: 0;
z-index: 1111;
width: 100%;
height: 100%;
content: ' ';
}
.left-item__item--remove {
display: flex;
position: absolute;
align-items: center;
justify-content: center;
right: 4px;
top: 4px;
z-index: 1113;
width: 84px;
height: 28px;
background: #FF4466;
border-radius: 2px;
cursor: pointer;
color: #fff;
font-size: 12px;
i {
margin-right: 6px;
font-size: 12px;
&:hover {
color: #fff;
}
}
&:hover {
opacity: 0.8;
}
}
&:hover {
background: #f6f7ff;
}
&.active {
background: #f6f7ff;
}
.left-item__item--upload {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 120px;
height: 120px;
line-height: 1;
border-radius: 6px;
border: 1px dashed #bbb;
i {
font-size: 24px;
color: #8899bb;
}
span {
margin-top: 10px;
font-size: 12px;
color: #555;
}
}
.text-item {
input {
display: block;
width: 100%;
height: 40px;
border: none;
border-bottom: 1px solid #ddd;
&:focus {
outline: none;
}
&:disabled {
background: #fff;
}
}
}
.textarea-item {
textarea {
width: 100%;
height: 120px;
resize: none;
border: 1px solid #ddd;
padding: 10px;
&:focus {
outline: none;
}
&:disabled {
background: #fff;
}
}
}
.radio-item {
display: flex;
align-items: center;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
label {
margin-left: 10px;
}
img {
margin-left: 10px;
}
}
}
.left-item__item--title {
display: flex;
align-items: center;
margin-bottom: 10px;
i {
margin-right: 5px;
color: #E22120;
}
h2 {
color: #333333;
font-size: 15px;
}
}
}
.middle-content {
width: 96%;
margin: 0 auto;
padding: 0px 0 1px;
.middle-content__wrapper {
// min-height: 800px;
// background: #fff;
& > div {
&.active {
background: #f6f7ff;
}
}
.radio-item {
img {
width: 40px;
height: 40px;
}
}
}
}
div {
box-sizing: border-box;
}
.right-item__select--wrapper {
.select-item {
display: flex;
align-items: center;
}
}
::v-deep .ai-detail__title {
margin: 0 !important;
margin-bottom: 4px !important;
padding: 0 20px;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.08);
}
::v-deep .ai-detail__content--wrapper {
display: flex;
max-width: 100% !important;
height: 100% !important;
padding: 0 !important;
background: #F5F6F9;
}
.middle {
flex: 1;
height: 100%;
::v-deep .el-scrollbar__view {
height: 100%;
}
}
.add-select {
height: auto;
line-height: 1;
margin: 10px 0 0 26px;
padding: 0;
}
.right-item__select--wrapper {
.select-item {
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
i {
margin-right: 8px;
color: #8c9dbd;
}
.mover {
cursor: move;
}
.iconDelete {
cursor: pointer;
margin-left: 10px;
}
}
::v-deep .el-upload-list__item {
width: 40px !important;
height: 40px !important;
object-fit: cover;
}
.config-item__select {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border: 1px solid #D0D4DC;
&:hover {
opacity: 0.7;
}
i {
font-size: 18px;
}
}
}
.right {
width: 320px;
height: 100%;
overflow: hidden;
background: #FFFFFF;
.el-checkbox {
display: block;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
.right-item {
margin: 20px 0;
padding: 0 20px;
.right-item__tips {
margin-top: 10px;
color: #888888;
font-size: 12px;
}
}
.right-item__title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
.right-item__title--left {
display: flex;
align-items: center;
i {
color: #888888;
font-size: 12px;
font-style: normal;
}
}
h2 {
color: #222222;
font-size: 14px;
}
}
}
.left {
width: 280px;
height: 100%;
overflow: hidden;
background: #FFFFFF;
.left-item {
padding: 0 20px;
&:last-child {
padding-bottom: 20px;
}
.left-item__title {
display: flex;
align-items: baseline;
margin-bottom: 20px;
h2 {
color: #222222;
font-size: 14px;
font-weight: 700;
}
span {
color: #888888;
font-size: 12px;
}
}
}
.left-item {
margin-top: 20px;
}
}
}
.left-item__item {
display: flex;
align-items: center;
height: 40px;
margin-bottom: 10px;
width: 100%;
padding: 0 13px;
background: #FFFFFF;
border-radius: 2px;
color: #222222;
font-size: 12px;
border: 1px solid #E4E8EF;
cursor: move;
&:hover {
border: 1px dashed #2367ff;
color: #2367ff;
i {
color: #2367ff;
}
}
i {
margin-right: 13px;
font-size: 14px;
color: #8899BB;
}
&:last-child {
margin-bottom: 0px;
}
}
</style>