@travel-screen/l-module-components 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +86 -0
  2. package/package.json +34 -0
  3. package/src/index.js +60 -0
  4. package/src/l-bar-chart/README.md +302 -0
  5. package/src/l-bar-chart/index.vue +405 -0
  6. package/src/l-bar-chart/interface/index.js +210 -0
  7. package/src/l-data-panel/README.md +331 -0
  8. package/src/l-data-panel/img/images.js +47 -0
  9. package/src/l-data-panel/img/l-data-panel_bg_1.png +0 -0
  10. package/src/l-data-panel/img/l-data-panel_bg_2.png +0 -0
  11. package/src/l-data-panel/img/l-data-panel_bg_3.png +0 -0
  12. package/src/l-data-panel/index.vue +150 -0
  13. package/src/l-data-panel/interface/index.js +249 -0
  14. package/src/l-data-ring/README.md +292 -0
  15. package/src/l-data-ring/img/images.js +21 -0
  16. package/src/l-data-ring/img/l-data-ring_bg.png +0 -0
  17. package/src/l-data-ring/index.vue +93 -0
  18. package/src/l-data-ring/interface/index.js +116 -0
  19. package/src/l-date-picker/README.md +276 -0
  20. package/src/l-date-picker/index.vue +185 -0
  21. package/src/l-date-picker/interface/index.js +128 -0
  22. package/src/l-icon-label/README.md +356 -0
  23. package/src/l-icon-label/img/images.js +28 -0
  24. package/src/l-icon-label/img/l-icon-label_bg.png +0 -0
  25. package/src/l-icon-label/img/l-icon-label_icon.png +0 -0
  26. package/src/l-icon-label/index.vue +162 -0
  27. package/src/l-icon-label/interface/index.js +234 -0
  28. package/src/l-label-value/README.md +358 -0
  29. package/src/l-label-value/img/images.js +19 -0
  30. package/src/l-label-value/img/l-label-value_line.png +0 -0
  31. package/src/l-label-value/index.vue +132 -0
  32. package/src/l-label-value/interface/index.js +116 -0
  33. package/src/l-metric-tile/README.md +391 -0
  34. package/src/l-metric-tile/img/images.js +19 -0
  35. package/src/l-metric-tile/img/l-metric-tile_bg_1.png +0 -0
  36. package/src/l-metric-tile/index.vue +138 -0
  37. package/src/l-metric-tile/interface/index.js +241 -0
  38. package/src/l-module-box/README.md +289 -0
  39. package/src/l-module-box/img/images.js +28 -0
  40. package/src/l-module-box/img/l-module-box_bg.png +0 -0
  41. package/src/l-module-box/img/l-module-box_icon.png +0 -0
  42. package/src/l-module-box/index.vue +125 -0
  43. package/src/l-module-box/interface/index.js +93 -0
  44. package/src/l-module-tag/README.md +243 -0
  45. package/src/l-module-tag/index.vue +137 -0
  46. package/src/l-module-tag/interface/index.js +91 -0
  47. package/src/l-rank-list/README.md +553 -0
  48. package/src/l-rank-list/img/images.js +34 -0
  49. package/src/l-rank-list/img/l-rank-list_icon_line.png +0 -0
  50. package/src/l-rank-list/img/l-rank-list_icon_rank_1.png +0 -0
  51. package/src/l-rank-list/img/l-rank-list_icon_rank_2.png +0 -0
  52. package/src/l-rank-list/img/l-rank-list_icon_rank_other.png +0 -0
  53. package/src/l-rank-list/index.vue +610 -0
  54. package/src/l-rank-list/interface/index.js +343 -0
  55. package/src/l-stat-card/README.md +309 -0
  56. package/src/l-stat-card/img/images.js +36 -0
  57. package/src/l-stat-card/img/img-bg.png +0 -0
  58. package/src/l-stat-card/img/l-stat-card_img_1.png +0 -0
  59. package/src/l-stat-card/index.vue +136 -0
  60. package/src/l-stat-card/interface/index.js +159 -0
  61. package/src/l-tabs/README.md +285 -0
  62. package/src/l-tabs/img/images.js +19 -0
  63. package/src/l-tabs/img/l-tabs_bg.png +0 -0
  64. package/src/l-tabs/index.vue +97 -0
  65. package/src/l-tabs/interface/index.js +115 -0
  66. package/src/l-trend-card/README.md +415 -0
  67. package/src/l-trend-card/img/images.js +41 -0
  68. package/src/l-trend-card/img/l-trend-card_bg.png +0 -0
  69. package/src/l-trend-card/img/l-trend-card_icon_1.png +0 -0
  70. package/src/l-trend-card/img/l-trend-card_icon_down.png +0 -0
  71. package/src/l-trend-card/img/l-trend-card_icon_up.png +0 -0
  72. package/src/l-trend-card/index.vue +181 -0
  73. package/src/l-trend-card/interface/index.js +243 -0
  74. package/src/utils/tooltip.js +30 -0
  75. package/src/utils/util.js +27 -0
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <div class="l-stat-card">
3
+ <div class="l-stat-card__icon" :style="iconContainerStyle">
4
+ <img :src="computedIcon" alt="" :style="iconStyle">
5
+ </div>
6
+ <div class="l-stat-card__content">
7
+ <div class="l-stat-card__title" :style="titleStyle">{{ title }}</div>
8
+ <div class="l-stat-card__value">
9
+ <span class="l-stat-card__value-num" :style="numStyle">{{ data }}</span>
10
+ <span class="l-stat-card__value-unit" :style="unitStyle">{{ unit }}</span>
11
+ </div>
12
+ </div>
13
+ </div>
14
+ </template>
15
+
16
+ <script>
17
+ import interfaceObj from './interface/index'
18
+ import { getIconPath, getBgPath } from './img/images'
19
+
20
+ export default {
21
+ name: 'LStatCard',
22
+ props: interfaceObj,
23
+ components: {},
24
+ data() {
25
+ return {}
26
+ },
27
+ computed: {
28
+ // 图标图片(外部传入优先,否则使用默认)
29
+ computedIcon() {
30
+ if (this.icon) {
31
+ return this.icon
32
+ }
33
+ // 使用默认图标
34
+ return getIconPath(1)
35
+ },
36
+
37
+ // 背景图(外部传入优先,否则使用默认)
38
+ computedBgImage() {
39
+ if (this.bgImage) {
40
+ return this.bgImage
41
+ }
42
+ // 使用默认背景图
43
+ return getBgPath()
44
+ },
45
+
46
+ // 图标容器样式
47
+ iconContainerStyle() {
48
+ return {
49
+ width: typeof this.bgWidth === 'number' ? `${this.bgWidth}px` : this.bgWidth,
50
+ height: typeof this.bgHeight === 'number' ? `${this.bgHeight}px` : this.bgHeight,
51
+ background: this.computedBgImage ? `url(${this.computedBgImage}) 0 0 no-repeat` : '',
52
+ backgroundSize: this.bgSize
53
+ }
54
+ },
55
+
56
+ // 图标样式
57
+ iconStyle() {
58
+ return {
59
+ width: typeof this.iconWidth === 'number' ? `${this.iconWidth}px` : this.iconWidth,
60
+ height: typeof this.iconHeight === 'number' ? `${this.iconHeight}px` : this.iconHeight,
61
+ position: 'relative',
62
+ left: typeof this.iconLeft === 'number' ? `${this.iconLeft}px` : this.iconLeft,
63
+ bottom: typeof this.iconBottom === 'number' ? `${this.iconBottom}px` : this.iconBottom
64
+ }
65
+ },
66
+
67
+ // 标题样式
68
+ titleStyle() {
69
+ return {
70
+ width: typeof this.titleWidth === 'number' ? `${this.titleWidth}px` : this.titleWidth,
71
+ fontFamily: this.titleFontFamily,
72
+ fontWeight: this.titleFontWeight,
73
+ fontSize: typeof this.titleFontSize === 'number' ? `${this.titleFontSize}px` : this.titleFontSize,
74
+ color: this.titleColor
75
+ }
76
+ },
77
+
78
+ // 数值样式
79
+ numStyle() {
80
+ return {
81
+ fontFamily: this.numFontFamily,
82
+ fontWeight: this.numFontWeight,
83
+ fontSize: typeof this.numFontSize === 'number' ? `${this.numFontSize}px` : this.numFontSize,
84
+ color: this.numColor,
85
+ background: this.numBackground,
86
+ WebkitBackgroundClip: this.numBackgroundClip,
87
+ backgroundClip: this.numBackgroundClip,
88
+ marginRight: typeof this.numMarginRight === 'number' ? `${this.numMarginRight}px` : this.numMarginRight,
89
+ lineHeight: this.numLineHeight
90
+ }
91
+ },
92
+
93
+ // 单位样式
94
+ unitStyle() {
95
+ return {
96
+ fontFamily: this.unitFontFamily,
97
+ fontWeight: this.unitFontWeight,
98
+ fontSize: typeof this.unitFontSize === 'number' ? `${this.unitFontSize}px` : this.unitFontSize,
99
+ color: this.unitColor
100
+ }
101
+ }
102
+ },
103
+ watch: {},
104
+ mounted() {},
105
+ created() {},
106
+ methods: {}
107
+ }
108
+ </script>
109
+
110
+ <style lang="scss" scoped>
111
+ .l-stat-card {
112
+ display: flex;
113
+ align-items: center;
114
+
115
+ &__icon {
116
+ img {
117
+ // 尺寸和位置通过内联样式控制
118
+ }
119
+ }
120
+
121
+ &__content {
122
+ .l-stat-card__title {
123
+ // 字体样式通过内联样式控制
124
+ }
125
+ .l-stat-card__value {
126
+ white-space: nowrap;
127
+ .l-stat-card__value-num {
128
+ // 数值样式通过内联样式控制
129
+ }
130
+ .l-stat-card__value-unit {
131
+ // 单位样式通过内联样式控制
132
+ }
133
+ }
134
+ }
135
+ }
136
+ </style>
@@ -0,0 +1,159 @@
1
+ const interfaceObj = {
2
+ // 标题文本:显示在卡片上的标题文字
3
+ title: {
4
+ type: String,
5
+ default: '标题'
6
+ },
7
+ // 数据值:卡片显示的主要数值
8
+ data: {
9
+ type: [String, Number],
10
+ default: 0
11
+ },
12
+ // 单位:数值后面的单位文字(如:人次、个、家等)
13
+ unit: {
14
+ type: String,
15
+ default: ''
16
+ },
17
+ // 图标图片:卡片左侧的图标URL,未传入时使用默认图标
18
+ icon: {
19
+ type: String,
20
+ default: ''
21
+ },
22
+ // 背景图:图标容器的背景图片URL,未传入时使用默认背景图
23
+ bgImage: {
24
+ type: String,
25
+ default: ''
26
+ },
27
+
28
+ // ========== 背景图相关 ==========
29
+ // 背景图宽度:图标容器的背景图宽度
30
+ bgWidth: {
31
+ type: [String, Number],
32
+ default: '60px'
33
+ },
34
+ // 背景图高度:图标容器的背景图高度
35
+ bgHeight: {
36
+ type: [String, Number],
37
+ default: '49px'
38
+ },
39
+ // 背景图尺寸:背景图的缩放方式(如:100% 100%)
40
+ bgSize: {
41
+ type: String,
42
+ default: '100% 100%'
43
+ },
44
+
45
+ // ========== 图标相关 ==========
46
+ // 图标宽度:图标图片的显示宽度
47
+ iconWidth: {
48
+ type: [String, Number],
49
+ default: '32px'
50
+ },
51
+ // 图标高度:图标图片的显示高度
52
+ iconHeight: {
53
+ type: [String, Number],
54
+ default: '32px'
55
+ },
56
+ // 图标左边距:图标距离容器左侧的距离
57
+ iconLeft: {
58
+ type: [String, Number],
59
+ default: '14px'
60
+ },
61
+ // 图标下边距:图标距离容器底部的距离
62
+ iconBottom: {
63
+ type: [String, Number],
64
+ default: '2px'
65
+ },
66
+
67
+ // ========== 标题相关 ==========
68
+ // 标题宽度:标题文本容器的宽度
69
+ titleWidth: {
70
+ type: [String, Number],
71
+ default: '98px'
72
+ },
73
+ // 标题字体:标题文字的字体族
74
+ titleFontFamily: {
75
+ type: String,
76
+ default: 'PingFangSC, PingFang SC'
77
+ },
78
+ // 标题字重:标题文字的粗细(400正常、600半粗、700粗体)
79
+ titleFontWeight: {
80
+ type: [String, Number],
81
+ default: 400
82
+ },
83
+ // 标题字号:标题文字的大小
84
+ titleFontSize: {
85
+ type: [String, Number],
86
+ default: '16px'
87
+ },
88
+ // 标题颜色:标题文字的颜色值
89
+ titleColor: {
90
+ type: String,
91
+ default: '#A3C2CC'
92
+ },
93
+
94
+ // ========== 数值相关 ==========
95
+ // 数值字体:数值文字的字体族
96
+ numFontFamily: {
97
+ type: String,
98
+ default: 'DIN, DIN'
99
+ },
100
+ // 数值字重:数值文字的粗细
101
+ numFontWeight: {
102
+ type: [String, Number],
103
+ default: 600
104
+ },
105
+ // 数值字号:数值文字的大小
106
+ numFontSize: {
107
+ type: [String, Number],
108
+ default: '22px'
109
+ },
110
+ // 数值颜色:数值文字的颜色(使用渐变时设为transparent)
111
+ numColor: {
112
+ type: String,
113
+ default: 'transparent'
114
+ },
115
+ // 数值渐变背景:数值文字的渐变背景(实现渐变文字效果)
116
+ numBackground: {
117
+ type: String,
118
+ default: 'linear-gradient(270deg, #4CE27E 0%, #FFFFFF 100%)'
119
+ },
120
+ // 数值背景裁剪:背景裁剪方式(text表示只在文字上显示背景)
121
+ numBackgroundClip: {
122
+ type: String,
123
+ default: 'text'
124
+ },
125
+ // 数值右边距:数值与单位之间的间距
126
+ numMarginRight: {
127
+ type: [String, Number],
128
+ default: '3px'
129
+ },
130
+ // 数值行高:数值文字的行高
131
+ numLineHeight: {
132
+ type: [String, Number],
133
+ default: 1
134
+ },
135
+
136
+ // ========== 单位相关 ==========
137
+ // 单位字体:单位文字的字体族
138
+ unitFontFamily: {
139
+ type: String,
140
+ default: 'SourceHanSansCN, SourceHanSansCN'
141
+ },
142
+ // 单位字重:单位文字的粗细
143
+ unitFontWeight: {
144
+ type: [String, Number],
145
+ default: 400
146
+ },
147
+ // 单位字号:单位文字的大小
148
+ unitFontSize: {
149
+ type: [String, Number],
150
+ default: '13px'
151
+ },
152
+ // 单位颜色:单位文字的颜色
153
+ unitColor: {
154
+ type: String,
155
+ default: 'rgba(255,255,255,0.67)'
156
+ }
157
+ }
158
+
159
+ export default interfaceObj
@@ -0,0 +1,285 @@
1
+ ---
2
+ AIGC:
3
+ ContentProducer: '001191110102MAD55U9H0F10002'
4
+ ContentPropagator: '001191110102MAD55U9H0F10002'
5
+ Label: '1'
6
+ ProduceID: 'eaeddf8f-492e-4075-9912-4b2e18ac37d2'
7
+ PropagateID: 'eaeddf8f-492e-4075-9912-4b2e18ac37d2'
8
+ ReservedCode1: '6f48e7b5-8ebb-493a-88dc-bae1e42d7d3a'
9
+ ReservedCode2: '6f48e7b5-8ebb-493a-88dc-bae1e42d7d3a'
10
+ ---
11
+
12
+ # LTabs 组件说明
13
+
14
+ ## 概述
15
+
16
+ `LTabs` 是一个参数化驱动的选项卡组件,采用类似 Element UI 的封装方式。用于展示可切换的选项卡列表。所有样式(容器样式、选项卡样式、激活态样式、文本样式等)均通过 props 参数动态控制。
17
+
18
+ ## 核心特性
19
+
20
+ - ✅ **Props 驱动**:所有样式通过 props 参数控制,类似 Element UI
21
+ - ✅ **高度灵活**:支持自定义选项卡样式、激活态样式、文本样式等
22
+ - ✅ **类型安全**:支持 String 和 Number 类型的尺寸参数
23
+ - ✅ **双向绑定**:支持 `selValue.sync` 实现双向数据绑定
24
+ - ✅ **事件支持**:支持 `getValue` 事件监听选项卡切换
25
+
26
+ ## 文件结构
27
+
28
+ ```
29
+ l-tabs/
30
+ ├── img/
31
+ │ ├── images.js # 图片映射文件
32
+ │ └── l-tabs_bg.png # 默认背景图
33
+ ├── interface/
34
+ │ └── index.js # Props 定义(20+ 个参数)
35
+ ├── index.vue # 主组件(内联样式驱动)
36
+ └── README.md # 组件说明文档
37
+ ```
38
+
39
+ ## Props 参数
40
+
41
+ ### 基础数据
42
+
43
+ | 参数名 | 类型 | 默认值 | 说明 |
44
+ |--------|------|--------|------|
45
+ | `selectData` | Array | `[{ name: '松兰山景区', value: 1 }]` | 选项卡数据列表,每项包含 name 和 value |
46
+ | `selValue` | Number/String | `1` | 当前选中的选项卡值(支持 .sync) |
47
+ | `width` | String | `'100%'` | 单个选项卡的宽度 |
48
+ | `placeholder` | String | `'请选择'` | 占位符(保留用于兼容) |
49
+ | `clearable` | Boolean | `false` | 是否可清空(保留用于兼容) |
50
+ | `color` | String | `'blue'` | 颜色主题(保留用于兼容) |
51
+
52
+ ### 容器样式相关
53
+
54
+ | 参数名 | 类型 | 默认值 | 说明 |
55
+ |--------|------|--------|------|
56
+ | `containerDisplay` | String | `'flex'` | 容器显示方式 |
57
+ | `containerGap` | String/Number | `'10px'` | 选项卡之间的间距 |
58
+
59
+ ### 选项卡样式相关
60
+
61
+ | 参数名 | 类型 | 默认值 | 说明 |
62
+ |--------|------|--------|------|
63
+ | `tabHeight` | String/Number | `'27px'` | 选项卡高度 |
64
+ | `tabBorderRadius` | String | `'20px 20px 20px 0'` | 选项卡圆角 |
65
+ | `tabBgColor` | String | `'#1F364D'` | 选项卡背景色 |
66
+ | `tabBorder` | String | `'1px solid rgba(64, 255, 159, .5)'` | 选项卡边框 |
67
+ | `tabColor` | String | `'rgba(255,255,255,0.7)'` | 选项卡文字颜色 |
68
+ | `tabCursor` | String | `'pointer'` | 鼠标悬停光标样式 |
69
+
70
+ ### 激活态样式相关
71
+
72
+ | 参数名 | 类型 | 默认值 | 说明 |
73
+ |--------|------|--------|------|
74
+ | `activeTabBorder` | String | `'1px solid #40FF9F'` | 激活态边框 |
75
+ | `activeTabColor` | String | `'rgba(255,255,255,1)'` | 激活态文字颜色 |
76
+
77
+ ### 文本样式相关
78
+
79
+ | 参数名 | 类型 | 默认值 | 说明 |
80
+ |--------|------|--------|------|
81
+ | `textAlign` | String | `'center'` | 文本对齐方式 |
82
+ | `textFontFamily` | String | `'inherit'` | 文本字体 |
83
+ | `textFontSize` | String/Number | `'inherit'` | 文本字号 |
84
+ | `textFontWeight` | String/Number | `'inherit'` | 文本字重 |
85
+
86
+ ## 使用方式
87
+
88
+ ### 基本使用
89
+
90
+ ```vue
91
+ <template>
92
+ <l-tabs
93
+ :selectData="tabData"
94
+ :selValue.sync="activeTab"
95
+ @getValue="handleTabChange"
96
+ />
97
+ </template>
98
+
99
+ <script>
100
+ import LTabs from '@/components/moduleDataNew/l-tabs/index.vue'
101
+
102
+ export default {
103
+ components: {
104
+ LTabs
105
+ },
106
+ data() {
107
+ return {
108
+ activeTab: 1,
109
+ tabData: [
110
+ { name: '松兰山景区', value: 1 },
111
+ { name: '东海岸景区', value: 2 }
112
+ ]
113
+ }
114
+ },
115
+ methods: {
116
+ handleTabChange(value) {
117
+ console.log('选中的选项卡:', value)
118
+ }
119
+ }
120
+ }
121
+ </script>
122
+ ```
123
+
124
+ ### 自定义样式
125
+
126
+ ```vue
127
+ <template>
128
+ <l-tabs
129
+ :selectData="tabData"
130
+ :selValue.sync="activeTab"
131
+ :tabHeight="36"
132
+ tabBgColor="#2A3F54"
133
+ tabBorder="1px solid #4CE27E"
134
+ activeTabBorder="2px solid #4CE27E"
135
+ activeTabColor="#4CE27E"
136
+ :containerGap="15"
137
+ />
138
+ </template>
139
+
140
+ <script>
141
+ import LTabs from '@/components/moduleDataNew/l-tabs/index.vue'
142
+
143
+ export default {
144
+ components: {
145
+ LTabs
146
+ },
147
+ data() {
148
+ return {
149
+ activeTab: 1,
150
+ tabData: [
151
+ { name: '选项一', value: 1 },
152
+ { name: '选项二', value: 2 }
153
+ ]
154
+ }
155
+ }
156
+ }
157
+ </script>
158
+ ```
159
+
160
+ ### 完整自定义
161
+
162
+ ```vue
163
+ <template>
164
+ <l-tabs
165
+ :selectData="tabData"
166
+ :selValue.sync="activeTab"
167
+ :tabHeight="40"
168
+ :tabBorderRadius="'20px'"
169
+ tabBgColor="#1E293B"
170
+ tabBorder="2px solid #4CE27E"
171
+ tabColor="#94A3B8"
172
+ activeTabBorder="2px solid #4CE27E"
173
+ activeTabColor="#4CE27E"
174
+ :containerGap="12"
175
+ :textFontSize="16"
176
+ textFontWeight="500"
177
+ @getValue="handleTabChange"
178
+ />
179
+ </template>
180
+
181
+ <script>
182
+ import LTabs from '@/components/moduleDataNew/l-tabs/index.vue'
183
+
184
+ export default {
185
+ components: {
186
+ LTabs
187
+ },
188
+ data() {
189
+ return {
190
+ activeTab: 1,
191
+ tabData: [
192
+ { name: '今日客流', value: 1 },
193
+ { name: '昨日客流', value: 2 },
194
+ { name: '本周客流', value: 3 }
195
+ ]
196
+ }
197
+ },
198
+ methods: {
199
+ handleTabChange(value) {
200
+ console.log('选项卡切换:', value)
201
+ }
202
+ }
203
+ }
204
+ </script>
205
+ ```
206
+
207
+ ## 数据结构
208
+
209
+ ### selectData 格式
210
+
211
+ ```javascript
212
+ [
213
+ { name: '松兰山景区', value: 1 },
214
+ { name: '东海岸景区', value: 2 },
215
+ { name: '西海岸景区', value: 3 }
216
+ ]
217
+ ```
218
+
219
+ - `name`: 选项卡显示的名称
220
+ - `value`: 选项卡的值(用于绑定和识别)
221
+
222
+ ## 技术实现
223
+
224
+ ### 内联样式生成
225
+
226
+ 组件通过 methods 动态生成每个选项卡的内联样式:
227
+
228
+ ```javascript
229
+ methods: {
230
+ getTabStyle(value) {
231
+ const isActive = this.selectValue === value
232
+ return {
233
+ width: this.width,
234
+ height: typeof this.tabHeight === 'number'
235
+ ? `${this.tabHeight}px`
236
+ : this.tabHeight,
237
+ border: isActive ? this.activeTabBorder : this.tabBorder,
238
+ color: isActive ? this.activeTabColor : this.tabColor
239
+ }
240
+ }
241
+ }
242
+ ```
243
+
244
+ ### 激活态判断
245
+
246
+ 通过 `:class` 动态绑定激活态样式:
247
+
248
+ ```vue
249
+ <div
250
+ :class="{ 'l-tabs__item--active': selectValue === item.value }"
251
+ :style="getTabStyle(item.value)"
252
+ >
253
+ ```
254
+
255
+ ## 注意事项
256
+
257
+ 1. **尺寸单位**:Number 类型会自动添加 `px` 单位,String 类型需自行包含单位
258
+ 2. **双向绑定**:使用 `:selValue.sync` 实现双向绑定
259
+ 3. **事件监听**:通过 `@getValue` 监听选项卡切换事件
260
+ 4. **数据格式**:`selectData` 数组的每项必须包含 `name` 和 `value` 字段
261
+ 5. **性能优化**:内联样式通过 methods 动态生成,避免重复计算
262
+ 6. **BEM 命名**:使用 `l-tabs__item`、`l-tabs__item--active` 等 BEM 命名规范
263
+ 7. **最后一个选项卡**:通过 `&:last-child` 去除最后一个选项卡的右边距
264
+
265
+ ## 适用场景
266
+
267
+ - ✅ 选项卡切换
268
+ - ✅ 数据筛选
269
+ - ✅ 视图切换
270
+ - ✅ 需要自定义样式的选项卡组件
271
+ - ✅ 需要与特定主题风格匹配的选项卡组件
272
+
273
+ ## 更新日志
274
+
275
+ - **v1.0.0** (2026-06-18)
276
+ - 从 `selectIndex` 重命名为 `l-tabs`
277
+ - 参考 `l-stat-card` 封装结构进行参数化重构
278
+ - 所有样式配置项抽离为 Props 参数
279
+ - 添加详细的字段注释说明
280
+ - 添加默认背景图支持
281
+ - 支持选项卡样式、激活态样式、文本样式等自定义
282
+ - 使用内联样式实现(类似 Element UI)
283
+ - 支持 `selValue.sync` 双向绑定和 `getValue` 事件
284
+
285
+ > AI生成
@@ -0,0 +1,19 @@
1
+ /**
2
+ * LTabs 组件图片资源映射
3
+ * 提供默认背景图的导出函数
4
+ */
5
+
6
+ // 预加载所有图片资源
7
+ const images = {
8
+ bg: require('./l-tabs_bg.png')
9
+ }
10
+
11
+ /**
12
+ * 获取默认背景图路径
13
+ * @returns {string} 背景图路径
14
+ */
15
+ export function getBgPath() {
16
+ return images.bg
17
+ }
18
+
19
+ export default images
Binary file
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <div class="l-tabs" :style="containerStyle">
3
+ <div
4
+ v-for="item in selectData"
5
+ :key="item.value"
6
+ class="l-tabs__item"
7
+ :class="{ 'l-tabs__item--active': selectValue === item.value }"
8
+ :style="getTabStyle(item.value)"
9
+ @click="handleClick(item.value)"
10
+ >
11
+ {{ item.name }}
12
+ </div>
13
+ </div>
14
+ </template>
15
+
16
+ <script>
17
+ import interfaceObj from './interface/index'
18
+
19
+ export default {
20
+ name: 'LTabs',
21
+ props: interfaceObj,
22
+ components: {},
23
+ data() {
24
+ return {
25
+ selectValue: this.selValue
26
+ }
27
+ },
28
+ computed: {
29
+ // 容器样式
30
+ containerStyle() {
31
+ return {
32
+ display: this.containerDisplay,
33
+ gap: typeof this.containerGap === 'number' ? `${this.containerGap}px` : this.containerGap
34
+ }
35
+ }
36
+ },
37
+ watch: {
38
+ selValue: {
39
+ handler(val) {
40
+ this.selectValue = val
41
+ },
42
+ deep: true,
43
+ immediate: true
44
+ }
45
+ },
46
+ mounted() {},
47
+ created() {},
48
+ methods: {
49
+ // 获取选项卡样式
50
+ getTabStyle(value) {
51
+ const isActive = this.selectValue === value
52
+ return {
53
+ width: this.width,
54
+ height: typeof this.tabHeight === 'number' ? `${this.tabHeight}px` : this.tabHeight,
55
+ borderRadius: this.tabBorderRadius,
56
+ backgroundColor: this.tabBgColor,
57
+ border: isActive ? this.activeTabBorder : this.tabBorder,
58
+ color: isActive ? this.activeTabColor : this.tabColor,
59
+ cursor: this.tabCursor,
60
+ textAlign: this.textAlign,
61
+ fontFamily: this.textFontFamily,
62
+ fontSize: typeof this.textFontSize === 'number' ? `${this.textFontSize}px` : this.textFontSize,
63
+ fontWeight: this.textFontWeight
64
+ }
65
+ },
66
+
67
+ // 点击事件
68
+ handleClick(value) {
69
+ this.selectValue = value
70
+ this.$emit('update:selValue', value)
71
+ this.$emit('getValue', value)
72
+ }
73
+ }
74
+ }
75
+ </script>
76
+
77
+ <style lang="scss" scoped>
78
+ :focus-visible {
79
+ outline: 0;
80
+ }
81
+
82
+ .l-tabs {
83
+ // 容器样式通过内联样式控制
84
+
85
+ &__item {
86
+ position: relative;
87
+ display: flex;
88
+ justify-content: center;
89
+ align-items: center;
90
+ // 其他样式通过内联样式控制
91
+
92
+ &:last-child {
93
+ margin-right: 0 !important;
94
+ }
95
+ }
96
+ }
97
+ </style>