feat: 🎸 新增Detail详情组件并给出相应示例

This commit is contained in:
kailong321200875 2020-12-27 19:43:54 +08:00
parent fa9f24d5da
commit e77a931ef2
39 changed files with 800 additions and 192 deletions

View File

@ -0,0 +1,207 @@
<template>
<div class="detail__wrap">
<div class="detail__wrap--header" @click="toggleClick">
<div class="detail__wrap--title">
<div v-if="title">
{{ title }}
<el-tooltip
v-if="message"
effect="dark"
:content="message"
placement="right"
>
<i class="el-icon-warning-outline" />
</el-tooltip>
</div>
</div>
<i v-if="collapsed" :class="['el-icon-arrow-down', { 'el-icon-arrow-down-transform': !show }]" />
</div>
<el-collapse-transition>
<div
v-show="show"
class="detail__content"
:style="contentStyleObj"
>
<el-row>
<el-col
v-for="(item, $index) in schema"
:key="$index"
:span="item.span || 12"
>
<div
class="detail__content--item"
:class="{'detail__content--flex': !vertical}"
>
<div class="content__item--label" :style="labelStyleObj">
<slot v-if="item.slots && item.slots.title" :name="item.slots.title" :row="item" />
<template v-else>{{ item.label }}</template>
</div>
<div class="content__item--message" :style="messageStyleObj">
<slot v-if="item.slots && item.slots.default" :name="item.slots.default" :row="item" />
<template v-else>{{ data[item.field] }}</template>
</div>
</div>
</el-col>
</el-row>
</div>
</el-collapse-transition>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, PropType, computed } from 'vue'
export default defineComponent({
name: 'Detail',
props: {
//
title: {
type: String as PropType<string>,
default: ''
},
//
collapsed: {
type: Boolean as PropType<boolean>,
default: true
},
//
message: {
type: String as PropType<string>,
default: ''
},
//
border: {
type: Boolean as PropType<boolean>,
default: true
},
//
data: {
type: Object as PropType<object>,
required: true
},
//
schema: {
type: Array as PropType<any[]>,
required: true
},
//
vertical: {
type: Boolean as PropType<boolean>,
default: false
},
//
labelWidth: {
type: String as PropType<string>,
default: '150px'
},
//
labelAlign: {
type: String as PropType<string>,
default: 'left'
},
//
borderColor: {
type: String as PropType<string>,
default: '#f0f0f0'
},
//
labelBg: {
type: String as PropType<string>,
default: '#fafafa'
}
},
setup(props) {
const show = ref<boolean>(true)
const contentStyleObj = computed(() => {
return {
borderTop: props.border ? `1px solid ${props.borderColor}` : '',
borderLeft: props.border ? `1px solid ${props.borderColor}` : ''
}
})
const labelStyleObj = computed(() => {
return {
width: props.vertical ? `calc(100% - 33px)` : props.labelWidth,
textAlign: props.labelAlign,
backgroundColor: props.border ? props.labelBg : '',
borderRight: props.border ? `1px solid ${props.borderColor}` : '',
borderBottom: props.border ? `1px solid ${props.borderColor}` : ''
}
})
const messageStyleObj = computed(() => {
return {
width: props.vertical ? `calc(100% - 33px)` : '100%',
borderRight: props.border ? `1px solid ${props.borderColor}` : '',
borderBottom: props.border ? `1px solid ${props.borderColor}` : ''
}
})
function toggleClick() {
if (props.collapsed) {
show.value = !show.value
}
}
return {
show,
contentStyleObj, labelStyleObj, messageStyleObj,
toggleClick
}
}
})
</script>
<style lang="less" scoped>
.detail__wrap {
background: #fff;
border-radius: 2px;
padding: 10px;
.detail__wrap--header {
display: flex;
height: 32px;
margin-bottom: 10px;
justify-content: space-between;
align-items: center;
cursor: pointer;
.detail__wrap--title {
display: inline-block;
font-size: 18px;
font-weight: 700;
color: rgba(0, 0, 0, .85);
position: relative;
margin-left: 10px;
&:after {
content: "";
width: 3px;
height: 100%;
background: #2d8cf0;
border-radius: 2px;
position: absolute;
top: 1px;
left: -10px;
}
}
.el-icon-arrow-down {
transition: all .2s;
}
.el-icon-arrow-down-transform {
transform: rotate(-180deg);
}
}
.detail__content {
.detail__content--flex {
display: flex;
height: 100%;
}
.content__item--label {
padding: 8px 16px;
}
.content__item--message {
flex: 1;
padding: 8px 16px;
line-height: 20px;
}
}
}
</style>

View File

@ -1,11 +1,11 @@
<template>
<div class="more__item clearfix">
<p class="more__item--text">{{ content }}</p>
<div class="more__item clearfix" :style="styleWrapObj">
<p class="more__item--text" :style="styleTextObj">{{ content }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, computed, unref, onMounted, nextTick, ref } from 'vue'
import { defineComponent, PropType, computed } from 'vue'
export default defineComponent({
name: 'More',
props: {
@ -17,47 +17,51 @@ export default defineComponent({
//
lineClamp: {
type: Number as PropType<number>,
default: 0
},
//
width: {
type: String as PropType<string>,
default: '300px'
default: 1
},
// style
style: {
type: Object as PropType<object>,
default: () => {
return {
width: '300px',
float: 'left'
}
}
default: () => null
}
},
setup(props) {
const styleObj = computed(() => {
const styleWrapObj = computed(() => {
return props.style
})
const styleTextObj = computed(() => {
if (props.lineClamp === 1) {
//
return {
'white-space': 'nowrap'
}
} else {
//
return {
display: '-webkit-box',
'-webkit-line-clamp': props.lineClamp,
'-webkit-box-orient': 'vertical'
}
}
})
return {
styleWrapObj,
styleTextObj
}
}
})
</script>
<style lang="less" scoped>
.more__item {
width: 528px;
height: 122px;
float: left;
.more__item--text {
width: 476px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
font-size: 14px;
color: #545c63;
line-height: 28px;
transition: all .1s;
text-align: left;
&:hover {
@ -67,10 +71,11 @@ export default defineComponent({
z-index: 5;
border-radius: 8px;
box-shadow: 0 8px 16px 0 rgba(7,17,27,.2);
-webkit-line-clamp: inherit;
padding: 10px;
-webkit-line-clamp: inherit !important;
padding: 10px;
margin-top: -10px;
margin-left: -10px;
white-space: normal !important;
}
}
}

View File

@ -77,6 +77,11 @@
<el-switch v-model="logo" @change="setLogo" />
</div>
<div class="setting__item">
<span>灰色模式</span>
<el-switch v-model="greyMode" @change="setGreyMode" />
</div>
<div class="setting__item">
<span>页面标题</span>
<el-input v-model="title" size="mini" @change="setTitle" />
@ -86,6 +91,7 @@
<span>LOGO标题</span>
<el-input v-model="logoTitle" size="mini" @change="setLogoTitle" />
</div>
</div>
</el-drawer>
</template>
@ -158,6 +164,11 @@ export default defineComponent({
appStore.SetLogoTitle(logoTitle)
}
const greyMode = ref<boolean>(appStore.greyMode)
function setGreyMode(greyMode: boolean) {
appStore.SetGreyMode(greyMode)
}
return {
drawer, toggleClick,
layout, setLayout,
@ -170,7 +181,8 @@ export default defineComponent({
tagsView, setTagsView,
logo, setLogo,
title, setTitle,
logoTitle, setLogoTitle
logoTitle, setLogoTitle,
greyMode, setGreyMode
}
}
})
@ -199,7 +211,7 @@ export default defineComponent({
//
.setting__content {
background: @appBg;
padding: 0 20px;
padding: 0 20px 20px 20px;
.setting__title {
text-align: center;
padding-top: 20px;

View File

@ -4,16 +4,16 @@
<!-- 树型数据 -->
<template v-if="item.children && item.children.length">
<table-column
:key="item[item.key]"
:key="item[item.field]"
:child="item"
/>
</template>
<template v-else>
<el-table-column
:key="item[item.key]"
:key="item[item.field]"
v-bind="{...getItemBindValue(item)}"
:prop="item.key"
:prop="item.field"
>
<!-- 表头插槽 -->
<template v-if="item.slots && item.slots.header" #header="scope">
@ -37,7 +37,7 @@
<!-- 不需要插槽 -->
<!-- <span v-if="!item.slots || !item.slots.default">
{{ scope.row[item.key] }}
{{ scope.row[item.field] }}
</span> -->
</el-table-column>
</template>

View File

@ -12,7 +12,7 @@
<!-- 自定义索引 -->
<template v-if="item.type === 'index'">
<el-table-column
:key="item[item.key]"
:key="item[item.field]"
v-bind="{...getItemBindValue(item)}"
type="index"
:index="item.index"
@ -22,16 +22,16 @@
<!-- 树型数据 -->
<template v-else-if="item.children && item.children.length">
<table-column
:key="item[item.key]"
:key="item[item.field]"
:child="item"
/>
</template>
<template v-else>
<el-table-column
:key="item[item.key]"
:key="item[item.field]"
v-bind="{...getItemBindValue(item)}"
:prop="item.key"
:prop="item.field"
>
<!-- 表头插槽 -->
<template v-if="item.slots && item.slots.header" #header="scope">
@ -55,7 +55,7 @@
</template>
<!-- 不需要插槽 -->
<!-- <span v-if="!item.slots || !item.slots.default">
{{ scope.row[item.key] }}
{{ scope.row[item.field] }}
</span> -->
</el-table-column>
</template>

View File

@ -297,7 +297,7 @@ export default defineComponent({
font-size: 12px;
margin-left: 5px;
&:last-of-type {
margin-right: 23px;
margin-right: 10px;
}
&.active {
background-color: @tagActiveBg;

View File

@ -2,9 +2,11 @@ import type { App } from 'vue'
import Dialog from './Dialog/index.vue'// Dialog组件
import ComTable from './Table/index.vue'// Table组件
import ComSearch from './Search/index.vue'// Search组件
import ComDetail from './Detail/index.vue'// Detail组件
export function setupGlobCom(app: App<Element>): void {
app.component('ComDialog', Dialog)
app.component('ComTable', ComTable)
app.component('ComSearch', ComSearch)
app.component('ComDetail', ComDetail)
}

View File

@ -1,11 +1,18 @@
<template>
<router-view class="app" />
<router-view class="app" :class="{'grey__mode': greyMode}" />
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { defineComponent, computed } from 'vue'
import { appStore } from '_p/index/store/modules/app'
export default defineComponent({
name: 'App'
name: 'App',
setup() {
const greyMode = computed(() => appStore.greyMode)
return {
greyMode
}
}
})
</script>
@ -24,4 +31,12 @@ html,body {
.size;
background: @appBg;
}
.grey__mode {
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
-o-filter: grayscale(100%);
filter: grayscale(100%);
filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
}
</style>

View File

@ -188,6 +188,14 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
meta: {
title: '显示更多'
}
},
{
path: 'detail',
component: () => import('_p/index/views/components-demo/detail/index.vue'),
name: 'DetailDemo',
meta: {
title: '详情组件'
}
}
]
},

View File

@ -15,6 +15,7 @@ export interface AppState {
title: String
logoTitle: String
userInfo: String
greyMode: Boolean
}
@Module({ dynamic: true, namespaced: true, store, name: 'app' })
@ -32,6 +33,7 @@ class App extends VuexModule implements AppState {
public title = 'vue-element-plus-admin' // 标题
public logoTitle = 'vue-ElPlus-admin' // logo标题
public userInfo = 'userInfo' // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突
public greyMode = false // 是否开始灰色模式,用于特殊悼念日
@Mutation
private SET_COLLAPSED(collapsed: boolean): void {
@ -81,6 +83,10 @@ class App extends VuexModule implements AppState {
private SET_LOGOTITLE(logoTitle: string): void {
this.logoTitle = logoTitle
}
@Mutation
private SET_GREYMODE(greyMode: boolean): void {
this.greyMode = greyMode
}
@Action
public SetCollapsed(collapsed: boolean): void {
@ -130,6 +136,10 @@ class App extends VuexModule implements AppState {
public SetLogoTitle(logoTitle: string): void {
this.SET_LOGOTITLE(logoTitle)
}
@Action
public SetGreyMode(greyMode: boolean): void {
this.SET_GREYMODE(greyMode)
}
}
export const appStore = getModule<App>(App)

View File

@ -0,0 +1,263 @@
<template>
<div>
<el-alert
effect="dark"
:closable="false"
title="详情组件。"
type="info"
style="margin-bottom: 20px;"
/>
<com-detail
:data="data"
:schema="schema"
title="基础示例"
message="辅助文字"
/>
<com-detail
title="不可折叠"
:data="data"
:schema="schema"
:collapsed="false"
message="辅助文字"
style="margin-top: 20px;"
/>
<com-detail
title="没有辅助文字"
:data="data"
:schema="schema"
style="margin-top: 20px;"
/>
<com-detail
title="没有边框"
:data="data"
:schema="schema"
:border="false"
style="margin-top: 20px;"
/>
<com-detail
title="垂直布局"
:data="data"
:vertical="true"
:schema="schema"
style="margin-top: 20px;"
/>
<el-form
ref="formRef"
:hide-required-asterisk="true"
:model="form"
:rules="rules"
style="margin-top: 20px;"
>
<com-detail
title="与表单结合并自定义插槽"
:data="form"
:schema="fromSchema"
>
<template #title="scope">
<span class="is-required-item">{{ scope.row.label }}</span>
</template>
<template #titleDefault>
<el-form-item prop="title">
<el-input v-model="form.title" placeholder="请输入标题" />
</el-form-item>
</template>
<template #author="scope">
<span class="is-required-item">{{ scope.row.label }}</span>
</template>
<template #authorDefault>
<el-form-item prop="author">
<el-input v-model="form.author" placeholder="请输入作者" />
</el-form-item>
</template>
<template #time="scope">
<span class="is-required-item">{{ scope.row.label }}</span>
</template>
<template #timeDefault>
<el-form-item prop="display_time">
<el-date-picker
v-model="form.display_time"
type="datetime"
placeholder="请选择创建时间"
style="width: 100%;"
/>
</el-form-item>
</template>
<template #importance="scope">
<span class="is-required-item">{{ scope.row.label }}</span>
</template>
<template #importanceDefault>
<el-form-item prop="importance">
<el-select v-model="form.importance" placeholder="请选择重要性" style="width: 100%;">
<el-option label="重要" value="3" />
<el-option label="良好" value="2" />
<el-option label="一般" value="1" />
</el-select>
</el-form-item>
</template>
<template #pageviews="scope">
<span class="is-required-item">{{ scope.row.label }}</span>
</template>
<template #pageviewsDefault>
<el-form-item prop="pageviews">
<el-input-number
v-model="form.pageviews"
:min="0"
:max="99999999"
style="width: 100%;"
/>
</el-form-item>
</template>
</com-detail>
<div style="margin-top: 15px;text-align: center;">
<el-button type="primary" @click="saveData">保存(控制台查看数据)</el-button>
</div>
</el-form>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, unref } from 'vue'
import { InfoWriteParams, InfoWriteRules } from './types'
const data: any = {
username: 'chenkl',
nickName: '梦似花落。',
age: 26,
phone: '13655971xxxx',
email: '502431556@qq.com',
addr: '这是一个很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的地址',
sex: '男',
certy: '35058319940712xxxx'
}
const schema: any[] = [
{
field: 'username',
label: '用户名'
},
{
field: 'nickName',
label: '昵称'
},
{
field: 'phone',
label: '联系电话'
},
{
field: 'email',
label: '邮箱'
},
{
field: 'addr',
label: '地址',
span: 24
}
]
const requiredRule = {
required: true,
message: '该项为必填项'
}
const fromSchema: any[] = [
{
field: 'title',
label: '标题',
span: 24,
slots: {
title: 'title',
default: 'titleDefault'
}
},
{
field: 'author',
label: '作者',
slots: {
title: 'author',
default: 'authorDefault'
}
},
{
field: 'display_time',
label: '创建时间',
slots: {
title: 'time',
default: 'timeDefault'
}
},
{
field: 'importance',
label: '重要性',
slots: {
title: 'importance',
default: 'importanceDefault'
}
},
{
field: 'pageviews',
label: '阅读数',
slots: {
title: 'pageviews',
default: 'pageviewsDefault'
}
}
]
export default defineComponent({
// name: 'DetailDemo',
setup() {
const formRef = ref<HTMLElement | null>(null)
const form = reactive<InfoWriteParams>({
id: '', // id
author: '', //
title: '', //
importance: '', //
display_time: '', //
pageviews: 0 //
})
const rules = reactive<InfoWriteRules>({
title: [requiredRule],
author: [requiredRule],
importance: [requiredRule],
display_time: [requiredRule],
pageviews: [requiredRule]
})
function saveData() {
const formRefWrap = unref(formRef as any)
try {
formRefWrap.validate(async(valid: boolean) => {
if (valid) {
const formData = unref(form)
console.log(formData)
} else {
console.log('error submit!!')
return false
}
})
} catch (err) {
console.log(err)
}
}
return {
data, schema,
formRef, form, fromSchema, rules, saveData
}
}
})
</script>
<style>
</style>

View File

@ -0,0 +1,16 @@
export interface InfoWriteParams {
title: string
id?: string
author: string
importance: string
display_time: string
pageviews: number
}
export interface InfoWriteRules {
title?: any[]
author?: any[]
importance?: any[]
display_time?: any[]
pageviews?: any[]
}

View File

@ -3,30 +3,89 @@
<el-alert
effect="dark"
:closable="false"
title="显示更多组件。"
title="显示更多组件,默认显示一行,超出隐藏。必须设置 width 样式。"
type="info"
style="margin-bottom: 20px;"
style="margin-bottom: 20px;margin-top: 20px;"
/>
<div style="display: flex;">
<div style="flex: 1;">
<el-alert
effect="dark"
:closable="false"
title="脱离文档流,只需要添加 height 样式即可。"
type="info"
style="margin-bottom: 20px;"
/>
<More
:style="{
width: '600px',
height: '100px',
lineHeight: '28px'
}"
content="有两个年轻人同在一家车行里工作,两个人关系很好,以兄弟相称。他们在这家车行已经做了两年了,每天除了修理汽车外什么也没有。哥哥总不肯闲着,他一会儿扫地,一会儿擦玻璃,有时还帮助别人干活儿。弟弟却不这么勤快,没有急活儿的时候他总是懒洋洋地躺着。一天,车行里来了一位中年主顾,他说汽车出了点毛病,让他们给修理一下。弟弟刚刚吃完饭,正在休息呢,哪里肯干活。于是,哥哥走了过去,把弟弟手中的抹布接过来,给汽车做了检查。车子没什么大问题,就是很长时间没修养过了,于是他对那位先生说:“您放心地交给我吧,车子明天一定能修好。”客人听到这话,放心地走了。哥哥一刻不停地忙了起来,他不但修理好了汽车的毛病,还把汽车里里外外擦得一尘不染。这时,躺在一旁的弟弟嘲笑他说:“老兄,别太傻了,不该干的活儿也干了,那么勤快有什么用!”哥哥却笑了笑说:“反正我也没事做,擦擦车我并没有受损失呀,等明天顾客来取车时看到车子焕然一新心里一定很高兴。”第二天,那个顾客来取车了,他看到修好的汽车后非常吃惊,连声感谢修车的哥哥,并对他说:“我是一个大公司的董事长,你为我修车的这种勤快、细致、周到的精神,使我深受感动。我认为你是一个优秀的人,你愿意到我的公司去工作吗?”哥哥的命运从此发生了改变,不久,经过努力他当上了这个公司的部门经理;而弟弟却仍然在车行里做着他觉得枯燥的工作。"
/>
</div>
<div style="margin-left: 20px; flex: 1;">
<el-alert
effect="dark"
:closable="false"
title="不脱离文档流。"
type="info"
style="margin-bottom: 20px;"
/>
<More
:style="{
width: '600px',
lineHeight: '28px'
}"
content="生活中有许多小的“细节”通常并不为人们注意。但是遇到特殊机遇它又会像仙人的魔棒一样改变人的一生创造出人间奇迹。美国总统奥巴马是美国历史上第一位黑人总统。他在《无畏的希望》一书中曾深情地回忆了一幅画改变他命运的事。这幅深深打动他的画。就是19世纪英国艺术大师乔治-弗雷德里克·瓦兹创作的《希望》。画中,一位年轻女子坐在象征世界的地球上面,身体向前倾斜;低垂着头,眼睛被蒙上绷带,手里弹拨着仅剩下一根弦的古希腊七弦琴,并俯首倾听这根弦发出的微弱乐曲声。画家的意图是表现人类直到最后也不能丧失希望的主题。这幅画使奥巴马看后大受启发,他深情地回忆自己读画后的感受说:“虽然这名女子身有疼伤和血迹,穿着破烂不堪,竖琴也只剩下一根弦,但仍有希望;虽然世界被战争撕裂;虽然世界被仇恨摧残;虽然世界被猜疑蹂躏;虽然世界被疾病惩罚;虽然这个世界上充满了饥饿和贪婪;虽然她的竖琴被毁得只剩下一根琴弦但这名女子仍有无畏的希望在她那仅存的一根琴弦上去弹奏音乐去赞美世界。”奥巴马把这次看画的机遇当做他人生的转折点。就是在看了这幅画以后他立志去竞选美国总统以施展抱负改变美国改变自己的人生。经过了一番艰苦努力他终于如愿以偿地当上了美国第44任总统。"
/>
</div>
</div>
<el-alert
effect="dark"
:closable="false"
title="默认显示一行,超出隐藏。"
title="显示3行,超出隐藏。"
type="info"
style="margin-bottom: 20px;margin-top: 20px;"
/>
<div>
<More :line-clamp="3" content="2020版是根据前端全栈工程师岗位要求结合时下技术热点及未来前端术发展趋势全新制作的课程让你实现从零基础到具备全栈能力的就业水平。相比之前的版本我们有几大重要内容1. 两大核心企业项目贯穿整个体系实现项目闭环开发亲历从0到1的企业级项目。项目之间强关联还原企业开发模式渐进式实现过程更顺滑。第一个旅游网项目还原企业迭代开发同一个项目从PC端演变到移动webapp掌握跨端开发能力。 第二个电商全栈项目用Vue.js 实现前端node.js+koa2+MongoDB实现后端打造6大核心业务15+精美网页12+真实数据接口并打通前后端数据联调。2. 项目驱动式教学案例融入课程讲解中让学习不枯燥。多领域案例帮你开拓眼界和经验。3. 不用担心版本问题课程截止上线使用最新版本。比如Vue3.0等让大家学到新技术。4. 技术覆盖更实用更全面从0基础到全栈能力循序渐进的培养慕课网宗旨只学有用的。帮你打通跨端和全栈技能。5. 慕课网网红讲师亲授讲解更细致阶梯式习题整门课程代码量将达到6万行相当于1~2年工作经验所敲代码。" />
</div>
<el-alert
effect="dark"
:closable="false"
title="默认显示3行超出隐藏。"
type="info"
style="margin-bottom: 20px;margin-top: 20px;"
/>
<div>
<More :line-clamp="3" content="2020版是根据前端全栈工程师岗位要求结合时下技术热点及未来前端术发展趋势全新制作的课程让你实现从零基础到具备全栈能力的就业水平。相比之前的版本我们有几大重要内容1. 两大核心企业项目贯穿整个体系实现项目闭环开发亲历从0到1的企业级项目。项目之间强关联还原企业开发模式渐进式实现过程更顺滑。第一个旅游网项目还原企业迭代开发同一个项目从PC端演变到移动webapp掌握跨端开发能力。 第二个电商全栈项目用Vue.js 实现前端node.js+koa2+MongoDB实现后端打造6大核心业务15+精美网页12+真实数据接口并打通前后端数据联调。2. 项目驱动式教学案例融入课程讲解中让学习不枯燥。多领域案例帮你开拓眼界和经验。3. 不用担心版本问题课程截止上线使用最新版本。比如Vue3.0等让大家学到新技术。4. 技术覆盖更实用更全面从0基础到全栈能力循序渐进的培养慕课网宗旨只学有用的。帮你打通跨端和全栈技能。5. 慕课网网红讲师亲授讲解更细致阶梯式习题整门课程代码量将达到6万行相当于1~2年工作经验所敲代码。" />
<div style="display: flex;">
<div style="flex: 1;">
<el-alert
effect="dark"
:closable="false"
title="脱离文档流,只需要添加 height 样式即可。"
type="info"
style="margin-bottom: 20px;"
/>
<More
:line-clamp="3"
:style="{
width: '100%',
height: '100px',
lineHeight: '28px'
}"
content="20多年前一位知名企业的总经理想要招聘一名助理。这对于刚刚走出校门的青年们来说是一个非常好的机会所以一时间应征者云集。经过严格的初选、复试、面试总经理最终挑中了一个毫无经验的青年。副总经理对于他的决定有些不理解于是问他“那个青年胜在哪里呢?他既没带一封介绍信,也没受任何人的推荐,而且毫无经验。”总经理告诉他:“的确,他没带来介绍信,刚刚从大学毕业,一点经验也没有。但他有很多东西更可贵。他进来的时候在门口蹭掉了脚下带的土,进门后又随手关上了门,这说明他做事小心仔细。当看到那位身体上有些残疾的面试者时,他立即起身让座,表明他心地善良、体贴别人。进了办公室他先脱去帽子,回答我提出的问题时也是干脆果断,证明他既懂礼貌又有教养。”总经理顿了顿,接着说:“面试之前,我在地板上扔了本书,其他所有人都从书上迈了过去,而这个青年却把它捡起来了,并放回桌子上;当我和他交谈时,我发现他衣着整洁,头发梳得整整齐齐,指甲修得干干净净。在我看来,这些细节就是最好的介绍信,这些修养是一个人最最要的品牌形象。”"
/>
</div>
<div style="margin-left: 20px; flex: 1;">
<el-alert
effect="dark"
:closable="false"
title="不脱离文档流。"
type="info"
style="margin-bottom: 20px;"
/>
<More
:line-clamp="3"
:style="{
width: '100%',
lineHeight: '28px'
}"
content="一个青年刚刚大学毕业,凭借着自己的能力,他找到了一份人人羡慕的高薪工作,在一个海上油田钻井队里做技术员。工作的第一天,领班要求青年在限定的时间内登上几十米高的钻井架,把一个包装好的漂亮盒子拿给在井架顶层的主管。青年对这第一个任务信心百倍,他拿着盒子,快步登上狭窄的舷梯,登舷梯是十分累人的,当青年气喘吁吁、满头大汗地登上顶层,把盒子交给主管时,主管只在盒子上面签下自己的名字,又让他送回去。于是,青年按照吩咐又快步走下舷梯,把盒子交给领班,而领班也是同样在盒子上面签下自己的名字,让他再次送给主管。青年此时有些耐烦了,他觉得这项工作一点意义也没有。可是领导的命令不得不执行,于是青年又转身登上舷梯。当他第二次登上井架的顶层时,已经浑身是汗,两条腿抖得厉害。主管和上次一样,只是在盒子上签下名字,又让他把盒子送下去。年轻人擦了擦脸上的汗水,转身走下舷梯,把盒子送下来,然而,领班还是在签完字以后让他再送上去。青年有些生气了,他觉得主管和领班是在跟他开玩笑。他长长地叹了一口气,尽力忍着不发作,擦了擦满脸的汗水,抬头看着那已经爬上爬下了数次的舷梯,拿起盒子,步履艰难地往上爬。当他上到顶层时,浑身上下都被汗水浸透了,汗水顺着脸颊往下淌。他第三次把盒子递给主管,主管看着他慢条斯理地说:“请你把盒子打开。”青年于是打开了盒子——里面竟然是两个玻璃罐:一罐是咖啡,另一罐是咖啡伴侣。年轻人终于无法克制心头的怒火,把愤怒的目光射向主管。主管接着对他说:“请你把咖啡冲上。”这时,青年将所有的愤怒和不满全部发泄了出来,他“啪”地一声把盒子扔在地上,大声地说:“我不干了!”此时,主管摇了摇头,对青年说:“很遗憾,如果你再忍耐一下,你就可以通过这个考验了。刚才我们所做的是一种‘承受极限训练’,因为我们在海上作业,随时会遇到危险,这就要求队员们有极强的承受力,承受各种危险的考验,只有这样才能成功地完成海上作业任务。你已经通过了前面三次,只差最后一关,你没有喝到自己冲的胜利的咖啡。因此,对不起,您不能在这里工作了。”"
/>
</div>
</div>
</div>
</template>

View File

@ -78,31 +78,31 @@ const searchData = [
const columns = [
{
key: 'title',
field: 'title',
label: '标题',
showOverflowTooltip: true
},
{
key: 'author',
field: 'author',
label: '作者'
},
{
key: 'display_time',
field: 'display_time',
label: '创建时间'
},
{
key: 'importance',
field: 'importance',
label: '重要性',
slots: {
default: 'importance'
}
},
{
key: 'pageviews',
field: 'pageviews',
label: '阅读数'
},
{
key: 'action',
field: 'action',
label: '操作',
width: '150px',
slots: {

View File

@ -74,31 +74,31 @@ const searchData = [
const columns = [
{
key: 'title',
field: 'title',
label: '标题',
showOverflowTooltip: true
},
{
key: 'author',
field: 'author',
label: '作者'
},
{
key: 'display_time',
field: 'display_time',
label: '创建时间'
},
{
key: 'importance',
field: 'importance',
label: '重要性',
slots: {
default: 'importance'
}
},
{
key: 'pageviews',
field: 'pageviews',
label: '阅读数'
},
{
key: 'action',
field: 'action',
label: '操作',
width: '150px',
slots: {

View File

@ -96,11 +96,11 @@ export default defineComponent({
passWord: [{ required: true, message: '请输入密码' }]
})
async function login(): Promise<void> {
const form = unref(loginForm) as any
if (!form) return
const formWrap = unref(loginForm) as any
if (!formWrap) return
loading.value = true
try {
form.validate((valid: boolean) => {
formWrap.validate((valid: boolean) => {
if (valid) {
permissionStore.GenerateRoutes().then(() => {
permissionStore.addRouters.forEach(async(route: RouteRecordRaw) => {

View File

@ -16,15 +16,15 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -21,15 +21,15 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -39,15 +39,15 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'action',
field: 'action',
slots: {
header: 'actionHeader',
default: 'action'

View File

@ -48,22 +48,22 @@ export default defineComponent({
const columns = ref<any[]>([
{
key: 'index',
field: 'index',
type: 'index',
index: (index: number) => {
return index * 2
}
},
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
])

View File

@ -47,22 +47,22 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'id',
field: 'id',
type: 'expand',
slots: {
default: 'id'
}
},
{
key: 'id',
field: 'id',
label: '商品ID'
},
{
key: 'name',
field: 'name',
label: '商品名称'
},
{
key: 'desc',
field: 'desc',
label: '描述'
}
]

View File

@ -28,38 +28,38 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期',
fixed: true,
width: '150'
},
{
key: 'name',
field: 'name',
label: '姓名',
width: '120'
},
{
key: 'province',
field: 'province',
label: '省份',
width: '120'
},
{
key: 'city',
field: 'city',
label: '市区',
width: '120'
},
{
key: 'address',
field: 'address',
label: '地址',
width: '300'
},
{
key: 'zip',
field: 'zip',
label: '邮编',
width: '120'
},
{
key: 'action',
field: 'action',
label: '操作',
width: '100',
fixed: 'right',

View File

@ -27,38 +27,38 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期',
fixed: true,
width: '150'
},
{
key: 'name',
field: 'name',
label: '姓名',
width: '120'
},
{
key: 'province',
field: 'province',
label: '省份',
width: '120'
},
{
key: 'city',
field: 'city',
label: '市区',
width: '120'
},
{
key: 'address',
field: 'address',
label: '地址',
width: '300'
},
{
key: 'zip',
field: 'zip',
label: '邮编',
width: '120'
},
{
key: 'action',
field: 'action',
label: '操作',
width: '100',
fixed: 'right',

View File

@ -22,15 +22,15 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -27,38 +27,38 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期',
fixed: true,
width: '150'
},
{
key: 'name',
field: 'name',
label: '姓名',
width: '120'
},
{
key: 'province',
field: 'province',
label: '省份',
width: '120'
},
{
key: 'city',
field: 'city',
label: '市区',
width: '120'
},
{
key: 'address',
field: 'address',
label: '地址',
width: '300'
},
{
key: 'zip',
field: 'zip',
label: '邮编',
width: '120'
},
{
key: 'action',
field: 'action',
label: '操作',
width: '100',
fixed: 'right',

View File

@ -31,25 +31,25 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'id',
field: 'id',
label: 'ID'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'amount1',
field: 'amount1',
label: '数值1',
sortable: true
},
{
key: 'amount2',
field: 'amount2',
label: '数值2',
sortable: true
},
{
key: 'amount3',
field: 'amount3',
label: '数值4',
sortable: true
}
@ -57,23 +57,23 @@ const columns = [
const columns1 = [
{
key: 'id',
field: 'id',
label: 'ID'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'amount1',
field: 'amount1',
label: '数值1'
},
{
key: 'amount2',
field: 'amount2',
label: '数值2'
},
{
key: 'amount3',
field: 'amount3',
label: '数值4'
}
]

View File

@ -27,7 +27,7 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期',
fixed: true,
width: '150'
@ -36,7 +36,7 @@ const columns = [
label: '配送信息',
children: [
{
key: 'name',
field: 'name',
label: '姓名',
width: '120'
},
@ -44,24 +44,24 @@ const columns = [
label: '地址',
children: [
{
key: 'province',
field: 'province',
label: '省份',
width: '120'
},
{
key: 'city',
field: 'city',
label: '市区',
width: '120'
},
{
key: 'address',
field: 'address',
label: '地址',
slots: {
default: 'address'
}
},
{
key: 'zip',
field: 'zip',
label: '邮编',
width: '120'
}
@ -70,7 +70,7 @@ const columns = [
]
},
{
key: 'action',
field: 'action',
label: '操作',
width: '100',
slots: {

View File

@ -28,15 +28,15 @@ import { defineComponent, ref, unref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -26,15 +26,15 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -12,7 +12,7 @@
<com-table
ref="filterTable"
v-loading="loading"
row-key="date"
row-field="date"
:columns="columns"
:data="tableData"
:default-sort="{prop: 'date', order: 'descending'}"
@ -66,25 +66,25 @@ export default defineComponent({
const columns = ref<any[]>([
{
key: 'date',
field: 'date',
label: '日期',
sortable: true,
width: '180',
columnKey: 'date',
columnfield: 'date',
filters: [{ text: '2016-05-01', value: '2016-05-01' }, { text: '2016-05-02', value: '2016-05-02' }, { text: '2016-05-03', value: '2016-05-03' }, { text: '2016-05-04', value: '2016-05-04' }],
filterMethod: filterHandler
},
{
key: 'name',
field: 'name',
label: '姓名',
sortable: true
},
{
key: 'address',
field: 'address',
label: '地址'
},
{
key: 'tag',
field: 'tag',
label: '标签',
filters: [{ text: '家', value: '家' }, { text: '公司', value: '公司' }],
filterMethod: filterTag,

View File

@ -28,15 +28,15 @@ import { defineComponent, ref, unref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -22,17 +22,17 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期',
sortable: true
},
{
key: 'name',
field: 'name',
label: '姓名',
sortable: true
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -21,15 +21,15 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -21,15 +21,15 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -1,20 +1,20 @@
<template>
<a-table :data-source="data" :columns="columns">
<template #filterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
<template #filterDropdown="{ setSelectedfields, selectedfields, confirm, clearFilters, column }">
<div style="padding: 8px">
<a-input
:ref="c => (searchInput = c)"
:placeholder="`Search ${column.dataIndex}`"
:value="selectedKeys[0]"
:value="selectedfields[0]"
style="width: 188px; margin-bottom: 8px; display: block;"
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
@pressEnter="handleSearch(selectedKeys, confirm, column.dataIndex)"
@change="e => setSelectedfields(e.target.value ? [e.target.value] : [])"
@pressEnter="handleSearch(selectedfields, confirm, column.dataIndex)"
/>
<el-button
type="primary"
size="small"
style="width: 90px; margin-right: 8px"
@click="handleSearch(selectedKeys, confirm, column.dataIndex)"
@click="handleSearch(selectedfields, confirm, column.dataIndex)"
>
<template #icon><SearchOutlined /></template>
Search
@ -51,25 +51,25 @@
import { SearchOutlined } from '@ant-design/icons-vue';
const data = [
{
key: '1',
field: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
field: '2',
name: 'Joe Black',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
field: '3',
name: 'Jim Green',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
field: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
@ -90,7 +90,7 @@ export default {
{
title: 'Name',
dataIndex: 'name',
key: 'name',
field: 'name',
slots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
@ -113,7 +113,7 @@ export default {
{
title: 'Age',
dataIndex: 'age',
key: 'age',
field: 'age',
slots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
@ -135,7 +135,7 @@ export default {
{
title: 'Address',
dataIndex: 'address',
key: 'address',
field: 'address',
slots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
@ -158,10 +158,10 @@ export default {
};
},
methods: {
handleSearch(selectedKeys, confirm, dataIndex) {
handleSearch(selectedfields, confirm, dataIndex) {
confirm();
console.log(selectedKeys[0]);
this.searchText = selectedKeys[0];
console.log(selectedfields[0]);
this.searchText = selectedfields[0];
this.searchedColumn = dataIndex;
this.$forceUpdate();
},

View File

@ -4,38 +4,38 @@ import { defineComponent } from 'vue'
const columns = [
{
dataIndex: 'name',
key: 'name',
field: 'name',
// slots: { title: 'customTitle', customRender: 'name' }
},
{
title: 'Age',
dataIndex: 'age',
key: 'age'
field: 'age'
},
{
title: 'Address',
dataIndex: 'address',
key: 'address'
field: 'address'
}
]
const data = [
{
key: '1',
field: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer']
},
{
key: '2',
field: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser']
},
{
key: '3',
field: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',

View File

@ -33,25 +33,25 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'id',
field: 'id',
label: 'ID'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'amount1',
field: 'amount1',
label: '数值1',
sortable: true
},
{
key: 'amount2',
field: 'amount2',
label: '数值2',
sortable: true
},
{
key: 'amount3',
field: 'amount3',
label: '数值4',
sortable: true
}
@ -59,23 +59,23 @@ const columns = [
const columns1 = [
{
key: 'id',
field: 'id',
label: 'ID'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'amount1',
field: 'amount1',
label: '数值1'
},
{
key: 'amount2',
field: 'amount2',
label: '数值2'
},
{
key: 'amount3',
field: 'amount3',
label: '数值4'
}
]

View File

@ -11,7 +11,7 @@
v-loading="loading"
:columns="columns"
:data="tableData"
row-key="id"
row-field="id"
border
default-expand-all
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
@ -21,7 +21,7 @@
v-loading="loading"
:columns="columns1"
:data="tableData1"
row-key="id"
row-field="id"
border
lazy
:load="load"
@ -36,32 +36,32 @@ import { defineComponent, ref } from 'vue'
const columns = [
{
key: 'date',
field: 'date',
label: '日期',
sortable: true
},
{
key: 'name',
field: 'name',
label: '姓名',
sortable: true
},
{
key: 'address',
field: 'address',
label: '地址'
}
]
const columns1 = [
{
key: 'date',
field: 'date',
label: '日期'
},
{
key: 'name',
field: 'name',
label: '姓名'
},
{
key: 'address',
field: 'address',
label: '地址'
}
]

View File

@ -1,3 +1,4 @@
// 悬停样式
.hover-effect {
cursor: pointer;
transition: background .3s;
@ -5,6 +6,16 @@
background: rgba(0, 0, 0, .025);
}
}
// 悬停样式
// 必填样式
.is-required-item::before {
content: "*";
color: #F56C6C;
margin-right: 4px;
}
// 必填样式
// 综合实例样式
.search__example--wrap {