版本迭代
This commit is contained in:
333
src/components/AiArticle.vue
Normal file
333
src/components/AiArticle.vue
Normal file
@@ -0,0 +1,333 @@
|
||||
<template>
|
||||
<div class="ai-article">
|
||||
<div v-html="value"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiArticle',
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: String
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-article {
|
||||
width: 100%;
|
||||
line-height: 1.75;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
text-align: justify;
|
||||
overflow-x: auto;
|
||||
word-break: break-word;
|
||||
|
||||
:deep( h1 ){
|
||||
margin: 1.3rem 0;
|
||||
line-height: 1.2
|
||||
}
|
||||
|
||||
:deep( p ){
|
||||
line-height: 2.27rem
|
||||
}
|
||||
|
||||
:deep( hr ){
|
||||
border: none;
|
||||
border-top: 1px solid #ddd;
|
||||
margin-top: 2.7rem;
|
||||
margin-bottom: 2.7rem
|
||||
}
|
||||
|
||||
:deep( img:not(.equation)), :deep( iframe), :deep( embed), :deep( video ){
|
||||
display: block;
|
||||
margin: 18px auto;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
:deep( img.equation ){
|
||||
margin: 0 .1em;
|
||||
max-width: 100% !important;
|
||||
vertical-align: middle
|
||||
}
|
||||
|
||||
:deep( figure ){
|
||||
margin: 2.7rem auto;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
:deep( img:not(.equation) ){
|
||||
cursor: zoom-in
|
||||
}
|
||||
|
||||
:deep( figure figcaption ){
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
line-height: 2.7rem;
|
||||
color: #909090
|
||||
}
|
||||
|
||||
:deep( pre ){
|
||||
line-height: 1.93rem;
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
:deep( code),
|
||||
:deep( pre ){
|
||||
font-family: Menlo, Monaco, Consolas, Courier New, monospace
|
||||
}
|
||||
|
||||
:deep( code ){
|
||||
font-size: 1rem;
|
||||
padding: .26rem .53em;
|
||||
word-break: break-word;
|
||||
color: #4e5980;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 2px;
|
||||
overflow-x: auto
|
||||
}
|
||||
|
||||
:deep( pre>code ){
|
||||
font-size: 1rem;
|
||||
padding: .67rem 1.3rem;
|
||||
margin: 0;
|
||||
word-break: normal;
|
||||
display: block
|
||||
}
|
||||
|
||||
:deep( a ){
|
||||
color: #259
|
||||
}
|
||||
|
||||
:deep( a:active),
|
||||
:deep( a:hover ){
|
||||
color: #275b8c
|
||||
}
|
||||
|
||||
:deep( table ){
|
||||
width: 100%;
|
||||
margin-top: 18px;
|
||||
margin-bottom: 18px;
|
||||
overflow: auto;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
border-top: 1px solid #ccc;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
:deep( thead ){
|
||||
background: #f6f6f6;
|
||||
color: #000;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
:deep( td),
|
||||
:deep( th ){
|
||||
padding: 3px 5px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
:deep( td ){
|
||||
min-width: 10rem
|
||||
}
|
||||
|
||||
:deep( blockquote ){
|
||||
margin: 1em 0;
|
||||
border-left: 4px solid #ddd;
|
||||
padding: 0 1.3rem
|
||||
}
|
||||
|
||||
:deep( blockquote>p ){
|
||||
margin: .6rem 0
|
||||
}
|
||||
|
||||
:deep( ol),
|
||||
:deep( ul ){
|
||||
padding-left: 2.7rem
|
||||
}
|
||||
|
||||
:deep( ol li),
|
||||
:deep( ul li ){
|
||||
margin-bottom: .6rem
|
||||
}
|
||||
|
||||
:deep( ol ol),
|
||||
:deep( ol ul),
|
||||
:deep( ul ol),
|
||||
:deep( ul ul ){
|
||||
margin-top: .27rem
|
||||
}
|
||||
|
||||
:deep( pre>code ){
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
color: #333;
|
||||
background: #f8f8f8
|
||||
}
|
||||
|
||||
:deep( p ){
|
||||
line-height: inherit;
|
||||
margin-top: 18px;
|
||||
margin-bottom: 18px
|
||||
}
|
||||
:deep( p:first-child){
|
||||
margin-top: 0!important;
|
||||
}
|
||||
:deep( img ){
|
||||
max-height: none
|
||||
}
|
||||
|
||||
:deep( a ){
|
||||
color: #0269c8;
|
||||
border-bottom: 1px solid #d1e9ff
|
||||
}
|
||||
|
||||
:deep( code ){
|
||||
background-color: #fff5f5;
|
||||
color: #ff502c;
|
||||
font-size: .87em;
|
||||
padding: .065em .4em
|
||||
}
|
||||
|
||||
:deep( blockquote ){
|
||||
color: #666;
|
||||
padding: 1px 23px;
|
||||
margin: 18px 0;
|
||||
border-left: 4px solid #cbcbcb;
|
||||
background-color: #f8f8f8
|
||||
}
|
||||
|
||||
:deep( blockquote:after ){
|
||||
display: block;
|
||||
content: ""
|
||||
}
|
||||
|
||||
:deep( blockquote>p ){
|
||||
margin: 10px 0
|
||||
}
|
||||
|
||||
:deep( blockquote.warning ){
|
||||
position: relative;
|
||||
border-left-color: #f75151;
|
||||
margin-left: 8px
|
||||
}
|
||||
|
||||
:deep( blockquote.warning:before ){
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: -12px;
|
||||
background: #f75151;
|
||||
border-radius: 50%;
|
||||
content: "!";
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center
|
||||
}
|
||||
|
||||
:deep( ol),
|
||||
:deep( ul ){
|
||||
padding-left: 28px
|
||||
}
|
||||
|
||||
:deep( ol li),
|
||||
:deep( ul li ){
|
||||
margin-bottom: 0;
|
||||
list-style: inherit
|
||||
}
|
||||
|
||||
:deep( ol li.task-list-item),
|
||||
:deep( ul li.task-list-item ){
|
||||
list-style: none
|
||||
}
|
||||
|
||||
:deep( ol li.task-list-item ol),
|
||||
:deep( ol li.task-list-item ul),
|
||||
:deep( ul li.task-list-item ol),
|
||||
:deep( ul li.task-list-item ul ){
|
||||
margin-top: 0
|
||||
}
|
||||
|
||||
:deep( ol li ){
|
||||
padding-left: 6px
|
||||
}
|
||||
|
||||
:deep( pre ){
|
||||
position: relative;
|
||||
line-height: 1.75
|
||||
}
|
||||
|
||||
:deep( pre>code ){
|
||||
padding: 15px 12px
|
||||
}
|
||||
|
||||
:deep( pre>code.hljs[lang] ){
|
||||
padding: 18px 15px 12px
|
||||
}
|
||||
|
||||
:deep( h1),
|
||||
:deep( h2),
|
||||
:deep( h3),
|
||||
:deep( h4),
|
||||
:deep( h5),
|
||||
:deep( h6 ){
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
margin-top: 35px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep( h1 ){
|
||||
font-size: 30px;
|
||||
margin-bottom: 5px
|
||||
}
|
||||
|
||||
:deep( h2 ){
|
||||
padding-bottom: 12px;
|
||||
font-size: 24px;
|
||||
border-bottom: 1px solid #ececec
|
||||
}
|
||||
|
||||
:deep( h3 ){
|
||||
font-size: 18px;
|
||||
padding-bottom: 0
|
||||
}
|
||||
|
||||
:deep( h4 ){
|
||||
font-size: 16px
|
||||
}
|
||||
|
||||
:deep( h5 ){
|
||||
font-size: 15px
|
||||
}
|
||||
|
||||
:deep( h6 ){
|
||||
margin-top: 5px
|
||||
}
|
||||
|
||||
:deep( h1.heading+h2.heading ){
|
||||
margin-top: 20px
|
||||
}
|
||||
|
||||
:deep( h1.heading+h3.heading ){
|
||||
margin-top: 15px
|
||||
}
|
||||
|
||||
:deep( .heading+.heading ){
|
||||
margin-top: 0
|
||||
}
|
||||
|
||||
:deep( h1+:not(.heading) ){
|
||||
margin-top: 25px
|
||||
}
|
||||
}
|
||||
</style>
|
||||
114
src/components/AiBar.vue
Normal file
114
src/components/AiBar.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="aibar" :style="{ marginBottom }" :class="{'aibar-center':titlePosition === 'center',card}">
|
||||
<div v-if="titlePosition === 'center'"/>
|
||||
<div class="aibar-left" :class="[titlePosition === 'center' ? 'aibar-left__center' : '']">
|
||||
<template v-if="!$slots.title">{{ title }}</template>
|
||||
<slot name="title" v-else></slot>
|
||||
</div>
|
||||
<div class="aibar-right">
|
||||
<slot v-if="$slots.right" name="right"/>
|
||||
<slot v-else/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiBar',
|
||||
|
||||
props: {
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
|
||||
customCliker: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
marginBottom: {
|
||||
type: String,
|
||||
default: '16px'
|
||||
},
|
||||
|
||||
titlePosition: {
|
||||
type: String,
|
||||
default: 'left'
|
||||
},
|
||||
card: Boolean
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.aibar {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 56px;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
|
||||
|
||||
&.card {
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
}
|
||||
|
||||
.aibar-left {
|
||||
color: #222;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.aibar-left__center {
|
||||
position: relative;
|
||||
width: 556px;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.aibar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #5088FF;
|
||||
font-size: 12px;
|
||||
|
||||
i {
|
||||
line-height: 1;
|
||||
color: #5088FF;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
& > div, & > a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aibar-center {
|
||||
height: auto;
|
||||
padding: 10px 0;
|
||||
|
||||
h2 {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
64
src/components/AiCard.vue
Normal file
64
src/components/AiCard.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<section class="ai-card" :class="{panel,headerPanel}">
|
||||
<ai-bar v-if="!hideHeader" :title="title" v-bind="$attrs">
|
||||
<template #title>
|
||||
<slot name="title"></slot>
|
||||
</template>
|
||||
<template #right>
|
||||
<slot name="right"></slot>
|
||||
</template>
|
||||
</ai-bar>
|
||||
<div class="ai-card__body" :style="{paddingTop: hideHeader ? '20px' : 0}">
|
||||
<slot v-if="$scopedSlots.content" name="content"/>
|
||||
<slot v-else/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiCard',
|
||||
props: {
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
hideTitle: Boolean,
|
||||
panel: Boolean,
|
||||
headerPanel: Boolean
|
||||
},
|
||||
computed: {
|
||||
hideHeader: v => v.hideTitle || (v.panel && !v.title)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.ai-card {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
background: #FFFFFF;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
border-radius: 2px;
|
||||
|
||||
.ai-card__body {
|
||||
height: calc(100% - 72px);
|
||||
padding: 0 20px 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&.panel, &.headerPanel {
|
||||
margin-bottom: 0;
|
||||
|
||||
.ai-card__body {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.aibar {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
131
src/components/AiDetail.vue
Normal file
131
src/components/AiDetail.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="ai-detail">
|
||||
<div class="ai-detail__title">
|
||||
<slot name="title"></slot>
|
||||
</div>
|
||||
<div class="ai-detail__step" v-if="isHasStepSlot">
|
||||
<slot name="step"></slot>
|
||||
</div>
|
||||
<div class="ai-detail__content" :class="className">
|
||||
<div class="ai-detail__content--wrapper" :class="{'ai-detail__content--side':isHasSidebar,list}">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-detail__footer" v-if="isHasFooterSlot">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiDetail',
|
||||
|
||||
props: {
|
||||
isHasSidebar: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
list: Boolean
|
||||
},
|
||||
|
||||
computed: {
|
||||
isHasFooterSlot() {
|
||||
return this.$slots.footer
|
||||
},
|
||||
|
||||
isHasStepSlot() {
|
||||
return this.$slots.step
|
||||
},
|
||||
|
||||
className() {
|
||||
if (this.isHasFooterSlot) {
|
||||
if (this.isHasStepSlot) {
|
||||
return 'ai-detail__content--active-step'
|
||||
}
|
||||
|
||||
return 'ai-detail__content--active'
|
||||
} else {
|
||||
if (this.isHasStepSlot) {
|
||||
return 'ai-detail__content--step'
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-detail {
|
||||
height: 100%;
|
||||
background: #F5F6F9;
|
||||
overflow: hidden;
|
||||
|
||||
.ai-detail__title {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.ai-detail__step {
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.ai-detail__content {
|
||||
height: calc(100% - 48px);
|
||||
padding: 0 0 20px 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: overlay;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1740px) {
|
||||
.ai-detail__content {
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-detail__content--wrapper {
|
||||
position: relative;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding-top: 20px;
|
||||
|
||||
&.list {
|
||||
max-width: unset;
|
||||
margin: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1740px) {
|
||||
.ai-detail__content--side {
|
||||
margin-left: 128px;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-detail__content--active {
|
||||
height: calc(100% - 48px - 64px);
|
||||
}
|
||||
|
||||
.ai-detail__content--active-step {
|
||||
height: calc(100% - 48px - 64px - 72px);
|
||||
}
|
||||
|
||||
.ai-detail__content--step {
|
||||
height: calc(100% - 48px - 72px);
|
||||
}
|
||||
|
||||
.ai-detail__footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
background: #F5F5F5;
|
||||
box-shadow: 0px 1px 0px 0px #E5E5E5;
|
||||
|
||||
.el-button {
|
||||
width: 92px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
121
src/components/AiDialog.vue
Normal file
121
src/components/AiDialog.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<section class="ai-dialog__wrapper">
|
||||
<el-dialog custom-class="ai-dialog" v-on="$listeners" v-bind="$attrs" :visible.sync="dialog">
|
||||
<div class="ai-dialog__header fill" slot="title" v-text="title"/>
|
||||
<div class="ai-dialog__content">
|
||||
<div class="ai-dialog__content--wrapper pad-r8">
|
||||
<slot/>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="customFooter" slot="footer">
|
||||
<slot name="footer"/>
|
||||
</template>
|
||||
<div v-else class="dialog-footer" slot="footer">
|
||||
<el-button @click="onCancel">取消</el-button>
|
||||
<el-button @click="onConfirm" type="primary">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'AiDialog',
|
||||
model: {
|
||||
prop: "visible",
|
||||
event: "input"
|
||||
},
|
||||
props: {
|
||||
visible: Boolean,
|
||||
title: {type: String, default: ''},
|
||||
customFooter: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dialog(v) {
|
||||
this.visible != v && this.$emit("input", v)
|
||||
},
|
||||
visible(v) {
|
||||
this.dialog = v
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onCancel() {
|
||||
this.$emit('input', false)
|
||||
this.$emit('update:visible', false)
|
||||
this.$emit('cancel')
|
||||
},
|
||||
onConfirm() {
|
||||
this.$emit('confirm')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.ai-dialog {
|
||||
margin: unset !important;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 20px 40px 20px;
|
||||
}
|
||||
|
||||
.ai-dialog__content {
|
||||
overflow-y: auto;
|
||||
padding-bottom: 4px;
|
||||
max-height: 500px;
|
||||
|
||||
.ai-dialog__content--wrapper {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-dialog__header {
|
||||
flex: 1;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
padding: 0 16px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 16px 20px;
|
||||
box-sizing: border-box;
|
||||
background: #F3F6F9;
|
||||
text-align: center;
|
||||
|
||||
& + .el-button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
width: 92px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog__header {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
top: unset;
|
||||
right: unset;
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
34
src/components/AiEmpty.vue
Normal file
34
src/components/AiEmpty.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="ai-empty">
|
||||
<div class="ai-empty__bg"></div>
|
||||
<template v-if="!isHasTitleSlot">暂无数据</template>
|
||||
<slot v-else></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiEmpty',
|
||||
|
||||
computed: {
|
||||
isHasTitleSlot () {
|
||||
return this.$slots.default
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.ai-empty {
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
color: #acaaad;
|
||||
|
||||
.ai-empty__bg {
|
||||
background: url("../assets/images/empty.svg") no-repeat center;
|
||||
background-size: 120px 120px;
|
||||
height: 120px;
|
||||
margin: 48px auto 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
91
src/components/AiInfoItem.vue
Normal file
91
src/components/AiInfoItem.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="ai-info-item" :style="contentStyle">
|
||||
<label class="ai-info-item__left" :style="contentLabelStyle">
|
||||
<template v-if="$slots.label">
|
||||
<slot name="label"/>
|
||||
</template>
|
||||
<template v-else>{{ label }}</template>
|
||||
</label>
|
||||
<div class="ai-info-item__right">
|
||||
<slot v-if="$scopedSlots.default"/>
|
||||
<template v-else-if="!!openType">
|
||||
<ai-open-data :type="openType" :openid="value"/>
|
||||
</template>
|
||||
<template v-else>{{ value || value === 0 ? value : '-' }}</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiInfoItem',
|
||||
|
||||
inject: ['AiWrapper'],
|
||||
|
||||
props: {
|
||||
label: {
|
||||
type: String
|
||||
},
|
||||
|
||||
value: {
|
||||
type: [String, Number]
|
||||
},
|
||||
|
||||
'label-width': {
|
||||
type: String
|
||||
},
|
||||
|
||||
isLine: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
openType: {default: ""}
|
||||
},
|
||||
|
||||
computed: {
|
||||
contentStyle() {
|
||||
let width = this.AiWrapper.autoWidth
|
||||
|
||||
if (this.isLine) {
|
||||
width = '100%'
|
||||
}
|
||||
|
||||
return {
|
||||
width
|
||||
}
|
||||
},
|
||||
|
||||
contentLabelStyle() {
|
||||
return {
|
||||
width: this.labelWidth || this.AiWrapper.autoLableWidth
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-info-item {
|
||||
display: flex;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 16px;
|
||||
|
||||
label {
|
||||
flex-shrink: 0;
|
||||
width: 96px;
|
||||
margin-right: 40px;
|
||||
text-align: right;
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ai-info-item__right {
|
||||
flex: 1;
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
263
src/components/AiList.vue
Normal file
263
src/components/AiList.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<div class="ai-list" :class="listClass">
|
||||
<div class="ai-list__title" v-if="$slots.title">
|
||||
<slot name="title"></slot>
|
||||
</div>
|
||||
<div class="ai-list__tabs" v-if="$slots.tabs">
|
||||
<slot name="tabs"></slot>
|
||||
</div>
|
||||
<el-row type="flex" class="ai-list__blank" v-else-if="$scopedSlots.blank" :class="{hasLeft:$scopedSlots.left}">
|
||||
<div class="ai-list__content--left" v-if="$scopedSlots.left">
|
||||
<slot name="left"/>
|
||||
</div>
|
||||
<div class="fill">
|
||||
<slot name="blank"/>
|
||||
</div>
|
||||
</el-row>
|
||||
<div class="ai-list__content" v-else :class="contentClass">
|
||||
<div class="ai-list__content--wrapper">
|
||||
<slot name="custom" v-if="!!$slots.custom"/>
|
||||
<template v-else>
|
||||
<div class="ai-list__content--left" v-if="$slots.left">
|
||||
<slot name="left"></slot>
|
||||
</div>
|
||||
<div class="ai-list__content--right" :style="{width: !$slots.left ? '100%' : 'auto' }">
|
||||
<div class="ai-list__content--right-wrapper" :style="{minHeight: $slots.left ? '100%' : 'auto' }">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiList',
|
||||
props: {
|
||||
isTabs: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
listClass() {
|
||||
if (this.$slots.left || this.$slots.tabs) {
|
||||
return 'ai-left__list'
|
||||
}
|
||||
|
||||
if (this.isTabs) {
|
||||
return 'ai-left__list ai-list__notab'
|
||||
}
|
||||
|
||||
if (!this.isTabs && !this.$slots.tabs && !this.$slots.left) {
|
||||
return 'ai-list__single'
|
||||
}
|
||||
|
||||
return ''
|
||||
},
|
||||
|
||||
contentClass() {
|
||||
if (this.isTabs) {
|
||||
return 'ai-list__tab--content'
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
//全局tab css
|
||||
:deep( .ai-list__tabs ) {
|
||||
margin-top: 0 !important;
|
||||
|
||||
.el-tabs__item {
|
||||
position: relative;
|
||||
width: auto !important;;
|
||||
padding: 0 16px !important;
|
||||
}
|
||||
|
||||
.el-tabs__item.is-active {
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.el-tabs__nav-wrap::after {
|
||||
height: 1px;
|
||||
background-color: #D8DCE3;
|
||||
}
|
||||
|
||||
.el-tabs__active-bar {
|
||||
// left: -16px;
|
||||
// width: 50%!important;
|
||||
}
|
||||
|
||||
.el-tabs__nav {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.el-tabs__header {
|
||||
padding: 0;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.el-tab-pane {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
overflow: hidden;
|
||||
background: #f3f6f9;
|
||||
|
||||
&.ai-list__single {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
|
||||
.ai-list__title {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.ai-list__content {
|
||||
flex: 1;
|
||||
padding: 20px 20px 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ai-list__content--wrapper {
|
||||
height: 100%;
|
||||
margin: 0 !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ai-list__content--right-wrapper {
|
||||
// margin: 0 20px;
|
||||
padding: 20px 20px !important;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.ai-list__tabs {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
:deep( .el-tabs__item ) {
|
||||
min-width: 80px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
color: #222;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid transparent !important;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__header ) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tabs ) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__content ) {
|
||||
height: calc(100% - 32px);
|
||||
background: #f3f6f9;
|
||||
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ai-list__content {
|
||||
.ai-list__content--wrapper {
|
||||
display: flex;
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-list__content--left {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
margin-right: 20px;
|
||||
padding-left: 1px;
|
||||
height: 100%;
|
||||
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
min-width: 264px;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.ai-list__content--right {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
|
||||
.ai-list__content--right-wrapper {
|
||||
padding: 12px 16px 12px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 4px 6px -2px rgba(15, 15, 21, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.ai-list__tab--content {
|
||||
height: 100% !important;
|
||||
|
||||
& > div {
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.ai-list__notab {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ai-list__blank {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
height: calc(100% - 50px);
|
||||
|
||||
|
||||
&.hasLeft {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
& > .fill {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ai-left__list {
|
||||
height: 100%;
|
||||
background: #f3f6f9;
|
||||
overflow: hidden;
|
||||
|
||||
.ai-list__content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.ai-list__content--wrapper {
|
||||
height: calc(100% - 32px);
|
||||
}
|
||||
|
||||
.ai-list__content--right {
|
||||
// margin-left: 11px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
76
src/components/AiPullDown.vue
Normal file
76
src/components/AiPullDown.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<section class="AiPullDown">
|
||||
<div class="line"/>
|
||||
<div class="down-content" @click="handleExpand">
|
||||
<i :class="expandIcon"/>
|
||||
<span>{{ btnText }}</span>
|
||||
</div>
|
||||
<div class="line"/>
|
||||
</section>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AiPullDown",
|
||||
props: {
|
||||
target: String,
|
||||
height: {default: 4},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expand: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleExpand() {
|
||||
this.expand = !this.expand
|
||||
if (this.target) {
|
||||
|
||||
} else this.$emit('change', this.expandStyle)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
btnText() {
|
||||
return this.expand ? '收起高级搜索' : '展开高级搜索'
|
||||
},
|
||||
expandStyle() {
|
||||
let initStyle = {overflow: 'hidden', height: `${this.height}px`}
|
||||
this.expand && (initStyle.height = "auto")
|
||||
return initStyle
|
||||
},
|
||||
expandIcon() {
|
||||
return this.expand ? 'iconfont iconDouble_Up' : 'iconfont iconDouble_Down'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$emit("change", this.expandStyle)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.AiPullDown {
|
||||
display: flex;
|
||||
|
||||
.line {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.down-content {
|
||||
cursor: pointer;
|
||||
padding: 0 8px;
|
||||
height: 24px;
|
||||
border-radius: 0 0 8px 8px;
|
||||
border: 1px solid #eee;
|
||||
border-top: 0;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
52
src/components/AiSearch.vue
Normal file
52
src/components/AiSearch.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<section class="AiSearch">
|
||||
<slot v-if="$slots.label" name="label"/>
|
||||
<span v-else>{{ label }}</span>
|
||||
<div class="content">
|
||||
<slot/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AiSearch",
|
||||
props: {
|
||||
label: {default: ""}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiSearch {
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
background: #F5F5F5;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
border: 1px solid #D0D4DC;
|
||||
border-right: none;
|
||||
padding: 0 8px;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep( .el-input__inner ){
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
* + * {
|
||||
.el-input__inner {
|
||||
margin-left: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
101
src/components/AiSearchBar.vue
Normal file
101
src/components/AiSearchBar.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<section>
|
||||
<div ref="AiSearchBarZone" class="AiSearchBar" :class="{bottomBorder}" :style="searchBarStyle">
|
||||
<div class="searchLeftZone">
|
||||
<slot name="left"/>
|
||||
</div>
|
||||
<div class="searchRightZone" ref="searchRightZone">
|
||||
<slot name="right"/>
|
||||
</div>
|
||||
</div>
|
||||
<ai-pull-down v-if="!isSingleRow" @change="handlePullDown" :height="rightHeight"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AiSearchBar",
|
||||
props: {
|
||||
bottomBorder: Boolean,
|
||||
size: {default: "small"}
|
||||
},
|
||||
computed: {
|
||||
isSingleRow() {
|
||||
return this.height <= 55
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
height: 0,
|
||||
rightHeight: 0,
|
||||
searchBarStyle: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePullDown(style) {
|
||||
this.searchBarStyle = style
|
||||
if (style.height == 'auto') {
|
||||
this.searchBarStyle.marginBottom = '16px'
|
||||
} else {
|
||||
this.searchBarStyle.marginBottom = '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.height = this.$refs?.AiSearchBarZone?.offsetHeight
|
||||
this.rightHeight = this.$refs?.searchRightZone?.offsetHeight
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiSearchBar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding-bottom: 12px;
|
||||
|
||||
&.bottomBorder {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
:deep(.searchLeftZone ){
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
:deep(.searchRightZone ){
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
align-self: flex-start;
|
||||
|
||||
.el-input {
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
* + button, * + div, * + section {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-input {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.AiPullDown {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
:deep( .searchLeftZone > .el-button), :deep( .searchRightZone > .el-button ){
|
||||
margin-left: 0;
|
||||
}
|
||||
</style>
|
||||
98
src/components/AiSidebar.vue
Normal file
98
src/components/AiSidebar.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="ai-sidebar">
|
||||
<div class="ai-sidebar__tab">
|
||||
<span
|
||||
:class="[currIndex === index ? 'ai-sidebar__tab--active' : '']"
|
||||
v-for="(item, index) in tabTitle"
|
||||
:key="index"
|
||||
@click="changeTab(index)">
|
||||
{{ item }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiSidebar',
|
||||
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: Number,
|
||||
require: true,
|
||||
default: 0
|
||||
},
|
||||
tabTitle: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value (v) {
|
||||
if (v >= 0 && this.currIndex !== v) {
|
||||
this.currIndex = v
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
currIndex: 0
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
if (this.value > 0) {
|
||||
this.currIndex = this.value
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
changeTab(index) {
|
||||
this.currIndex = index
|
||||
this.$emit('change', index)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-sidebar {
|
||||
position: fixed;
|
||||
margin-left: -20px;
|
||||
font-size: 14px;
|
||||
transform: translateX(-100%);
|
||||
|
||||
.ai-sidebar__tab {
|
||||
min-width: 88px;
|
||||
overflow: hidden;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding: 0 16px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
font-stretch: normal;
|
||||
letter-spacing: 0;
|
||||
color: #666666;
|
||||
cursor: pointer;
|
||||
border-right: 3px solid #D8DCE3;
|
||||
}
|
||||
|
||||
.ai-sidebar__tab--active {
|
||||
border-right: 3px solid #1FBAD6;
|
||||
color: #1FBAD6;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
400
src/components/AiTable.vue
Normal file
400
src/components/AiTable.vue
Normal file
@@ -0,0 +1,400 @@
|
||||
<template>
|
||||
<div class="ai-table" :class="[isShowBorder ? 'ai-table__border' : 'ai-table__noborder']">
|
||||
<el-table :data="tableData"
|
||||
header-cell-class-name="ai-table__header"
|
||||
cell-class-name="ai-table__cell"
|
||||
row-class-name="ai-table__row"
|
||||
:class="{'ai-header__border': isShowBorder}"
|
||||
:ref="refName"
|
||||
:size="tableSize"
|
||||
:stripe="stripe"
|
||||
:tooltip-effect="tooltipEffect"
|
||||
@selection-change="handleSelectionChange"
|
||||
v-on="$listeners"
|
||||
v-bind="$attrs"
|
||||
v-loading="loading">
|
||||
<template v-for="colConfig in colConfigs.filter(e=>!e.hide)">
|
||||
<slot v-if="colConfig.slot && colConfig.slot !== 'options'" :name="colConfig.slot"/>
|
||||
<component
|
||||
:key="colConfig.id"
|
||||
v-else-if="colConfig.component"
|
||||
:is="colConfig.component"
|
||||
:col-config="colConfig">
|
||||
</component>
|
||||
<el-table-column
|
||||
v-else-if="colConfig.dict"
|
||||
:key="colConfig.id"
|
||||
v-bind="colConfig">
|
||||
<span slot-scope="{row}" :style="{color:colConfig.color||dict.getColor(colConfig.dict, row[colConfig.prop])}">
|
||||
{{ $dict.getLabel(colConfig.dict, row[colConfig.prop]) }}
|
||||
</span>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-else-if="colConfig.openType"
|
||||
:key="colConfig.id"
|
||||
v-bind="colConfig">
|
||||
<template v-slot="{row}">
|
||||
<ai-open-data :type="colConfig.openType" :openid="row[colConfig.prop]"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-else-if="colConfig.type"
|
||||
:key="colConfig.id"
|
||||
v-bind="colConfig"
|
||||
:width="colConfig.width || 100"/>
|
||||
<el-table-column v-else v-bind="colConfig" :key="colConfig.id"
|
||||
:show-overflow-tooltip="colConfig['show-overflow-tooltip'] != false">
|
||||
<template slot-scope="scope">
|
||||
<render-slot v-if="colConfig.render" :render="colConfig.render" :row="scope.row" :index="scope.$index"
|
||||
:column="colConfig"/>
|
||||
<span v-else>{{ getValue(colConfig, scope.row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<slot class="table-options" name="options"/>
|
||||
<template #empty>
|
||||
<slot v-if="$scopedSlots.empty" name="empty"/>
|
||||
<AiEmpty v-else class="no-data" style="height:160px;"/>
|
||||
</template>
|
||||
</el-table>
|
||||
<div class="pagination newPagination" v-if="isShowPagination">
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="page.current"
|
||||
:total="page.total"
|
||||
:page-size="page.size"
|
||||
v-bind="$attrs"
|
||||
:page-sizes="pageSizes"
|
||||
:layout="layout"
|
||||
:pager-count="page.pagerCount"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleChange">
|
||||
<div class="paginationPre">
|
||||
<el-checkbox v-if="isHasPaginationBtnsSlot" :disabled="!tableData.length" :indeterminate="isIndeterminate"
|
||||
:value="checkAll" @click.native="toggleAllSelection">全选
|
||||
</el-checkbox>
|
||||
<slot name="pagination"/>
|
||||
<div class="pagination-btns">
|
||||
<slot name="paginationBtns"></slot>
|
||||
</div>
|
||||
<div class="paginationPre-total" :style="{marginLeft: isHasPaginationBtnsSlot ? '24px' : 0}">共<label class="color-primary">{{ page.total }}</label>条记录
|
||||
</div>
|
||||
</div>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'dayjs'
|
||||
|
||||
export default {
|
||||
name: 'AiTable',
|
||||
model: {
|
||||
prop: "tableData",
|
||||
event: "change"
|
||||
},
|
||||
props: {
|
||||
colConfigs: {type: Array, required: true},
|
||||
tableData: {type: Array, required: true, default: () => []},
|
||||
current: {default: 1},
|
||||
size: {default: 10},
|
||||
isShowPagination: {type: Boolean, default: true},
|
||||
total: Number,
|
||||
layout: {type: String, default: 'slot,->, prev, pager, next, sizes, jumper'},
|
||||
stripe: {type: Boolean, default: true},
|
||||
loading: {type: Boolean, default: false},
|
||||
tooltipEffect: String,
|
||||
tableSize: String,
|
||||
tableRef: String,
|
||||
pagerCount: {default: 5},
|
||||
pageSizes: {default: () => [10, 20, 50, 100]},
|
||||
pageConfig: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
chooseList: [],
|
||||
defaultPage: {}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
renderSlot: {
|
||||
functional: true,
|
||||
props: {
|
||||
row: Object,
|
||||
render: Function,
|
||||
index: Number,
|
||||
column: {type: Object, default: null},
|
||||
},
|
||||
render: (h, data) => {
|
||||
const params = {
|
||||
row: data.props.row,
|
||||
index: data.props.index
|
||||
}
|
||||
if (data.props.column) {
|
||||
params.column = data.props.column
|
||||
}
|
||||
return data.props.render(h, params)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
refName: v => v.tableRef || `aiTable${new Date().getTime()}`,
|
||||
isShowBorder: v => !!v.$attrs.border || v.$attrs.border === '',
|
||||
isHasPaginationBtnsSlot: v => v.$scopedSlots.paginationBtns,
|
||||
isIndeterminate: v => v.chooseList.length > 0 && v.chooseList.length < v.tableData.length,
|
||||
checkAll: v => v.chooseList.length == v.tableData.length && v.tableData !== 0,
|
||||
page() {
|
||||
return {
|
||||
current: this.current,
|
||||
size: this.size,
|
||||
total: this.total,
|
||||
pagerCount: this.pagerCount,
|
||||
...this.pageConfig
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChange(current) {
|
||||
this.$emit('update:current', current)
|
||||
this.$emit('update:pageConfig', {...this.pageConfig, current})
|
||||
this.$nextTick(() => {
|
||||
this.$emit('getList')
|
||||
})
|
||||
},
|
||||
handleSizeChange(size) {
|
||||
this.$emit('update:size', size)
|
||||
this.$emit('update:pageConfig', {...this.pageConfig, size})
|
||||
this.$nextTick(() => {
|
||||
this.$emit('getList')
|
||||
})
|
||||
},
|
||||
handleSelectionChange(e) {
|
||||
this.chooseList = e
|
||||
this.$emit('handleSelectionChange', e)
|
||||
},
|
||||
getValue(colConfig, row) {
|
||||
if (typeof colConfig.format === 'function') {
|
||||
return colConfig.format.call(this, row[colConfig.prop])
|
||||
}
|
||||
if (colConfig.dateFormat) {
|
||||
return moment(row[colConfig.prop]).format(colConfig.dateFormat)
|
||||
}
|
||||
|
||||
return row[colConfig.prop] === 0 || row[colConfig.prop] ? row[colConfig.prop] : '-'
|
||||
},
|
||||
/**
|
||||
* 表格方法代理
|
||||
*/
|
||||
clearSelection() {
|
||||
this.$refs[this.refName].clearSelection()
|
||||
},
|
||||
toggleRowSelection() {
|
||||
this.$refs[this.refName].toggleRowSelection(...arguments)
|
||||
},
|
||||
toggleAllSelection() {
|
||||
this.$refs[this.refName].toggleAllSelection()
|
||||
},
|
||||
toggleRowExpansion() {
|
||||
this.$refs[this.refName].toggleRowExpansion(...arguments)
|
||||
},
|
||||
setCurrentRow() {
|
||||
this.$refs[this.refName].setCurrentRow(...arguments)
|
||||
},
|
||||
clearSort() {
|
||||
this.$refs[this.refName].clearSort()
|
||||
},
|
||||
clearFilter() {
|
||||
this.$refs[this.refName].clearFilter(...arguments)
|
||||
},
|
||||
doLayout() {
|
||||
this.$refs[this.refName].doLayout()
|
||||
},
|
||||
sort() {
|
||||
this.$refs[this.refName].sort(...arguments)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-table {
|
||||
.color-primary {
|
||||
// color: $primaryColor;
|
||||
}
|
||||
:deep( .ai-header__border .ai-table__header ) {
|
||||
border-bottom: 1px solid #d0d4dc !important;
|
||||
border-right: 1px solid #d0d4dc !important;
|
||||
}
|
||||
|
||||
:deep( .el-table--border ) {
|
||||
border: 1px solid #d0d4dc;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
:deep( .el-table ) {
|
||||
color: #222;
|
||||
|
||||
.caret-wrapper {
|
||||
height: 24px;
|
||||
|
||||
.ascending {
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.descending {
|
||||
bottom: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
thead {
|
||||
color: #555
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .cell ) {
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
:deep( .el-table__header ) {
|
||||
th {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
tr {
|
||||
.cell {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
th:first-child {
|
||||
.cell {
|
||||
padding-left: 40px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .el-table__body ) {
|
||||
tr td:first-child .cell {
|
||||
padding-left: 40px !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .el-table__fixed-right ) {
|
||||
.el-table__body {
|
||||
tr td:first-child .cell {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep( .ai-table__header ) {
|
||||
border-bottom: none;
|
||||
background: #F3F4F5;
|
||||
}
|
||||
|
||||
:deep(.el-pager ) {
|
||||
li.active + li {
|
||||
border-left: 1px solid #d0d4dc;
|
||||
}
|
||||
}
|
||||
|
||||
.newPagination {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
padding: 0 40px !important;
|
||||
|
||||
.el-pagination {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.paginationPre {
|
||||
display: flex;
|
||||
height: 28px;
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
align-items: center;
|
||||
|
||||
.pagination-btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #1FBAD6 !important;
|
||||
|
||||
:deep( span), :deep( div ) {
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
color: #1FBAD6 !important;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.paginationPre-total {
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
|
||||
label {
|
||||
padding: 0 2px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
& > * + * {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
:deep( .el-pagination button), .el-pagination span:not([class*=suffix]) {
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox ) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.el-checkbox__input, .el-checkbox__inner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
min-width: 0 !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
.el-checkbox__label {
|
||||
font-size: 12px;
|
||||
color: #222222;
|
||||
height: auto !important;
|
||||
line-height: 1 !important;
|
||||
padding-left: 3px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ai-table__noborder {
|
||||
:deep( .el-table td), :deep( .el-table th.is-center ) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.el-table::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep( .el-table--striped .el-table__body tr.el-table__row--striped td ) {
|
||||
background: #F5F6F9;
|
||||
}
|
||||
|
||||
:deep( .el-table__fixed-right::before), :deep( .el-table__fixed::before ) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
140
src/components/AiTitle.vue
Normal file
140
src/components/AiTitle.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<section class="AiTitle" :class="{ 'bottomBorder': isShowBottomBorder, AiTitleSub: isHasSub}">
|
||||
<i class="iconfont iconBack_Large" v-if="isShowBack" @click="onBackBtnClick"/>
|
||||
<div class="fill">
|
||||
<div class="ailist-title">
|
||||
<div class="ailist-title__left">
|
||||
<h2>{{ title }}</h2>
|
||||
<p>{{ tips }}</p>
|
||||
</div>
|
||||
<div class="ailist-title__right">
|
||||
<div class="aititle-right__btns">
|
||||
<slot name="rightBtn"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subtitle" v-if="$scopedSlots.sub">
|
||||
<slot name="sub"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AiTitle',
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
tips: {
|
||||
type: String
|
||||
},
|
||||
instance: {
|
||||
type: Function
|
||||
},
|
||||
value: {
|
||||
type: String
|
||||
},
|
||||
isShowBack: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isShowBottomBorder: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isHasSub() {
|
||||
return this.$slots.sub
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onBackBtnClick() {
|
||||
this.closePage()
|
||||
this.$emit('onBackClick')
|
||||
this.$emit('back')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AiTitle {
|
||||
display: flex;
|
||||
|
||||
.ailist-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 48px;
|
||||
|
||||
.ailist-title__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
margin-left: 20px;
|
||||
font-size: 14px;
|
||||
color: #FA5555;
|
||||
}
|
||||
|
||||
& > i {
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #222;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.ailist-title__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-button ) {
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.AiTitleSub {
|
||||
height: auto;
|
||||
padding: 16px 0;
|
||||
|
||||
.ailist-title {
|
||||
height: auto;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&.bottomBorder {
|
||||
border-bottom: 1px solid #D8DCE3;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
width: 100%;
|
||||
color: #888888;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.iconBack_Large {
|
||||
line-height: 48px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
86
src/components/AiWrapper.vue
Normal file
86
src/components/AiWrapper.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="ai-wrapper">
|
||||
<ai-title class="w100" v-if="title" :title="title"/>
|
||||
<div class="ai-wrapper-content" :class="{border}">
|
||||
<slot/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AiTitle from "./AiTitle";
|
||||
|
||||
export default {
|
||||
name: 'AiWrapper',
|
||||
components: {AiTitle},
|
||||
componentName: 'AiWrapper',
|
||||
|
||||
provide() {
|
||||
return {
|
||||
AiWrapper: this
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
'label-width': {
|
||||
type: String
|
||||
},
|
||||
|
||||
columnsNumber: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
border: Boolean,
|
||||
title: String
|
||||
},
|
||||
|
||||
computed: {
|
||||
autoWidth() {
|
||||
return ((1 / this.columnsNumber) * 100).toFixed(2) + '%'
|
||||
},
|
||||
|
||||
autoLableWidth() {
|
||||
return this.labelWidth
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ai-wrapper {
|
||||
.w100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ai-wrapper-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
|
||||
&.border {
|
||||
border-left: 1px solid #d0d4dc;
|
||||
border-top: 1px solid #d0d4dc;
|
||||
|
||||
.ai-info-item {
|
||||
border-bottom: 1px solid #d0d4dc;
|
||||
border-right: 1px solid #d0d4dc;
|
||||
margin-bottom: 0;
|
||||
line-height: 32px;
|
||||
|
||||
.ai-info-item__left {
|
||||
background: rgba(0, 0, 0, .03);
|
||||
padding-right: 16px;
|
||||
border-right: 1px solid #d0d4dc;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
border: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user