Skip to content

Commit 1723410

Browse files
authored
feat: timeline (#9)
* feat: 基本逻辑完成 * style: 增加深色主题 * style: 样式优化
1 parent 9a509c5 commit 1723410

9 files changed

Lines changed: 876 additions & 1 deletion

File tree

packages/ccui/ui/shared/hooks/use-namespace.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export interface UseNamespace {
33
e: (el: string) => string
44
m: (mo: string) => string
55
em: (el: string, mo: string) => string
6+
is: (name: string) => string
67
}
78

89
function createBem(
@@ -35,10 +36,12 @@ export function useNamespace(block: string, needDot = false): UseNamespace {
3536
modifier ? createBem(namespace, '', modifier) : ''
3637
const em = (element: string, modifier: string) =>
3738
element && modifier ? createBem(namespace, element, modifier) : ''
39+
const is = (name: string) => `is-${name}`
3840
return {
3941
b,
4042
e,
4143
m,
4244
em,
45+
is,
4346
}
4447
}

packages/ccui/ui/timeline/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { App } from 'vue'
2+
import Timeline from './src/timeline'
3+
import TimelineItem from './src/timeline-item'
4+
5+
Timeline.install = function (app: App) {
6+
app.component(Timeline.name!, Timeline)
7+
app.component(TimelineItem.name!, TimelineItem)
8+
}
9+
10+
export { Timeline, TimelineItem }
11+
12+
export default {
13+
title: 'Timeline 时间线',
14+
category: '数据展示',
15+
status: '100%',
16+
install(app: App) {
17+
Timeline.install(app)
18+
},
19+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import type { TimelineItemProps } from './timeline-types'
2+
import { computed, defineComponent, h } from 'vue'
3+
import { useNamespace } from '../../shared/hooks/use-namespace'
4+
import { timelineItemProps } from './timeline-types'
5+
6+
export default defineComponent({
7+
name: 'CTimelineItem',
8+
props: timelineItemProps,
9+
emits: [],
10+
setup(props: TimelineItemProps, { slots }) {
11+
const ns = useNamespace('timeline-item')
12+
13+
// 计算节点的样式类名
14+
const nodeClasses = computed(() => {
15+
return [
16+
ns.e('node'),
17+
ns.em('node', props.size),
18+
props.type && ns.em('node', props.type),
19+
props.hollow && ns.is('hollow'),
20+
].filter(Boolean)
21+
})
22+
23+
// 计算时间戳的样式类名
24+
const timestampClasses = computed(() => {
25+
return [
26+
ns.e('timestamp'),
27+
ns.is(props.placement),
28+
]
29+
})
30+
31+
// 渲染图标
32+
const renderIcon = () => {
33+
if (props.icon) {
34+
if (typeof props.icon === 'string') {
35+
return <i class={[props.icon, ns.e('icon')]}></i>
36+
}
37+
else {
38+
// 如果是组件,使用 h 函数渲染
39+
return h(props.icon, { class: ns.e('icon') })
40+
}
41+
}
42+
return null
43+
}
44+
45+
// 渲染节点
46+
const renderNode = () => {
47+
if (slots.dot) {
48+
return (
49+
<div class={ns.e('dot')}>
50+
{slots.dot()}
51+
</div>
52+
)
53+
}
54+
55+
return (
56+
<div
57+
class={nodeClasses.value}
58+
style={props.color ? { backgroundColor: props.color, borderColor: props.color } : {}}
59+
>
60+
{renderIcon()}
61+
</div>
62+
)
63+
}
64+
65+
// 渲染时间戳
66+
const renderTimestamp = () => {
67+
if (props.hideTimestamp)
68+
return null
69+
70+
return (
71+
<div class={timestampClasses.value}>
72+
{props.timestamp}
73+
</div>
74+
)
75+
}
76+
77+
return () => {
78+
return (
79+
<li class={[ns.b(), props.center && ns.e('center')]}>
80+
{/* 连接线 */}
81+
<div class={ns.e('tail')}></div>
82+
83+
{/* 节点 */}
84+
{renderNode()}
85+
86+
{/* 内容区域 */}
87+
<div class={ns.e('wrapper')}>
88+
{/* 顶部时间戳 */}
89+
{props.placement === 'top' && renderTimestamp()}
90+
91+
{/* 内容 */}
92+
<div class={ns.e('content')}>
93+
{slots.default && slots.default()}
94+
</div>
95+
96+
{/* 底部时间戳 */}
97+
{props.placement === 'bottom' && renderTimestamp()}
98+
</div>
99+
</li>
100+
)
101+
}
102+
},
103+
})
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import type { Component, ExtractPropTypes, PropType } from 'vue'
2+
3+
// Timeline 主组件的 props
4+
export const timelineProps = {
5+
// 暂时没有特殊的 props,主要通过插槽传递 TimelineItem
6+
} as const
7+
8+
export type TimelineProps = ExtractPropTypes<typeof timelineProps>
9+
10+
export type TimelineItemType = 'primary' | 'success' | 'warning' | 'danger' | 'info' | ''
11+
12+
// TimelineItem 组件的 props
13+
export const timelineItemProps = {
14+
/**
15+
* 时间戳内容
16+
*/
17+
timestamp: {
18+
type: String,
19+
default: '',
20+
},
21+
/**
22+
* 是否隐藏时间戳
23+
*/
24+
hideTimestamp: {
25+
type: Boolean,
26+
default: false,
27+
},
28+
/**
29+
* 是否垂直居中
30+
*/
31+
center: {
32+
type: Boolean,
33+
default: false,
34+
},
35+
/**
36+
* 时间戳位置
37+
*/
38+
placement: {
39+
type: String as PropType<'top' | 'bottom'>,
40+
default: 'bottom',
41+
validator: (value: string) => ['top', 'bottom'].includes(value),
42+
},
43+
/**
44+
* 节点类型
45+
*/
46+
type: {
47+
type: String as PropType<TimelineItemType>,
48+
default: '',
49+
validator: (value: string) => ['primary', 'success', 'warning', 'danger', 'info', ''].includes(value),
50+
},
51+
/**
52+
* 节点颜色
53+
*/
54+
color: {
55+
type: String,
56+
default: '',
57+
},
58+
/**
59+
* 节点尺寸
60+
*/
61+
size: {
62+
type: String as PropType<'normal' | 'large'>,
63+
default: 'normal',
64+
validator: (value: string) => ['normal', 'large'].includes(value),
65+
},
66+
/**
67+
* 自定义图标
68+
*/
69+
icon: {
70+
type: [String, Object] as PropType<string | Component>,
71+
},
72+
/**
73+
* 是否空心点
74+
*/
75+
hollow: {
76+
type: Boolean,
77+
default: false,
78+
},
79+
} as const
80+
81+
export type TimelineItemProps = ExtractPropTypes<typeof timelineItemProps>

0 commit comments

Comments
 (0)