@coffic/cosy-ui 0.8.17 → 0.8.18

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.
package/dist/index-vue.ts CHANGED
@@ -29,8 +29,8 @@ export * from './src-vue/icons/index';
29
29
  export * from './src-vue/list/index';
30
30
 
31
31
  // Windows
32
- // export { MacWindowExamples, MacWindowExampleCodes, MacWindow } from './vue/MacWindow';
33
- // export { iPhoneWindowExamples, iPhoneWindowExampleCodes, iPhoneWindow } from './vue/iPhone';
32
+ export * from './src-vue/mac-window/index';
33
+ export * from './src-vue/iPhone/index';
34
34
 
35
35
  // SmartLink
36
36
  // export { default as SmartLink } from './vue/SmartLink.vue';
@@ -0,0 +1,121 @@
1
+ <!--
2
+ @component StatusBarContent
3
+
4
+ @description
5
+ StatusBarContent 组件显示 iPhone 状态栏的内容,包括时间、信号、WiFi 和电池图标。
6
+ 组件会自动更新时间显示,并支持根据设备大小进行缩放。
7
+
8
+ @usage
9
+ 基本用法:
10
+ ```vue
11
+ <StatusBarContent />
12
+ ```
13
+
14
+ 带缩放比例:
15
+ ```vue
16
+ <StatusBarContent :scaleRatio="1.5" />
17
+ ```
18
+
19
+ @props
20
+ @prop {Number} [scaleRatio=1] - 缩放比例,用于根据设备大小调整文字和图标大小
21
+ @slots
22
+ @emits
23
+ -->
24
+ <script lang="ts">
25
+ import { defineComponent, ref, onMounted, onUnmounted, computed } from 'vue';
26
+ import { IPhoneSignalIcon, IPhoneWifiIcon, IPhoneBatteryIcon } from '../icons';
27
+ import '../../style.ts';
28
+
29
+ export default defineComponent({
30
+ name: 'StatusBarContent',
31
+ components: {
32
+ IPhoneSignalIcon,
33
+ IPhoneWifiIcon,
34
+ IPhoneBatteryIcon,
35
+ },
36
+ props: {
37
+ scaleRatio: {
38
+ type: Number,
39
+ default: 1,
40
+ },
41
+ },
42
+ setup(props) {
43
+ const currentTime = ref('12:00');
44
+
45
+ // 更新时间的函数
46
+ const updateTime = () => {
47
+ const now = new Date();
48
+ const hours = now.getHours().toString().padStart(2, '0');
49
+ const minutes = now.getMinutes().toString().padStart(2, '0');
50
+ currentTime.value = `${hours}:${minutes}`;
51
+ };
52
+
53
+ // 计算缩放后的字体大小
54
+ const scaledFontSize = computed(() => {
55
+ const baseFontSize = 14; // 基础字体大小
56
+ return `${baseFontSize * props.scaleRatio}px`;
57
+ });
58
+
59
+ // 计算缩放后的图标尺寸
60
+ const scaledIconSize = computed(() => {
61
+ const baseIconSize = 15; // 基础图标宽度
62
+ return `${baseIconSize * props.scaleRatio}px`;
63
+ });
64
+
65
+ // 计算缩放后的图标高度百分比
66
+ const scaledIconHeight = computed(() => {
67
+ const baseHeight = 60; // 基础高度百分比
68
+ return `${baseHeight * props.scaleRatio}%`;
69
+ });
70
+
71
+ // 设置定时器更新时间
72
+ let timeInterval: number;
73
+ onMounted(() => {
74
+ updateTime();
75
+ timeInterval = window.setInterval(updateTime, 60000); // 每分钟更新一次
76
+ });
77
+
78
+ onUnmounted(() => {
79
+ if (timeInterval) {
80
+ clearInterval(timeInterval);
81
+ }
82
+ });
83
+
84
+ return {
85
+ currentTime,
86
+ scaledFontSize,
87
+ scaledIconSize,
88
+ scaledIconHeight,
89
+ };
90
+ },
91
+ });
92
+ </script>
93
+
94
+ <template>
95
+ <div class="cosy:flex cosy:items-center cosy:h-full cosy:justify-between">
96
+ <!-- 左侧时间 -->
97
+ <span class="cosy:font-medium time-text" :style="{ fontSize: scaledFontSize }">
98
+ {{ currentTime }}
99
+ </span>
100
+
101
+ <!-- 右侧状态图标 -->
102
+ <div class="cosy:flex cosy:flex-row cosy:items-center cosy:space-x-1 cosy:h-full">
103
+ <div :style="{ width: scaledIconSize, height: scaledIconHeight, minWidth: 0, minHeight: 0 }">
104
+ <IPhoneBatteryIcon />
105
+ </div>
106
+ </div>
107
+ </div>
108
+ </template>
109
+
110
+ <style scoped>
111
+ /* 确保图标渲染更平滑 */
112
+ svg {
113
+ shape-rendering: geometricPrecision;
114
+ }
115
+
116
+ /* 时间文字基础样式 */
117
+ .time-text {
118
+ line-height: 1;
119
+ transition: font-size 0.2s ease;
120
+ }
121
+ </style>
@@ -27,8 +27,30 @@ iPhoneWindow 组件模拟 iPhone 设备的外观,包含状态栏、时间显
27
27
  </iPhoneWindow>
28
28
  ```
29
29
 
30
+ 自定义高度:
31
+ ```vue
32
+ <iPhoneWindow height="md">
33
+ <div>应用内容</div>
34
+ </iPhoneWindow>
35
+ ```
36
+
37
+ 使用较大高度:
38
+ ```vue
39
+ <iPhoneWindow height="2xl">
40
+ <div>应用内容</div>
41
+ </iPhoneWindow>
42
+ ```
43
+
30
44
  @props
31
- @prop {String} [height='h-96'] - 窗口高度
45
+ @prop {'sm'|'md'|'lg'|'xl'|'2xl'|'3xl'|'4xl'|'5xl'} [height='lg'] - 窗口高度选项
46
+ - sm: 256px (h-64)
47
+ - md: 320px (h-80)
48
+ - lg: 384px (h-96) - 默认值
49
+ - xl: 480px
50
+ - 2xl: 560px
51
+ - 3xl: 640px
52
+ - 4xl: 720px
53
+ - 5xl: 800px
32
54
  @prop {String} [title=''] - 窗口标题
33
55
  @prop {Array} [statusBarButtons=[]] - 状态栏按钮数组
34
56
  @prop {Boolean} [withShadow=true] - 是否显示阴影效果
@@ -43,205 +65,170 @@ iPhoneWindow 组件模拟 iPhone 设备的外观,包含状态栏、时间显
43
65
  <script lang="ts">
44
66
  import '../../style.ts';
45
67
  import { AlertDialog } from '../../index-vue.ts';
46
- import { ref, onMounted, onUnmounted, defineComponent } from 'vue';
47
- // 导入内部图片资源
68
+ import { ref, defineComponent } from 'vue';
48
69
  import iphoneFrame from './assets/iPhone 14 Pro - Deep Purple - Portrait.png';
70
+ import StatusBarContent from './StatusBarContent.vue';
71
+
72
+ // iPhone边框图片-宽度
73
+ const iphoneFrameWidth = 1339;
74
+ // iPhone边框图片-高度
75
+ const iphoneFrameHeight = 2716;
76
+ // iPhone边框图片-状态栏离上边框的距离
77
+ const iphoneFrameStatusBarTop = 115;
78
+ // iPhone边框图片-状态栏高度
79
+ const iphoneFrameStatusBarHeight = 110;
80
+
81
+ // 比例-总宽度
82
+ const mainContentWidthAspectRatio = 1179 / iphoneFrameWidth;
83
+ // 比例-总高度
84
+ const mainContentHeightAspectRatio = 2556 / iphoneFrameHeight;
85
+ // 比例-状态栏高度
86
+ const iphoneFrameStatusBarHeightAspectRatio = iphoneFrameStatusBarHeight / iphoneFrameHeight;
87
+ // 比例-状态栏离上边框的距离
88
+ const iphoneFrameStatusBarTopAspectRatio = iphoneFrameStatusBarTop / iphoneFrameHeight;
89
+
90
+ // 预定义的高度选项
91
+ type HeightOption = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl';
92
+
93
+ const heightClasses: Record<HeightOption, string> = {
94
+ sm: 'cosy:h-64', // 256px
95
+ md: 'cosy:h-80', // 320px
96
+ lg: 'cosy:h-96', // 384px
97
+ xl: 'cosy:h-[480px]', // 480px
98
+ '2xl': 'cosy:h-[560px]', // 560px
99
+ '3xl': 'cosy:h-[640px]', // 640px
100
+ '4xl': 'cosy:h-[720px]', // 720px
101
+ '5xl': 'cosy:h-[800px]', // 800px
102
+ };
49
103
 
50
104
  export default defineComponent({
51
- name: 'iPhoneWindow',
52
- components: {
53
- AlertDialog,
54
- },
55
- props: {
56
- height: {
57
- type: String,
58
- default: 'h-96',
59
- },
60
- title: {
61
- type: String,
62
- default: '',
63
- },
64
- statusBarButtons: {
65
- type: Array,
66
- default: () => [],
105
+ name: 'iPhoneWindow',
106
+ components: {
107
+ AlertDialog,
108
+ StatusBarContent,
67
109
  },
68
- withShadow: {
69
- type: Boolean,
70
- default: true,
110
+ props: {
111
+ height: {
112
+ type: String as () => HeightOption,
113
+ default: 'lg',
114
+ validator: (value: string) => {
115
+ return Object.keys(heightClasses).includes(value);
116
+ },
117
+ },
118
+ debug: {
119
+ type: Boolean,
120
+ default: false,
121
+ },
122
+ title: {
123
+ type: String,
124
+ default: '',
125
+ },
126
+ statusBarButtons: {
127
+ type: Array,
128
+ default: () => [],
129
+ },
130
+ withShadow: {
131
+ type: Boolean,
132
+ default: true,
133
+ },
134
+ showFrame: {
135
+ type: Boolean,
136
+ default: true,
137
+ },
138
+ backgroundColor: {
139
+ type: String,
140
+ default: '',
141
+ },
71
142
  },
72
- showFrame: {
73
- type: Boolean,
74
- default: true,
143
+ setup(props) {
144
+ const showAlertDialog = ref(false);
145
+ const alertMessage = ref('');
146
+
147
+ // 获取图片URL
148
+ const frameSrc =
149
+ typeof iphoneFrame === 'string' ? iphoneFrame : iphoneFrame.src;
150
+
151
+ // 计算当前高度的缩放比例
152
+ const getScaleRatio = () => {
153
+ const heightValues = {
154
+ sm: 256,
155
+ md: 320,
156
+ lg: 384,
157
+ xl: 480,
158
+ '2xl': 560,
159
+ '3xl': 640,
160
+ '4xl': 720,
161
+ '5xl': 800,
162
+ };
163
+ const currentHeight = heightValues[props.height];
164
+ // 基于特定高度计算缩放比例
165
+ return currentHeight / 500;
166
+ };
167
+
168
+ return {
169
+ showAlertDialog,
170
+ alertMessage,
171
+ frameSrc,
172
+ heightClasses,
173
+ mainContentWidthAspectRatio,
174
+ mainContentHeightAspectRatio,
175
+ iphoneFrameWidth,
176
+ iphoneFrameHeight,
177
+ iphoneFrameStatusBarTop,
178
+ iphoneFrameStatusBarHeight,
179
+ iphoneFrameStatusBarHeightAspectRatio,
180
+ iphoneFrameStatusBarTopAspectRatio,
181
+ getScaleRatio,
182
+ };
75
183
  },
76
- backgroundColor: {
77
- type: String,
78
- default: '',
79
- },
80
- },
81
- setup() {
82
- const showAlertDialog = ref(false);
83
- const alertMessage = ref('');
84
- const currentTime = ref('12:00');
85
-
86
- // 获取图片URL
87
- const frameSrc =
88
- typeof iphoneFrame === 'string' ? iphoneFrame : iphoneFrame.src;
89
-
90
- // 更新时间的函数
91
- const updateTime = () => {
92
- const now = new Date();
93
- const hours = now.getHours().toString().padStart(2, '0');
94
- const minutes = now.getMinutes().toString().padStart(2, '0');
95
- currentTime.value = `${hours}:${minutes}`;
96
- };
97
-
98
- // 设置定时器更新时间
99
- let timeInterval: number;
100
- onMounted(() => {
101
- updateTime();
102
- timeInterval = window.setInterval(updateTime, 60000); // 每分钟更新一次
103
- });
104
-
105
- onUnmounted(() => {
106
- if (timeInterval) {
107
- clearInterval(timeInterval);
108
- }
109
- });
110
-
111
- return {
112
- showAlertDialog,
113
- alertMessage,
114
- currentTime,
115
- frameSrc,
116
- };
117
- },
118
184
  });
119
185
  </script>
120
186
 
121
187
  <template>
122
- <div class="cosy:relative cosy:w-full">
123
- <div class="cosy:relative cosy:aspect-[9/19.5]">
124
- <!-- iPhone 边框 (放在最底层) -->
125
- <img
126
- v-if="showFrame"
127
- :src="frameSrc"
128
- alt="iPhone frame"
129
- class="cosy:absolute cosy:inset-0 cosy:w-full cosy:h-full cosy:object-contain"
130
- />
131
-
132
- <!-- 内容区域 -->
133
- <div
134
- :class="[
135
- 'cosy:absolute cosy:inset-0',
136
- showFrame ? 'cosy:px-[6%] cosy:pt-[13%] cosy:pb-[13%]' : '',
137
- ]"
138
- >
139
- <div
140
- :class="[
141
- 'cosy:w-full cosy:h-full cosy:flex cosy:flex-col cosy:overflow-hidden',
142
- showFrame
143
- ? 'cosy:rounded-t-[2.5em] cosy:rounded-b-[2.5rem]'
144
- : 'cosy:rounded-lg cosy:shadow',
145
- backgroundColor || 'cosy:bg-transparent',
146
- ]"
147
- >
148
- <!-- 顶部状态栏 (z-index 设为负数,确保在边框下方) -->
149
- <div
150
- class="cosy:flex-none cosy:h-12 cosy:px-4 cosy:z-0 cosy:relative"
151
- >
152
- <div
153
- class="cosy:flex cosy:items-center cosy:h-full cosy:justify-between"
154
- >
155
- <!-- 左侧时间 -->
156
- <div class="cosy:flex cosy:items-center">
157
- <span
158
- class="cosy:text-sm cosy:font-medium cosy:text-gray-700 cosy:dark:text-gray-200"
159
- >{{ currentTime }}</span
160
- >
161
- </div>
162
- <!-- 右侧状态图标 -->
163
- <div class="cosy:flex cosy:items-center cosy:space-x-1.5">
164
- <!-- 信号图标 -->
165
- <svg
166
- class="cosy:w-5 cosy:h-3.5 cosy:text-gray-700 cosy:dark:text-gray-200"
167
- viewBox="0 0 20 12"
168
- fill="none"
169
- stroke="currentColor"
170
- >
171
- <path
172
- d="M1 11h2V6H1v5zm4 0h2V4H5v7zm4 0h2V2H9v9zm4 0h2V0h-2v11z"
173
- fill="currentColor"
174
- />
175
- <path
176
- d="M17 11h2V0h-2v11z"
177
- fill="currentColor"
178
- opacity="0.4"
179
- />
180
- </svg>
181
- <!-- Wi-Fi图标 -->
182
- <svg
183
- class="cosy:w-5 cosy:h-4 cosy:text-gray-700 cosy:dark:text-gray-200"
184
- viewBox="0 0 16 12"
185
- fill="currentColor"
186
- >
187
- <path
188
- d="M8 10.5a1 1 0 100-2 1 1 0 000 2zM4.25 7.25a5 5 0 017.5 0l-1.06 1.06a3.5 3.5 0 00-5.38 0L4.25 7.25z"
189
- />
190
- <path
191
- d="M1.75 4.75a8.5 8.5 0 0112.5 0l-1.06 1.06a7 7 0 00-10.38 0L1.75 4.75z"
192
- />
193
- </svg>
194
- <!-- 电池图标 -->
195
- <div class="cosy:flex cosy:items-center cosy:space-x-0.5">
196
- <svg
197
- class="cosy:w-6 cosy:h-3.5 cosy:text-gray-700 cosy:dark:text-gray-200"
198
- viewBox="0 0 25 12"
199
- fill="none"
200
- stroke="currentColor"
201
- >
202
- <rect
203
- x="0.5"
204
- y="0.5"
205
- width="21"
206
- height="11"
207
- rx="2.5"
208
- stroke-width="1"
209
- />
210
- <rect
211
- x="2"
212
- y="2"
213
- width="18"
214
- height="8"
215
- rx="1"
216
- fill="currentColor"
217
- />
218
- <path
219
- d="M23 4h1a1 1 0 011 1v2a1 1 0 01-1 1h-1V4z"
220
- fill="currentColor"
221
- />
222
- </svg>
223
- </div>
224
- </div>
188
+ <div :class="['cosy:relative', heightClasses[height]]" :style="{
189
+ aspectRatio: `${iphoneFrameWidth}/${iphoneFrameHeight}`,
190
+ // 调试模式,背景色为半透明的黄色
191
+ backgroundColor: debug ? 'rgba(255, 255, 0, 0.3)' : 'transparent',
192
+ }">
193
+ <!-- iPhone 边框 -->
194
+ <img v-if="showFrame" style="max-width: 100%; max-height: 100%;" :src="frameSrc" alt="iPhone frame" />
195
+
196
+ <!-- 顶部状态栏 -->
197
+ <div :style="{
198
+ position: 'absolute',
199
+ top: iphoneFrameStatusBarTopAspectRatio * 100 + '%',
200
+ height: iphoneFrameStatusBarHeightAspectRatio * 100 + '%',
201
+ width: mainContentWidthAspectRatio * 100 + '%',
202
+ left: '50%',
203
+ transform: 'translate(-50%, 0)',
204
+ paddingLeft: '5%',
205
+ paddingRight: '5%',
206
+ // 调试模式,背景色为半透明的红色
207
+ backgroundColor: debug ? 'rgba(255, 0, 0, 0.3)' : 'transparent',
208
+ zIndex: 10,
209
+ }">
210
+ <StatusBarContent :scaleRatio="getScaleRatio()" />
211
+ </div>
212
+
213
+ <!-- 内容区域 -->
214
+ <div class="cosy:inset-0 cosy:h-full" :style="{
215
+ width: mainContentWidthAspectRatio * 100 + '%',
216
+ height: mainContentHeightAspectRatio * 100 + '%',
217
+ // 水平居中
218
+ left: '50%',
219
+ // 垂直居中
220
+ top: '50%',
221
+ transform: 'translate(-50%, -50%)',
222
+ position: 'absolute',
223
+ // 调试模式,背景色为半透明的蓝色
224
+ backgroundColor: debug ? 'rgba(0, 0, 255, 0.3)' : 'transparent',
225
+ zIndex: 10,
226
+ }">
227
+ <div :class="[debug ? 'cosy:bg-green-300/50' : '', 'cosy:h-full cosy:w-full']">
228
+ <slot />
225
229
  </div>
226
- </div>
227
-
228
- <!-- 主要内容区域 -->
229
- <div
230
- class="cosy:flex-1 cosy:flex cosy:flex-col cosy:overflow-hidden cosy:relative"
231
- >
232
- <slot />
233
- </div>
234
230
  </div>
235
- </div>
236
231
  </div>
237
- </div>
238
- <!-- 添加 AlertDialog 组件 -->
239
- <AlertDialog v-model="showAlertDialog" :message="alertMessage" />
240
- </template>
241
-
242
- <style scoped>
243
- /* 确保图标渲染更平滑 */
244
- svg {
245
- shape-rendering: geometricPrecision;
246
- }
247
- </style>
232
+
233
+ <AlertDialog v-model="showAlertDialog" :message="alertMessage" />
234
+ </template>
@@ -1,31 +1,27 @@
1
1
  import Basic from './Basic.vue';
2
- import WeatherApp from './WeatherApp.vue';
3
2
  import NoFrame from './NoFrame.vue';
4
3
  import CustomBackground from './CustomBackground.vue';
5
4
  import BasicSource from './Basic.vue?raw';
6
5
  import NoFrameSource from './NoFrame.vue?raw';
7
6
  import CustomBackgroundSource from './CustomBackground.vue?raw';
8
- import WeatherAppSource from './WeatherApp.vue?raw';
9
- import { extractSimpleExample } from '../../utils/component';
7
+ import { extractSimpleExample } from '../utils/component';
10
8
 
11
9
  // 导出主组件
12
10
  export { default as iPhoneWindow } from './iPhoneWindow.vue';
13
11
 
14
12
  // 将示例组件整合为一个对象导出
15
13
  export const iPhoneWindowExamples = {
16
- Basic,
17
- WeatherApp,
18
- NoFrame,
19
- CustomBackground,
14
+ Basic,
15
+ NoFrame,
16
+ CustomBackground,
20
17
  };
21
18
 
22
19
  // 导出示例组件的源代码(简化版本)
23
20
  export const iPhoneWindowExampleCodes = {
24
- Basic: extractSimpleExample(BasicSource, 'iPhoneWindow'),
25
- NoFrame: extractSimpleExample(NoFrameSource, 'iPhoneWindow'),
26
- CustomBackground: extractSimpleExample(
27
- CustomBackgroundSource,
28
- 'iPhoneWindow'
29
- ),
30
- WeatherApp: extractSimpleExample(WeatherAppSource, 'iPhoneWindow'),
21
+ Basic: extractSimpleExample(BasicSource, 'iPhoneWindow'),
22
+ NoFrame: extractSimpleExample(NoFrameSource, 'iPhoneWindow'),
23
+ CustomBackground: extractSimpleExample(
24
+ CustomBackgroundSource,
25
+ 'iPhoneWindow'
26
+ ),
31
27
  };
@@ -0,0 +1,7 @@
1
+ <template>
2
+ <svg width="100%" height="100%" viewBox="0 0 25 12" fill="none" stroke="currentColor" style="display: block;">
3
+ <rect x="0.5" y="0.5" width="21" height="11" rx="2.5" stroke-width="1" />
4
+ <rect x="2" y="2" width="18" height="8" rx="1" fill="currentColor" />
5
+ <path d="M23 4h1a1 1 0 011 1v2a1 1 0 01-1 1h-1V4z" fill="currentColor" />
6
+ </svg>
7
+ </template>
@@ -0,0 +1,6 @@
1
+ <template>
2
+ <svg width="100%" height="100%" viewBox="0 0 20 12" fill="none" stroke="currentColor" style="display: block;">
3
+ <path d="M1 11h2V6H1v5zm4 0h2V4H5v7zm4 0h2V2H9v9zm4 0h2V0h-2v11z" fill="currentColor" />
4
+ <path d="M17 11h2V0h-2v11z" fill="currentColor" opacity="0.4" />
5
+ </svg>
6
+ </template>
@@ -0,0 +1,6 @@
1
+ <template>
2
+ <svg width="100%" height="100%" viewBox="0 0 16 12" fill="currentColor" style="display: block;">
3
+ <path d="M8 10.5a1 1 0 100-2 1 1 0 000 2zM4.25 7.25a5 5 0 017.5 0l-1.06 1.06a3.5 3.5 0 00-5.38 0L4.25 7.25z" />
4
+ <path d="M1.75 4.75a8.5 8.5 0 0112.5 0l-1.06 1.06a7 7 0 00-10.38 0L1.75 4.75z" />
5
+ </svg>
6
+ </template>
@@ -17,3 +17,6 @@ export { default as SuccessIcon } from './SuccessIcon.vue';
17
17
  export { default as UserIcon } from './UserIcon.vue';
18
18
  export { default as WarningIcon } from './WarningIcon.vue';
19
19
  export { default as XCircleIcon } from './XCircleIcon.vue';
20
+ export { default as IPhoneSignalIcon } from './IPhoneSignalIcon.vue';
21
+ export { default as IPhoneWifiIcon } from './IPhoneWifiIcon.vue';
22
+ export { default as IPhoneBatteryIcon } from './IPhoneBatteryIcon.vue';