@coffic/cosy-ui 0.9.68 → 0.9.71

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 (33) hide show
  1. package/dist/app.css +1 -1
  2. package/dist/src/components/button/buttonPropsBase.ts +1 -1
  3. package/dist/src/components/button/class-all.ts +21 -1
  4. package/dist/src/components/container/validate-sizing.ts +3 -4
  5. package/dist/src/components/link/class-all.ts +1 -3
  6. package/dist/src/components/link/linkPropsBase.ts +2 -0
  7. package/dist/src/utils/language.ts +2 -2
  8. package/dist/src/utils/link.ts +13 -13
  9. package/dist/src-astro/alert/Alert.astro +0 -3
  10. package/dist/src-astro/contact/Contact.astro +1 -1
  11. package/dist/src-astro/grid/Grid.astro +1 -1
  12. package/dist/src-astro/header/Header.astro +1 -1
  13. package/dist/src-astro/header/HeaderCenter.astro +1 -1
  14. package/dist/src-astro/header/MobileNav.astro +1 -1
  15. package/dist/src-astro/layout-basic/BaseLayout.astro +2 -2
  16. package/dist/src-astro/layout-dashboard/DashboardLayout.astro +1 -1
  17. package/dist/src-astro/link/types.ts +2 -4
  18. package/dist/src-vue/apple-phone/ApplePhone.vue +151 -133
  19. package/dist/src-vue/button/Button.vue +1 -0
  20. package/dist/src-vue/button/class.ts +21 -1
  21. package/dist/src-vue/image-display/ImageGrid.vue +1 -3
  22. package/dist/src-vue/image-display/ImageItem.vue +1 -3
  23. package/dist/src-vue/image-display/ImagePreview.vue +1 -3
  24. package/dist/src-vue/image-display/types.ts +1 -3
  25. package/dist/src-vue/key-catcher/KeyCatcher.vue +1 -1
  26. package/dist/src-vue/link/Link.vue +2 -0
  27. package/dist/src-vue/review/Review.vue +13 -3
  28. package/dist/src-vue/review/Reviews.vue +69 -0
  29. package/dist/src-vue/review/index.ts +5 -1
  30. package/dist/src-vue/review/props.ts +1 -10
  31. package/dist/src-vue/status-bar/StatusBarItem.vue +1 -3
  32. package/dist/src-vue/utils/link.ts +14 -14
  33. package/package.json +2 -17
@@ -11,7 +11,7 @@ export interface IButtonPropsBase {
11
11
  /**
12
12
  * 自定义 CSS 类名,用于覆盖默认样式
13
13
  */
14
- class?: string;
14
+ class?: string | { [key: string]: any };
15
15
 
16
16
  /**
17
17
  * 是否禁用按钮,设置为 true 时按钮不可点击
@@ -6,6 +6,23 @@ import { getButtonShapeClass } from "./class-shape";
6
6
  import { getButtonModifierClasses } from "./class-modifiers";
7
7
  import { getButtonGradientClass } from "./class-gradient";
8
8
 
9
+ /**
10
+ * 将可能的对象形式的 class 转换为字符串
11
+ * @param className 类名,可以是字符串或对象
12
+ * @returns 字符串形式的类名
13
+ */
14
+ function normalizeClass(className: string | object | undefined): string {
15
+ if (!className) return "";
16
+ if (typeof className === "string") return className;
17
+ if (typeof className === "object") {
18
+ return Object.entries(className)
19
+ .filter(([, value]) => value)
20
+ .map(([key]) => key)
21
+ .join(" ");
22
+ }
23
+ return "";
24
+ }
25
+
9
26
  /**
10
27
  * 计算 Button 组件的组合类名(用于基础接口)
11
28
  * @param props Button 组件的基础 props
@@ -22,6 +39,9 @@ export function getBaseButtonClasses(props: IButtonPropsBase): string[] {
22
39
  class: className = "",
23
40
  } = props;
24
41
 
42
+ // 规范化 class 属性
43
+ const normalizedClass = normalizeClass(className);
44
+
25
45
  // 构建基础类名
26
46
  const baseClasses = getButtonBaseClasses();
27
47
 
@@ -48,7 +68,7 @@ export function getBaseButtonClasses(props: IButtonPropsBase): string[] {
48
68
  shapeClass,
49
69
  ...modifierClasses,
50
70
  gradientClass,
51
- className,
71
+ normalizedClass,
52
72
  ];
53
73
 
54
74
  return classes;
@@ -8,16 +8,15 @@ export function validateSizing(props: Record<string, unknown>): string[] {
8
8
 
9
9
  // 检查属性是否被显式设置(不为 undefined 且不为默认值)
10
10
  const isAspectRatioSet =
11
- Object.prototype.hasOwnProperty.call(props, "aspectRatio") &&
12
- props.aspectRatio !== undefined;
11
+ Object.hasOwn(props, "aspectRatio") && props.aspectRatio !== undefined;
13
12
 
14
13
  const isHeightSet =
15
- Object.prototype.hasOwnProperty.call(props, "height") &&
14
+ Object.hasOwn(props, "height") &&
16
15
  props.height !== undefined &&
17
16
  props.height !== "none";
18
17
 
19
18
  const isWidthSet =
20
- Object.prototype.hasOwnProperty.call(props, "width") &&
19
+ Object.hasOwn(props, "width") &&
21
20
  props.width !== undefined &&
22
21
  props.width !== "none";
23
22
 
@@ -34,13 +34,11 @@ export function getBaseLinkClasses(props: ILinkPropsBase): string[] {
34
34
  active = false,
35
35
  navigationType,
36
36
  icon,
37
- hoverImage,
38
- hoverImageAlt,
39
37
  ...rest
40
38
  } = props;
41
39
 
42
40
  // 构建基础类名
43
- const baseClasses = getLinkBaseClasses(block, icon, hoverImage);
41
+ const baseClasses = getLinkBaseClasses(block, icon, rest["hoverImage"]);
44
42
 
45
43
  // 构建变体类名
46
44
  const variantClasses = getLinkVariantClasses(
@@ -104,4 +104,6 @@ export interface ILinkPropsBase {
104
104
  active?: boolean;
105
105
  navigationType?: "previous" | "next";
106
106
  icon?: LinkIconName;
107
+ hoverImage?: string;
108
+ hoverImageAlt?: string;
107
109
  }
@@ -34,13 +34,13 @@ export class LanguageUtil {
34
34
  return astroLang;
35
35
  }
36
36
  // 尝试从URL中获取语言
37
- const urlLang = this.getLanguageFromURL(astro.url.pathname);
37
+ const urlLang = LanguageUtil.getLanguageFromURL(astro.url.pathname);
38
38
  if (urlLang) {
39
39
  return urlLang;
40
40
  }
41
41
 
42
42
  // 尝试从浏览器设置中获取语言
43
- const browserLang = this.getLanguageFromBrowser();
43
+ const browserLang = LanguageUtil.getLanguageFromBrowser();
44
44
  if (browserLang) {
45
45
  return browserLang;
46
46
  }
@@ -32,7 +32,7 @@ export class LinkUtil {
32
32
  }
33
33
 
34
34
  static getHomeLink(lang: string): string {
35
- return `${this.getBaseUrl()}/${lang}`;
35
+ return `${LinkUtil.getBaseUrl()}/${lang}`;
36
36
  }
37
37
 
38
38
  static getLessonsLink(lang: string): string {
@@ -62,10 +62,10 @@ export class LinkUtil {
62
62
 
63
63
  static getLessonLink(lang: string, lessonId: string): string {
64
64
  if (lessonId.endsWith(lang)) {
65
- return `${this.getBaseUrl()}${lang}/lessons/${lessonId.replace(`${lang}`, "")}`;
65
+ return `${LinkUtil.getBaseUrl()}${lang}/lessons/${lessonId.replace(`${lang}`, "")}`;
66
66
  } else {
67
67
  const idWithoutLang = lessonId.replace(`${lang}/`, "");
68
- return `${this.getBaseUrl()}${lang}/lessons/${idWithoutLang}`;
68
+ return `${LinkUtil.getBaseUrl()}${lang}/lessons/${idWithoutLang}`;
69
69
  }
70
70
  }
71
71
 
@@ -97,43 +97,43 @@ export class LinkUtil {
97
97
  }
98
98
 
99
99
  static getMetaLink(lang: string, slug: string): string {
100
- return `/${this.normalizeLanguage(lang)}/meta/${slug}`;
100
+ return `/${LinkUtil.normalizeLanguage(lang)}/meta/${slug}`;
101
101
  }
102
102
 
103
103
  static getSigninLink(lang: string): string {
104
- return `/${this.normalizeLanguage(lang)}/signin`;
104
+ return `/${LinkUtil.normalizeLanguage(lang)}/signin`;
105
105
  }
106
106
 
107
107
  static getAuthCallbackCookieLink(lang: string): string {
108
- return `/${this.normalizeLanguage(lang)}/auth/callback_cookie`;
108
+ return `/${LinkUtil.normalizeLanguage(lang)}/auth/callback_cookie`;
109
109
  }
110
110
 
111
111
  static getAuthCallbackTokenLink(lang: string): string {
112
- return `/${this.normalizeLanguage(lang)}/auth/callback_token`;
112
+ return `/${LinkUtil.normalizeLanguage(lang)}/auth/callback_token`;
113
113
  }
114
114
 
115
115
  static getAuthAccountLink(lang: string): string {
116
- return `/${this.normalizeLanguage(lang)}/auth/account`;
116
+ return `/${LinkUtil.normalizeLanguage(lang)}/auth/account`;
117
117
  }
118
118
 
119
119
  static getDashboardUrl(lang: string): string {
120
- return `/${this.normalizeLanguage(lang)}/auth/dashboard`;
120
+ return `/${LinkUtil.normalizeLanguage(lang)}/auth/dashboard`;
121
121
  }
122
122
 
123
123
  static getAuthErrorLink(lang: string): string {
124
- return `/${this.normalizeLanguage(lang)}/auth/error`;
124
+ return `/${LinkUtil.normalizeLanguage(lang)}/auth/error`;
125
125
  }
126
126
 
127
127
  static getPrivacyLink(lang: string): string {
128
- return this.getMetaLink(lang, "privacy");
128
+ return LinkUtil.getMetaLink(lang, "privacy");
129
129
  }
130
130
 
131
131
  static getTermsLink(lang: string): string {
132
- return this.getMetaLink(lang, "terms");
132
+ return LinkUtil.getMetaLink(lang, "terms");
133
133
  }
134
134
 
135
135
  static getAboutLink(lang: string): string {
136
- return this.getMetaLink(lang, "about");
136
+ return LinkUtil.getMetaLink(lang, "about");
137
137
  }
138
138
 
139
139
  /**
@@ -73,7 +73,6 @@ import {
73
73
  ErrorIcon,
74
74
  CloseIcon,
75
75
  } from "../../index-astro";
76
- import { marginClasses, type MarginSize } from "../../src/common/margin";
77
76
 
78
77
  // 根据类型设置图标组件
79
78
  const getIconComponent = (type: string) => {
@@ -95,8 +94,6 @@ const {
95
94
  class: className = "",
96
95
  closable = true,
97
96
  showIcon = true,
98
- variant = "solid",
99
- marginY,
100
97
  } = props;
101
98
 
102
99
  // 获取图标组件
@@ -108,7 +108,7 @@
108
108
  import "../../style.ts";
109
109
  import Card from "../card/Card.astro";
110
110
  import Link from "../link/Link.astro";
111
- import { type BackgroundColor } from "../../src/common/backgrounds";
111
+ import type { BackgroundColor } from "../../src/common/backgrounds";
112
112
  import {
113
113
  FacebookIcon,
114
114
  GithubIcon,
@@ -150,7 +150,7 @@ const getColsClasses = (cols: ResponsiveValue<number>) => {
150
150
  }
151
151
  }
152
152
  // 响应式对象
153
- let result: string[] = [];
153
+ const result: string[] = [];
154
154
  const responsive = cols as any;
155
155
  if (responsive.base) {
156
156
  switch (responsive.base) {
@@ -104,7 +104,7 @@ const linkHeightClass =
104
104
  linkHeightClasses[height as keyof typeof linkHeightClasses];
105
105
 
106
106
  // 检查 i18n 是否启用,通过 Astro.currentLocale 来判断
107
- let isI18nEnabled = Astro.currentLocale !== undefined;
107
+ const isI18nEnabled = Astro.currentLocale !== undefined;
108
108
 
109
109
  const currentPath = Astro.url.pathname;
110
110
  const activeLink = LinkUtil.getActiveLink(
@@ -24,7 +24,7 @@
24
24
  * - linkHeightClass - 链接高度类名
25
25
  * - gap - 导航项之间的间距,可选值:0, 1, 2, 3, 4, 6, 8, 10, 12,默认 2
26
26
  */
27
- import { type INavItem } from "../../index-astro";
27
+ import type { INavItem } from "../../index-astro";
28
28
  import NavItems from "./NavItems.astro";
29
29
 
30
30
  export interface Props {
@@ -20,7 +20,7 @@
20
20
  * - activeLink - 当前激活的链接
21
21
  * - linkHeightClass - 链接高度类名
22
22
  */
23
- import { type INavItem } from "../../index-astro";
23
+ import type { INavItem } from "../../index-astro";
24
24
  import NavItems from "./NavItems.astro";
25
25
 
26
26
  export interface Props {
@@ -60,7 +60,7 @@
60
60
  */
61
61
 
62
62
  import "../../style.ts";
63
- import { type IMetaProps } from "../../src/common/meta";
63
+ import type { IMetaProps } from "../../src/common/meta";
64
64
  import {
65
65
  getBackgroundClass,
66
66
  type BackgroundColor,
@@ -101,7 +101,7 @@ const {
101
101
 
102
102
  // 处理类名
103
103
  const backgroundClass = getBackgroundClass(background as BackgroundColor);
104
- let bodyClasses = [backgroundClass].filter(Boolean).join(" ");
104
+ const bodyClasses = [backgroundClass].filter(Boolean).join(" ");
105
105
  ---
106
106
 
107
107
  <BaseLayoutHtml
@@ -129,7 +129,7 @@ import DashboardSidebar from "./DashboardSidebar.astro";
129
129
  import DashboardTopNavbar from "./DashboardTopNavbar.astro";
130
130
  import { ToastContainer, ConfirmDialog } from "../../index-astro";
131
131
  import "../../style.ts";
132
- import { type NavItem, type SidebarSize, type UserMenuItem } from "./types";
132
+ import type { NavItem, SidebarSize, UserMenuItem } from "./types";
133
133
 
134
134
  export interface Props {
135
135
  /**
@@ -1,9 +1,7 @@
1
1
  import type { HTMLAttributes } from "astro/types";
2
2
 
3
- // 定义链接尺寸类型
4
3
  export type LinkSize = "sm" | "md" | "lg";
5
4
 
6
- // 定义链接变体类型
7
5
  export type LinkVariant =
8
6
  | "default"
9
7
  | "primary"
@@ -15,14 +13,12 @@ export type LinkVariant =
15
13
  | "navigation"
16
14
  | "github";
17
15
 
18
- // 定义链接动画类型
19
16
  export type LinkAnimation =
20
17
  | "none"
21
18
  | "hover-lift"
22
19
  | "hover-glow"
23
20
  | "hover-scale";
24
21
 
25
- // 定义图标类型(排除需要特殊属性的组件)
26
22
  export type LinkIconName =
27
23
  | "AlertTriangle"
28
24
  | "AppStoreIcon"
@@ -103,4 +99,6 @@ export interface LinkProps extends HTMLAttributes<"a"> {
103
99
  active?: boolean;
104
100
  navigationType?: "previous" | "next";
105
101
  icon?: LinkIconName;
102
+ hoverImage?: string;
103
+ hoverImageAlt?: string;
106
104
  }
@@ -1,152 +1,170 @@
1
1
  <script setup lang="ts">
2
- import "../../style";
3
- import { AlertDialog, Container } from "../../index-vue";
4
- import { ref } from "vue";
5
- import type { BackgroundColor } from "../container/backgrounds";
6
- import iphoneFrame from "./assets/iPhone 14 Pro - Deep Purple - Portrait.png";
7
- import StatusBarContent from "./StatusBarContent.vue";
2
+ import '../../style';
3
+ import { AlertDialog, Container } from '../../index-vue';
4
+ import { ref } from 'vue';
5
+ import type { BackgroundColor } from '../../src/common/backgrounds';
6
+ import iphoneFrame from './assets/iPhone 14 Pro - Deep Purple - Portrait.png';
7
+ import StatusBarContent from './StatusBarContent.vue';
8
8
 
9
- /**
10
- * @component ApplePhone
11
- * @description ApplePhone 组件模拟 iPhone 设备的外观,包含状态栏、时间显示和设备边框。
12
- * 适用于创建移动应用界面原型或展示移动端设计效果。
13
- * @props
14
- * @prop {'sm'|'md'|'lg'|'xl'|'2xl'|'3xl'|'4xl'|'5xl'} [height='lg'] - 窗口高度选项
15
- * - sm: 256px (h-64)
16
- * - md: 320px (h-80)
17
- * - lg: 384px (h-96) - 默认值
18
- * - xl: 480px
19
- * - 2xl: 560px
20
- * - 3xl: 640px
21
- * - 4xl: 720px
22
- * - 5xl: 800px
23
- * @prop {String} [title=''] - 窗口标题
24
- * @prop {Boolean} [withShadow=true] - 是否显示阴影效果
25
- * @prop {Boolean} [showFrame=true] - 是否显示 iPhone 边框
26
- * @prop {BackgroundColor} [backgroundColor=''] - 内容区域背景色,等同于为其内部的 Container 设置背景色
27
- * @slots
28
- * @slot default - 主要内容区域
29
- * @emits
30
- */
9
+ /**
10
+ * @component ApplePhone
11
+ * @description ApplePhone 组件模拟 iPhone 设备的外观,包含状态栏、时间显示和设备边框。
12
+ * 适用于创建移动应用界面原型或展示移动端设计效果。
13
+ * @props
14
+ * @prop {'sm'|'md'|'lg'|'xl'|'2xl'|'3xl'|'4xl'|'5xl'} [height='lg'] - 窗口高度选项
15
+ * - sm: 256px (h-64)
16
+ * - md: 320px (h-80)
17
+ * - lg: 384px (h-96) - 默认值
18
+ * - xl: 480px
19
+ * - 2xl: 560px
20
+ * - 3xl: 640px
21
+ * - 4xl: 720px
22
+ * - 5xl: 800px
23
+ * @prop {String} [title=''] - 窗口标题
24
+ * @prop {Boolean} [withShadow=true] - 是否显示阴影效果
25
+ * @prop {Boolean} [showFrame=true] - 是否显示 iPhone 边框
26
+ * @prop {BackgroundColor} [backgroundColor=''] - 内容区域背景色,等同于为其内部的 Container 设置背景色
27
+ * @slots
28
+ * @slot default - 主要内容区域
29
+ * @emits
30
+ */
31
31
 
32
- // 类型定义
33
- type HeightOption = "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
32
+ // 类型定义
33
+ type HeightOption = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl';
34
34
 
35
- interface Props {
36
- height?: HeightOption;
37
- title?: string;
38
- withShadow?: boolean;
39
- showFrame?: boolean;
40
- backgroundColor?: BackgroundColor;
41
- }
35
+ interface Props {
36
+ height?: HeightOption;
37
+ title?: string;
38
+ withShadow?: boolean;
39
+ showFrame?: boolean;
40
+ backgroundColor?: BackgroundColor;
41
+ }
42
42
 
43
- // Props 定义
44
- const props = withDefaults(defineProps<Props>(), {
45
- height: "lg",
46
- title: "",
47
- withShadow: true,
48
- showFrame: true,
49
- backgroundColor: undefined,
50
- });
43
+ // Props 定义
44
+ const props = withDefaults(defineProps<Props>(), {
45
+ height: 'lg',
46
+ title: '',
47
+ withShadow: true,
48
+ showFrame: true,
49
+ backgroundColor: undefined,
50
+ });
51
51
 
52
- // iPhone边框图片-宽度
53
- const iphoneFrameWidth = 1339;
54
- // iPhone边框图片-高度
55
- const iphoneFrameHeight = 2716;
56
- // iPhone边框图片-状态栏离上边框的距离
57
- const iphoneFrameStatusBarTop = 115;
58
- // iPhone边框图片-状态栏高度
59
- const iphoneFrameStatusBarHeight = 110;
52
+ // iPhone边框图片-宽度
53
+ const iphoneFrameWidth = 1339;
54
+ // iPhone边框图片-高度
55
+ const iphoneFrameHeight = 2716;
56
+ // iPhone边框图片-状态栏离上边框的距离
57
+ const iphoneFrameStatusBarTop = 115;
58
+ // iPhone边框图片-状态栏高度
59
+ const iphoneFrameStatusBarHeight = 110;
60
60
 
61
- // 比例-总宽度
62
- const mainContentWidthAspectRatio = 1179 / iphoneFrameWidth;
63
- // 比例-总高度
64
- const mainContentHeightAspectRatio = 2556 / iphoneFrameHeight;
65
- // 比例-状态栏高度
66
- const iphoneFrameStatusBarHeightAspectRatio =
67
- iphoneFrameStatusBarHeight / iphoneFrameHeight;
68
- // 比例-状态栏离上边框的距离
69
- const iphoneFrameStatusBarTopAspectRatio =
70
- iphoneFrameStatusBarTop / iphoneFrameHeight;
61
+ // 比例-总宽度
62
+ const mainContentWidthAspectRatio = 1179 / iphoneFrameWidth;
63
+ // 比例-总高度
64
+ const mainContentHeightAspectRatio = 2556 / iphoneFrameHeight;
65
+ // 比例-状态栏高度
66
+ const iphoneFrameStatusBarHeightAspectRatio =
67
+ iphoneFrameStatusBarHeight / iphoneFrameHeight;
68
+ // 比例-状态栏离上边框的距离
69
+ const iphoneFrameStatusBarTopAspectRatio =
70
+ iphoneFrameStatusBarTop / iphoneFrameHeight;
71
71
 
72
- // 预定义的高度选项
73
- const heightClasses: Record<HeightOption, string> = {
74
- sm: "cosy:h-64", // 256px
75
- md: "cosy:h-80", // 320px
76
- lg: "cosy:h-96", // 384px
77
- xl: "cosy:h-[480px]", // 480px
78
- "2xl": "cosy:h-[560px]", // 560px
79
- "3xl": "cosy:h-[640px]", // 640px
80
- "4xl": "cosy:h-[720px]", // 720px
81
- "5xl": "cosy:h-[800px]", // 800px
82
- };
72
+ // 预定义的高度选项
73
+ const heightClasses: Record<HeightOption, string> = {
74
+ sm: 'cosy:h-64', // 256px
75
+ md: 'cosy:h-80', // 320px
76
+ lg: 'cosy:h-96', // 384px
77
+ xl: 'cosy:h-[480px]', // 480px
78
+ '2xl': 'cosy:h-[560px]', // 560px
79
+ '3xl': 'cosy:h-[640px]', // 640px
80
+ '4xl': 'cosy:h-[720px]', // 720px
81
+ '5xl': 'cosy:h-[800px]', // 800px
82
+ };
83
83
 
84
- // 响应式数据
85
- const showAlertDialog = ref(false);
86
- const alertMessage = ref("");
84
+ // 响应式数据
85
+ const showAlertDialog = ref(false);
86
+ const alertMessage = ref('');
87
87
 
88
- // 计算当前高度的缩放比例
89
- const getScaleRatio = () => {
90
- const heightValues = {
91
- sm: 256,
92
- md: 320,
93
- lg: 384,
94
- xl: 480,
95
- "2xl": 560,
96
- "3xl": 640,
97
- "4xl": 720,
98
- "5xl": 800,
99
- };
100
- const currentHeight = heightValues[props.height];
101
- // 基于特定高度计算缩放比例
102
- return currentHeight / 500;
103
- };
88
+ // 计算当前高度的缩放比例
89
+ const getScaleRatio = () => {
90
+ const heightValues = {
91
+ sm: 256,
92
+ md: 320,
93
+ lg: 384,
94
+ xl: 480,
95
+ '2xl': 560,
96
+ '3xl': 640,
97
+ '4xl': 720,
98
+ '5xl': 800,
99
+ };
100
+ const currentHeight = heightValues[props.height];
101
+ // 基于特定高度计算缩放比例
102
+ return currentHeight / 500;
103
+ };
104
104
 
105
- // 计算属性
106
- const iphoneFrameSrc = (iphoneFrame as any).src || iphoneFrame;
105
+ // 计算属性
106
+ const iphoneFrameSrc = (iphoneFrame as any).src || iphoneFrame;
107
107
  </script>
108
108
 
109
109
  <template>
110
- <div :class="['cosy:relative not-prose cosy:mx-auto', heightClasses[height]]" :style="{
111
- aspectRatio: `${iphoneFrameWidth}/${iphoneFrameHeight}`,
112
- }" apple-phone>
113
- <!-- iPhone 边框 -->
114
- <img v-if="showFrame"
115
- style="max-width: 100%; max-height: 100%; position: absolute; top: 0; left: 0; z-index: 100;"
116
- :src="iphoneFrameSrc" alt="iPhone frame" />
110
+ <div
111
+ :class="['cosy:relative not-prose cosy:mx-auto', heightClasses[height]]"
112
+ :style="{
113
+ aspectRatio: `${iphoneFrameWidth}/${iphoneFrameHeight}`,
114
+ }"
115
+ apple-phone>
116
+ <!-- iPhone 边框 -->
117
+ <img
118
+ v-if="showFrame"
119
+ style="
120
+ max-width: 100%;
121
+ max-height: 100%;
122
+ position: absolute;
123
+ top: 0;
124
+ left: 0;
125
+ z-index: 100;
126
+ "
127
+ :src="iphoneFrameSrc"
128
+ alt="iPhone frame" />
117
129
 
118
- <!-- 顶部状态栏 -->
119
- <div :style="{
120
- position: 'absolute',
121
- top: iphoneFrameStatusBarTopAspectRatio * 100 + '%',
122
- height: iphoneFrameStatusBarHeightAspectRatio * 100 + '%',
123
- width: mainContentWidthAspectRatio * 100 + '%',
124
- left: '50%',
125
- transform: 'translate(-50%, 0)',
126
- paddingLeft: '5%',
127
- paddingRight: '5%',
128
- zIndex: 50,
129
- }">
130
- <StatusBarContent :scaleRatio="getScaleRatio()" />
131
- </div>
130
+ <!-- 顶部状态栏 -->
131
+ <div
132
+ :style="{
133
+ position: 'absolute',
134
+ top: iphoneFrameStatusBarTopAspectRatio * 100 + '%',
135
+ height: iphoneFrameStatusBarHeightAspectRatio * 100 + '%',
136
+ width: mainContentWidthAspectRatio * 100 + '%',
137
+ left: '50%',
138
+ transform: 'translate(-50%, 0)',
139
+ paddingLeft: '5%',
140
+ paddingRight: '5%',
141
+ zIndex: 50,
142
+ }">
143
+ <StatusBarContent :scaleRatio="getScaleRatio()" />
144
+ </div>
132
145
 
133
- <!-- 内容区域 -->
134
- <div class="cosy:inset-0 cosy:h-full" :style="{
135
- width: mainContentWidthAspectRatio * 100 + '%',
136
- height: mainContentHeightAspectRatio * 100 + '%',
137
- // 水平居中
138
- left: '50%',
139
- // 垂直居中
140
- top: '50%',
141
- transform: 'translate(-50%, -50%)',
142
- position: 'absolute',
143
- zIndex: 20,
144
- }">
145
- <Container rounded="lg" style="height: 100%;" :background="backgroundColor || 'accent/90'">
146
- <slot />
147
- </Container>
148
- </div>
146
+ <!-- 内容区域 -->
147
+ <div
148
+ class="cosy:inset-0 cosy:h-full"
149
+ :style="{
150
+ width: mainContentWidthAspectRatio * 100 + '%',
151
+ height: mainContentHeightAspectRatio * 100 + '%',
152
+ // 水平居中
153
+ left: '50%',
154
+ // 垂直居中
155
+ top: '50%',
156
+ transform: 'translate(-50%, -50%)',
157
+ position: 'absolute',
158
+ zIndex: 20,
159
+ }">
160
+ <Container
161
+ rounded="lg"
162
+ style="height: 100%"
163
+ :background="backgroundColor || 'accent/90'">
164
+ <slot />
165
+ </Container>
149
166
  </div>
167
+ </div>
150
168
 
151
- <AlertDialog v-model="showAlertDialog" :message="alertMessage" />
169
+ <AlertDialog v-model="showAlertDialog" :message="alertMessage" />
152
170
  </template>
@@ -18,6 +18,7 @@ import { getButtonCombinedClassesVue } from "./class";
18
18
  * @props {string} [href] - 链接地址,设置后按钮变为链接形式
19
19
  * @props {string} [target] - 链接目标,支持 _self、_blank、_parent、_top
20
20
  * @props {Function} [onClick] - 点击事件处理函数
21
+ * @props {string|object} [class] - 自定义 CSS 类名,支持字符串或对象形式
21
22
  */
22
23
 
23
24
  const props = withDefaults(defineProps<IButtonProps>(), {