@coffic/cosy-ui 0.9.84 → 0.9.85

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.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Avatar 组件的基础属性接口(与框架无关)
3
+ */
4
+ export interface IAvatarPropsBase {
5
+ /**
6
+ * 用户名称
7
+ */
8
+ userName?: string;
9
+ /**
10
+ * 用户头像URL
11
+ */
12
+ avatar?: string;
13
+ /**
14
+ * 头像尺寸
15
+ * @default 'md'
16
+ */
17
+ size?: "sm" | "md" | "lg" | "xl";
18
+ }
19
+ //# sourceMappingURL=avatarPropsBase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatarPropsBase.d.ts","sourceRoot":"","sources":["../../../../src/components/avatar/avatarPropsBase.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CACjC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=avatarPropsBase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatarPropsBase.js","sourceRoot":"","sources":["../../../../src/components/avatar/avatarPropsBase.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Avatar 组件的共用工具函数
3
+ */
4
+ /**
5
+ * 尺寸映射类型
6
+ */
7
+ export declare const avatarSizeClasses: {
8
+ readonly sm: "cosy:w-8 cosy:h-8 cosy:text-sm";
9
+ readonly md: "cosy:w-12 cosy:h-12 cosy:text-lg";
10
+ readonly lg: "cosy:w-16 cosy:h-16 cosy:text-xl";
11
+ readonly xl: "cosy:w-20 cosy:h-20 cosy:text-2xl";
12
+ };
13
+ /**
14
+ * 头像背景色数组
15
+ */
16
+ export declare const avatarColors: readonly ["cosy:bg-primary", "cosy:bg-secondary", "cosy:bg-accent", "cosy:bg-info", "cosy:bg-success", "cosy:bg-warning", "cosy:bg-error"];
17
+ /**
18
+ * 生成头像背景色
19
+ * @param userName 用户名称
20
+ * @returns 背景色类名
21
+ */
22
+ export declare const getAvatarColor: (userName?: string) => string;
23
+ /**
24
+ * 获取用户名首字母(大写)
25
+ * @param userName 用户名称
26
+ * @returns 首字母或默认占位符
27
+ */
28
+ export declare const getAvatarInitial: (userName?: string) => string;
29
+ /**
30
+ * 获取尺寸类名
31
+ * @param size 尺寸
32
+ * @returns 尺寸类名
33
+ */
34
+ export declare const getAvatarSizeClass: (size?: keyof typeof avatarSizeClasses) => string;
35
+ //# sourceMappingURL=avatarUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatarUtils.d.ts","sourceRoot":"","sources":["../../../../src/components/avatar/avatarUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;CAKpB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,YAAY,4IAQf,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,KAAG,MAIlD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,KAAG,MAEpD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC9B,OAAM,MAAM,OAAO,iBAAwB,KACzC,MAEF,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Avatar 组件的共用工具函数
3
+ */
4
+ /**
5
+ * 尺寸映射类型
6
+ */
7
+ export const avatarSizeClasses = {
8
+ sm: "cosy:w-8 cosy:h-8 cosy:text-sm",
9
+ md: "cosy:w-12 cosy:h-12 cosy:text-lg",
10
+ lg: "cosy:w-16 cosy:h-16 cosy:text-xl",
11
+ xl: "cosy:w-20 cosy:h-20 cosy:text-2xl",
12
+ };
13
+ /**
14
+ * 头像背景色数组
15
+ */
16
+ export const avatarColors = [
17
+ "cosy:bg-primary",
18
+ "cosy:bg-secondary",
19
+ "cosy:bg-accent",
20
+ "cosy:bg-info",
21
+ "cosy:bg-success",
22
+ "cosy:bg-warning",
23
+ "cosy:bg-error",
24
+ ];
25
+ /**
26
+ * 生成头像背景色
27
+ * @param userName 用户名称
28
+ * @returns 背景色类名
29
+ */
30
+ export const getAvatarColor = (userName) => {
31
+ if (!userName)
32
+ return avatarColors[0]; // 默认使用第一个颜色
33
+ const index = userName.charCodeAt(0) % avatarColors.length;
34
+ return avatarColors[index];
35
+ };
36
+ /**
37
+ * 获取用户名首字母(大写)
38
+ * @param userName 用户名称
39
+ * @returns 首字母或默认占位符
40
+ */
41
+ export const getAvatarInitial = (userName) => {
42
+ return userName ? userName.charAt(0).toUpperCase() : "?";
43
+ };
44
+ /**
45
+ * 获取尺寸类名
46
+ * @param size 尺寸
47
+ * @returns 尺寸类名
48
+ */
49
+ export const getAvatarSizeClass = (size = "md") => {
50
+ return avatarSizeClasses[size];
51
+ };
52
+ //# sourceMappingURL=avatarUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatarUtils.js","sourceRoot":"","sources":["../../../../src/components/avatar/avatarUtils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAChC,EAAE,EAAE,gCAAgC;IACpC,EAAE,EAAE,kCAAkC;IACtC,EAAE,EAAE,kCAAkC;IACtC,EAAE,EAAE,mCAAmC;CAC9B,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B,iBAAiB;IACjB,mBAAmB;IACnB,gBAAgB;IAChB,cAAc;IACd,iBAAiB;IACjB,iBAAiB;IACjB,eAAe;CACN,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAiB,EAAU,EAAE;IAC3D,IAAI,CAAC,QAAQ;QAAE,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;IACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC;IAC3D,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,QAAiB,EAAU,EAAE;IAC7D,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AAC1D,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CACjC,OAAuC,IAAI,EAClC,EAAE;IACX,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type { IAvatarPropsBase } from "./avatarPropsBase";
2
+ export { avatarColors, avatarSizeClasses, getAvatarColor, getAvatarInitial, getAvatarSizeClass, } from "./avatarUtils";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/avatar/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EACN,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { avatarColors, avatarSizeClasses, getAvatarColor, getAvatarInitial, getAvatarSizeClass, } from "./avatarUtils";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/avatar/index.ts"],"names":[],"mappings":"AACA,OAAO,EACN,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,eAAe,CAAC"}
@@ -36,6 +36,6 @@ export interface ICardPropsBase {
36
36
  /**
37
37
  * 卡片标题
38
38
  */
39
- title: string;
39
+ title?: string;
40
40
  }
41
41
  //# sourceMappingURL=cardPropsBase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cardPropsBase.d.ts","sourceRoot":"","sources":["../../../../src/components/card/cardPropsBase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAE7B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IAEpB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACd"}
1
+ {"version":3,"file":"cardPropsBase.d.ts","sourceRoot":"","sources":["../../../../src/components/card/cardPropsBase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAE7B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IAEpB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CACf"}
@@ -1,9 +1,4 @@
1
1
  ---
2
-
3
-
4
-
5
-
6
-
7
2
  /**
8
3
  * @component Avatar
9
4
  *
@@ -33,7 +28,7 @@
33
28
  * ```
34
29
  *
35
30
  * @props
36
- * @prop {string} userName - 用户名称
31
+ * @prop {string} [userName] - 用户名称
37
32
  * @prop {string} [avatar] - 用户头像URL
38
33
  * @prop {'sm'|'md'|'lg'|'xl'} [size='md'] - 头像尺寸
39
34
  * @prop {string} [class] - 自定义类名
@@ -41,6 +36,11 @@
41
36
  */
42
37
 
43
38
  import { cn } from '../../src/class';
39
+ import {
40
+ getAvatarColor,
41
+ getAvatarInitial,
42
+ getAvatarSizeClass,
43
+ } from '../../src/components/avatar/avatarUtils';
44
44
  import type { AvatarProps } from './types';
45
45
 
46
46
  type Props = AvatarProps;
@@ -54,33 +54,7 @@ const {
54
54
  ...rest
55
55
  } = Astro.props;
56
56
 
57
- // 尺寸映射 - 使用 class builder
58
- const getSizeClasses = (size: 'sm' | 'md' | 'lg' | 'xl') => {
59
- const sizeMap = {
60
- sm: cn().add('cosy:w-8', 'cosy:h-8', 'cosy:text-sm').build(),
61
- md: cn().add('cosy:w-12', 'cosy:h-12', 'cosy:text-lg').build(),
62
- lg: cn().add('cosy:w-16', 'cosy:h-16', 'cosy:text-xl').build(),
63
- xl: cn().add('cosy:w-20', 'cosy:h-20', 'cosy:text-2xl').build(),
64
- };
65
- return sizeMap[size];
66
- };
67
-
68
- // 生成头像背景色 - 使用 class builder
69
- const getAvatarColor = (userName: string) => {
70
- const colors = [
71
- cn().add('cosy:bg-primary').build(),
72
- cn().add('cosy:bg-secondary').build(),
73
- cn().add('cosy:bg-accent').build(),
74
- cn().add('cosy:bg-info').build(),
75
- cn().add('cosy:bg-success').build(),
76
- cn().add('cosy:bg-warning').build(),
77
- cn().add('cosy:bg-error').build(),
78
- ];
79
- const index = userName.charCodeAt(0) % colors.length;
80
- return colors[index];
81
- };
82
-
83
- const sizeClass = getSizeClasses(size as 'sm' | 'md' | 'lg' | 'xl');
57
+ const sizeClass = getAvatarSizeClass(size as 'sm' | 'md' | 'lg' | 'xl');
84
58
 
85
59
  // 使用 classBuilder 构建各个部分的类名
86
60
  const avatarContainerClass = cn()
@@ -88,7 +62,7 @@ const avatarContainerClass = cn()
88
62
  'cosy:avatar',
89
63
  'cosy:rounded-full',
90
64
  'cosy:overflow-hidden',
91
- 'cosy:flex-shrink-0',
65
+ 'cosy:shrink-0',
92
66
  'cosy:bg-base-200',
93
67
  )
94
68
  .flex('row')
@@ -128,6 +102,6 @@ const avatarFallbackClass = cn()
128
102
  <div
129
103
  class={avatarFallbackClass}
130
104
  style={avatar ? 'display: none;' : 'display: flex;'}>
131
- {userName ? userName.charAt(0).toUpperCase() : '?'}
105
+ {getAvatarInitial(userName)}
132
106
  </div>
133
107
  </div>
@@ -1,22 +1,12 @@
1
1
  import type { HTMLAttributes } from "astro/types";
2
+ import type { IAvatarPropsBase } from "../../src/components/avatar/avatarPropsBase";
2
3
 
3
- export interface AvatarProps extends HTMLAttributes<"div"> {
4
- /**
5
- * 用户名称
6
- */
7
- userName: string;
8
-
9
- /**
10
- * 用户头像URL
11
- */
12
- avatar?: string;
13
-
14
- /**
15
- * 头像尺寸
16
- * @default 'md'
17
- */
18
- size?: "sm" | "md" | "lg" | "xl";
19
-
4
+ /**
5
+ * Avatar 组件的 Astro 版本属性接口(继承基础接口并扩展 Astro 特定属性)
6
+ */
7
+ export interface AvatarProps
8
+ extends IAvatarPropsBase,
9
+ Omit<HTMLAttributes<"div">, keyof IAvatarPropsBase> {
20
10
  /**
21
11
  * 自定义类名
22
12
  */
@@ -1,7 +1,4 @@
1
1
  ---
2
-
3
-
4
-
5
2
  /**
6
3
  * @component Card
7
4
  *
@@ -203,15 +200,17 @@ const Tag = href ? 'a' : 'article';
203
200
  {
204
201
  imageUrl && (
205
202
  <figure class={figureClass}>
206
- <Image src={imageUrl} alt={title} class={imageClass} />
203
+ <Image src={imageUrl} alt={title || ''} class={imageClass} />
207
204
  </figure>
208
205
  )
209
206
  }
210
207
 
211
208
  <div class={cardBodyClass}>
212
- <h2 class={cardTitleClass}>
213
- {title}
214
- </h2>
209
+ {title && (
210
+ <h2 class={cardTitleClass}>
211
+ {title}
212
+ </h2>
213
+ )}
215
214
 
216
215
  {subtitle && <p class={subtitleClass}>{subtitle}</p>}
217
216
 
@@ -9,7 +9,7 @@ import type { ImageSource } from "../image/types";
9
9
  export interface ICardProps
10
10
  extends ICardPropsBase,
11
11
  Omit<IContainerProps, "class" | "backgroundImage" | "title">,
12
- Omit<HTMLAttributes<"article">, "title"> {
12
+ Omit<HTMLAttributes<"article">, "title" | keyof ICardPropsBase> {
13
13
  /**
14
14
  * 自定义类名
15
15
  */
@@ -1,75 +1,55 @@
1
1
  <script setup lang="ts">
2
- import { computed } from "vue";
2
+ import { computed } from 'vue';
3
+ import type { IAvatarPropsBase } from '../../src/components/avatar/avatarPropsBase';
4
+ import {
5
+ avatarSizeClasses,
6
+ getAvatarColor,
7
+ getAvatarInitial,
8
+ } from '../../src/components/avatar/avatarUtils';
3
9
 
4
- /**
5
- * @component Avatar
6
- *
7
- * @description
8
- * Avatar 组件用于显示用户头像,支持真实头像和默认头像生成。
9
- * 当没有提供头像URL或头像加载失败时,自动生成带用户首字母的彩色圆形头像。
10
- *
11
- * @usage
12
- * 基本用法:
13
- * ```vue
14
- * <Avatar userName="张先生" />
15
- * ```
16
- *
17
- * 带真实头像:
18
- * ```vue
19
- * <Avatar userName="李女士" avatar="https://example.com/avatar.jpg" />
20
- * ```
21
- *
22
- * 不同尺寸:
23
- * ```vue
24
- * <Avatar userName="王先生" size="lg" />
25
- * ```
26
- *
27
- * @props
28
- * @prop {string} userName - 用户名称
29
- * @prop {string} [avatar] - 用户头像URL
30
- * @prop {'sm'|'md'|'lg'|'xl'} [size='md'] - 头像尺寸
31
- * @prop {string} [class] - 自定义类名
32
- * @prop {any} [class:list] - 类名列表
33
- */
10
+ /**
11
+ * @component Avatar
12
+ *
13
+ * @description
14
+ * Avatar 组件用于显示用户头像,支持真实头像和默认头像生成。
15
+ * 当没有提供头像URL或头像加载失败时,自动生成带用户首字母的彩色圆形头像。
16
+ *
17
+ * @usage
18
+ * 基本用法:
19
+ * ```vue
20
+ * <Avatar userName="张先生" />
21
+ * ```
22
+ *
23
+ * 带真实头像:
24
+ * ```vue
25
+ * <Avatar userName="李女士" avatar="https://example.com/avatar.jpg" />
26
+ * ```
27
+ *
28
+ * 不同尺寸:
29
+ * ```vue
30
+ * <Avatar userName="王先生" size="lg" />
31
+ * ```
32
+ *
33
+ * @props
34
+ * @prop {string} [userName] - 用户名称
35
+ * @prop {string} [avatar] - 用户头像URL
36
+ * @prop {'sm'|'md'|'lg'|'xl'} [size='md'] - 头像尺寸
37
+ * @prop {string} [class] - 自定义类名
38
+ * @prop {any} [class:list] - 类名列表
39
+ */
34
40
 
35
- export interface IAvatarProps {
36
- userName: string;
37
- avatar?: string;
38
- size?: "sm" | "md" | "lg" | "xl";
39
- class?: string;
40
- "class:list"?: any;
41
- }
41
+ export interface IAvatarProps extends IAvatarPropsBase {
42
+ class?: string;
43
+ 'class:list'?: any;
44
+ }
42
45
 
43
- const props = withDefaults(defineProps<IAvatarProps>(), {
44
- size: "md",
45
- class: "",
46
- });
46
+ const props = withDefaults(defineProps<IAvatarProps>(), {
47
+ size: 'md',
48
+ class: '',
49
+ });
47
50
 
48
- // 尺寸映射
49
- const sizeClasses = {
50
- sm: "cosy:w-8 cosy:h-8 cosy:text-sm",
51
- md: "cosy:w-12 cosy:h-12 cosy:text-lg",
52
- lg: "cosy:w-16 cosy:h-16 cosy:text-xl",
53
- xl: "cosy:w-20 cosy:h-20 cosy:text-2xl",
54
- };
55
-
56
- // 生成头像背景色
57
- const getAvatarColor = (userName: string) => {
58
- const colors = [
59
- "cosy:bg-primary",
60
- "cosy:bg-secondary",
61
- "cosy:bg-accent",
62
- "cosy:bg-info",
63
- "cosy:bg-success",
64
- "cosy:bg-warning",
65
- "cosy:bg-error",
66
- ];
67
- const index = userName.charCodeAt(0) % colors.length;
68
- return colors[index];
69
- };
70
-
71
- const sizeClass = computed(() => sizeClasses[props.size]);
72
- const avatarColor = computed(() => getAvatarColor(props.userName));
51
+ const sizeClass = computed(() => avatarSizeClasses[props.size || 'md']);
52
+ const avatarColor = computed(() => getAvatarColor(props.userName));
73
53
  </script>
74
54
 
75
55
  <template>
@@ -78,7 +58,7 @@ const avatarColor = computed(() => getAvatarColor(props.userName));
78
58
  'cosy:avatar',
79
59
  'cosy:rounded-full',
80
60
  'cosy:overflow-hidden',
81
- 'cosy:flex-shrink-0',
61
+ 'cosy:shrink-0',
82
62
  'cosy:bg-base-200',
83
63
  'cosy:flex',
84
64
  'cosy:items-center',
@@ -87,15 +67,13 @@ const avatarColor = computed(() => getAvatarColor(props.userName));
87
67
  props.class,
88
68
  props['class:list'],
89
69
  ]"
90
- v-bind="$attrs"
91
- >
70
+ v-bind="$attrs">
92
71
  <img
93
72
  v-if="avatar"
94
73
  :src="avatar"
95
74
  :alt="userName"
96
75
  class="cosy:w-full cosy:h-full cosy:object-cover"
97
- @error="handleImageError"
98
- />
76
+ @error="handleImageError" />
99
77
  <!-- 默认头像 -->
100
78
  <div
101
79
  :class="[
@@ -103,19 +81,18 @@ const avatarColor = computed(() => getAvatarColor(props.userName));
103
81
  avatarColor,
104
82
  'cosy:text-white cosy:flex cosy:items-center cosy:justify-center cosy:font-semibold',
105
83
  ]"
106
- :style="avatar ? 'display: none;' : 'display: flex;'"
107
- >
108
- {{ userName ? userName.charAt(0).toUpperCase() : '?' }}
84
+ :style="avatar ? 'display: none;' : 'display: flex;'">
85
+ {{ getAvatarInitial(userName) }}
109
86
  </div>
110
87
  </div>
111
88
  </template>
112
89
 
113
90
  <script lang="ts">
114
- // 处理图片加载错误
115
- const handleImageError = (event: Event) => {
116
- const img = event.target as HTMLImageElement;
117
- const nextElement = img.nextElementSibling as HTMLElement;
118
- if (img) img.style.display = 'none';
119
- if (nextElement) nextElement.style.display = 'flex';
120
- };
91
+ // 处理图片加载错误
92
+ const handleImageError = (event: Event) => {
93
+ const img = event.target as HTMLImageElement;
94
+ const nextElement = img.nextElementSibling as HTMLElement;
95
+ if (img) img.style.display = 'none';
96
+ if (nextElement) nextElement.style.display = 'flex';
97
+ };
121
98
  </script>
@@ -1,160 +1,164 @@
1
1
  <script setup lang="ts">
2
- import { computed } from "vue";
3
- import Container from "../container/Container.vue";
4
- import { getCardCombinedClassesVue, getCardPaddingClassVue } from "./class";
5
- import type { ICardProps } from "./props";
2
+ import { computed } from 'vue';
3
+ import Container from '../container/Container.vue';
4
+ import { getCardCombinedClassesVue, getCardPaddingClassVue } from './class';
5
+ import type { ICardProps } from './props';
6
6
 
7
- /**
8
- * @component Card
9
- * @description Vue 版本的 Card 组件,用于在页面中展示相关内容的容器,通常包含标题、描述和可选的图片
10
- * @props {string} title - 卡片标题(必填)
11
- * @props {string} [subtitle] - 卡片副标题或描述
12
- * @props {string} [imageUrl] - 卡片顶部图片的URL
13
- * @props {string} [href] - 如果提供,卡片将变成可点击的链接
14
- * @props {boolean} [compact=false] - 是否使用紧凑模式
15
- * @props {boolean} [muted=false] - 是否使用柔和色样式(未激活状态)
16
- * @props {ShadowSize} [shadow=xl] - 阴影大小
17
- * @props {string} [class] - 自定义CSS类,可用于覆盖默认样式
18
- */
7
+ /**
8
+ * @component Card
9
+ * @description Vue 版本的 Card 组件,用于在页面中展示相关内容的容器,通常包含标题、描述和可选的图片
10
+ * @props {string} title - 卡片标题(必填)
11
+ * @props {string} [subtitle] - 卡片副标题或描述
12
+ * @props {string} [imageUrl] - 卡片顶部图片的URL
13
+ * @props {string} [href] - 如果提供,卡片将变成可点击的链接
14
+ * @props {boolean} [compact=false] - 是否使用紧凑模式
15
+ * @props {boolean} [muted=false] - 是否使用柔和色样式(未激活状态)
16
+ * @props {ShadowSize} [shadow=xl] - 阴影大小
17
+ * @props {string} [class] - 自定义CSS类,可用于覆盖默认样式
18
+ */
19
19
 
20
- defineOptions({
21
- name: "Card",
22
- });
20
+ defineOptions({
21
+ name: 'Card',
22
+ });
23
23
 
24
- const props = withDefaults(defineProps<ICardProps>(), {
25
- class: "",
26
- compact: false,
27
- muted: false,
28
- shadow: "xl",
29
- });
24
+ const props = withDefaults(defineProps<ICardProps>(), {
25
+ class: '',
26
+ compact: false,
27
+ muted: false,
28
+ shadow: 'xl',
29
+ });
30
30
 
31
- // 从props中分离Container相关的属性和Card自身的属性
32
- const containerProps = computed(() => {
33
- const {
34
- // Container属性
35
- aspectRatio,
36
- centered,
37
- contentCentered,
38
- flex,
39
- fit,
40
- gap,
41
- height,
42
- items,
43
- justify,
44
- margin,
45
- muted,
46
- padding,
47
- py,
48
- pt,
49
- pb,
50
- px,
51
- pl,
52
- pr,
53
- width,
54
- rounded,
55
- background,
56
- border,
57
- borderColor,
58
- shadow,
59
- // Card属性
60
- class: className,
61
- compact,
62
- href,
63
- imageUrl,
64
- subtitle,
65
- title,
66
- ...rest
67
- } = props;
31
+ // 从props中分离Container相关的属性和Card自身的属性
32
+ const containerProps = computed(() => {
33
+ const {
34
+ // Container属性
35
+ aspectRatio,
36
+ centered,
37
+ contentCentered,
38
+ flex,
39
+ fit,
40
+ gap,
41
+ height,
42
+ items,
43
+ justify,
44
+ margin,
45
+ muted,
46
+ padding,
47
+ py,
48
+ pt,
49
+ pb,
50
+ px,
51
+ pl,
52
+ pr,
53
+ width,
54
+ rounded,
55
+ background,
56
+ border,
57
+ borderColor,
58
+ shadow,
59
+ // Card属性
60
+ class: className,
61
+ compact,
62
+ href,
63
+ imageUrl,
64
+ subtitle,
65
+ title,
66
+ ...rest
67
+ } = props;
68
68
 
69
- return {
70
- aspectRatio,
71
- centered,
72
- contentCentered,
73
- flex,
74
- fit,
75
- gap,
76
- height,
77
- items,
78
- justify,
79
- margin,
80
- muted,
81
- padding,
82
- py,
83
- pt,
84
- pb,
85
- px,
86
- pl,
87
- pr,
88
- width,
89
- rounded,
90
- background,
91
- border,
92
- borderColor,
93
- shadow,
94
- class: className,
95
- ...rest,
96
- };
97
- });
69
+ return {
70
+ aspectRatio,
71
+ centered,
72
+ contentCentered,
73
+ flex,
74
+ fit,
75
+ gap,
76
+ height,
77
+ items,
78
+ justify,
79
+ margin,
80
+ muted,
81
+ padding,
82
+ py,
83
+ pt,
84
+ pb,
85
+ px,
86
+ pl,
87
+ pr,
88
+ width,
89
+ rounded,
90
+ background,
91
+ border,
92
+ borderColor,
93
+ shadow,
94
+ class: className,
95
+ ...rest,
96
+ };
97
+ });
98
98
 
99
- // Card自身的属性
100
- const cardProps = computed(() => {
101
- const {
102
- // Container属性
103
- aspectRatio,
104
- centered,
105
- contentCentered,
106
- flex,
107
- fit,
108
- gap,
109
- height,
110
- items,
111
- justify,
112
- margin,
113
- muted,
114
- padding,
115
- py,
116
- pt,
117
- pb,
118
- px,
119
- pl,
120
- pr,
121
- width,
122
- rounded,
123
- background,
124
- border,
125
- borderColor,
126
- shadow,
127
- // Card属性
128
- class: className,
129
- compact,
130
- href,
131
- imageUrl,
132
- subtitle,
133
- title,
134
- ...rest
135
- } = props;
99
+ // Card自身的属性
100
+ const cardProps = computed(() => {
101
+ const {
102
+ // Container属性
103
+ aspectRatio,
104
+ centered,
105
+ contentCentered,
106
+ flex,
107
+ fit,
108
+ gap,
109
+ height,
110
+ items,
111
+ justify,
112
+ margin,
113
+ muted,
114
+ padding,
115
+ py,
116
+ pt,
117
+ pb,
118
+ px,
119
+ pl,
120
+ pr,
121
+ width,
122
+ rounded,
123
+ background,
124
+ border,
125
+ borderColor,
126
+ shadow,
127
+ // Card属性
128
+ class: className,
129
+ compact,
130
+ href,
131
+ imageUrl,
132
+ subtitle,
133
+ title,
134
+ ...rest
135
+ } = props;
136
136
 
137
- return {
138
- class: className,
139
- compact,
140
- href,
141
- imageUrl,
142
- subtitle,
143
- title,
144
- ...rest,
145
- };
146
- });
137
+ return {
138
+ class: className,
139
+ compact,
140
+ href,
141
+ imageUrl,
142
+ subtitle,
143
+ title,
144
+ ...rest,
145
+ };
146
+ });
147
147
 
148
- // 使用共用的工具函数计算组合类名(不包括Container的类名)
149
- const cardClasses = computed(() => getCardCombinedClassesVue(cardProps.value));
148
+ // 使用共用的工具函数计算组合类名(不包括Container的类名)
149
+ const cardClasses = computed(() =>
150
+ getCardCombinedClassesVue(cardProps.value)
151
+ );
150
152
 
151
- // 内容区域的padding类
152
- const contentPadding = computed(() =>
153
- getCardPaddingClassVue(cardProps.value.compact),
154
- );
153
+ // 内容区域的padding类
154
+ const contentPadding = computed(() =>
155
+ getCardPaddingClassVue(cardProps.value.compact)
156
+ );
155
157
 
156
- // 链接的目标属性
157
- const linkTarget = computed(() => (cardProps.value.href ? "_self" : undefined));
158
+ // 链接的目标属性
159
+ const linkTarget = computed(() =>
160
+ cardProps.value.href ? '_self' : undefined
161
+ );
158
162
  </script>
159
163
 
160
164
  <template>
@@ -168,12 +172,14 @@ const linkTarget = computed(() => (cardProps.value.href ? "_self" : undefined));
168
172
  <figure class="not-prose cosy:m-0 cosy:p-0">
169
173
  <img
170
174
  :src="cardProps.imageUrl"
171
- :alt="cardProps.title"
175
+ :alt="cardProps.title || ''"
172
176
  class="cosy:w-full cosy:h-48 cosy:object-cover cosy:rounded-t-lg" />
173
177
  </figure>
174
178
  </template>
175
179
  <div :class="['cosy:card-body', contentPadding]">
176
- <h2 class="cosy:card-title cosy:text-xl cosy:font-bold">
180
+ <h2
181
+ v-if="cardProps.title"
182
+ class="cosy:card-title cosy:text-xl cosy:font-bold">
177
183
  {{ cardProps.title }}
178
184
  </h2>
179
185
  <p
@@ -1,85 +1,80 @@
1
1
  <script setup lang="ts">
2
- import { computed } from "vue";
3
- import Avatar from "../avatar/Avatar.vue";
4
- import Badge from "../badge/Badge.vue";
5
- import Card from "../card/Card.vue";
6
- import SmartIcon from "../smart-icon/SmartIcon.vue";
7
- import Text from "../text/Text.vue";
8
- import type { IReviewProps } from "./props";
2
+ import { computed } from 'vue';
3
+ import Avatar from '../avatar/Avatar.vue';
4
+ import Badge from '../badge/Badge.vue';
5
+ import Card from '../card/Card.vue';
6
+ import SmartIcon from '../smart-icon/SmartIcon.vue';
7
+ import Text from '../text/Text.vue';
8
+ import type { IReviewProps } from './props';
9
9
 
10
- interface Props extends /* @vue-ignore */ IReviewProps {
11
- class?: string;
12
- "class:list"?: any;
13
- }
10
+ interface Props extends /* @vue-ignore */ IReviewProps {
11
+ class?: string;
12
+ 'class:list'?: any;
13
+ }
14
14
 
15
- const props = withDefaults(defineProps<Props>(), {
16
- verified: false,
17
- class: "",
18
- });
15
+ const props = withDefaults(defineProps<Props>(), {
16
+ verified: false,
17
+ class: '',
18
+ });
19
19
 
20
- // 从props中分离Card相关的属性和Review自身的属性
21
- const {
22
- userName,
23
- rating,
24
- comment,
25
- date,
26
- verified,
27
- avatar,
28
- class: className,
29
- "class:list": classList,
30
- // Card属性
31
- aspectRatio,
32
- centered,
33
- contentCentered,
34
- flex,
35
- fit,
36
- gap,
37
- height,
38
- items,
39
- justify,
40
- margin,
41
- muted,
42
- padding,
43
- py,
44
- pt,
45
- pb,
46
- px,
47
- pl,
48
- pr,
49
- shadow, // 添加 shadow 属性
50
- width,
51
- rounded,
52
- background,
53
- border,
54
- borderColor,
55
- subtitle,
56
- title,
57
- imageUrl,
58
- href,
59
- compact,
60
- } = props;
20
+ // 从props中分离Card相关的属性和Review自身的属性
21
+ const {
22
+ userName,
23
+ rating,
24
+ comment,
25
+ date,
26
+ verified,
27
+ avatar,
28
+ class: className,
29
+ 'class:list': classList,
30
+ // Card属性
31
+ aspectRatio,
32
+ centered,
33
+ contentCentered,
34
+ flex,
35
+ fit,
36
+ gap,
37
+ height,
38
+ items,
39
+ justify,
40
+ margin,
41
+ muted,
42
+ padding,
43
+ py,
44
+ pt,
45
+ pb,
46
+ px,
47
+ pl,
48
+ pr,
49
+ shadow,
50
+ width,
51
+ rounded,
52
+ background,
53
+ border,
54
+ borderColor,
55
+ } = props;
61
56
 
62
- // 生成星级评分
63
- const starArray = computed(() => {
64
- return Array.from({ length: 5 }, (_, i) => ({
65
- filled: i < Math.floor(props.rating),
66
- half: i === Math.floor(props.rating) && props.rating % 1 !== 0,
67
- }));
68
- });
57
+ // 生成星级评分
58
+ const starArray = computed(() => {
59
+ return Array.from({ length: 5 }, (_, i) => ({
60
+ filled: i < Math.floor(props.rating),
61
+ half: i === Math.floor(props.rating) && props.rating % 1 !== 0,
62
+ }));
63
+ });
69
64
 
70
- // 格式化日期
71
- const formattedDate = computed(() => {
72
- if (!props.date) return "";
73
- try {
74
- return new Date(props.date).toLocaleDateString("zh-CN", {
75
- year: "numeric",
76
- month: "2-digit",
77
- day: "2-digit",
78
- });
79
- } catch {
80
- return props.date;
81
- }
82
- });
65
+ // 格式化日期
66
+ const formattedDate = computed(() => {
67
+ if (!props.date) return '';
68
+ try {
69
+ return new Date(props.date).toLocaleDateString('zh-CN', {
70
+ year: 'numeric',
71
+ month: '2-digit',
72
+ day: '2-digit',
73
+ });
74
+ } catch {
75
+ return props.date;
76
+ }
77
+ });
83
78
  </script>
84
79
 
85
80
  <template>
@@ -109,49 +104,49 @@ const formattedDate = computed(() => {
109
104
  border,
110
105
  borderColor,
111
106
  shadow,
112
- subtitle,
113
- title: userName, // 将 userName 作为 title 传递给 Card 组件
114
- imageUrl,
115
- href,
116
- compact,
117
107
  }"
118
- :class="className"
108
+ :class="[className, 'cosy:review', 'cosy:p-6']"
119
109
  :class:list="classList">
120
- <div class="cosy:flex cosy:items-start cosy:gap-4">
121
- <Avatar
122
- v-if="avatar"
123
- :avatar="avatar"
124
- :userName="userName"
125
- size="md"
126
- class="cosy:flex-shrink-0" />
110
+ <!-- 用户信息 -->
111
+ <div class="cosy:flex cosy:items-center cosy:gap-3 cosy:mb-3">
112
+ <!-- 用户头像 -->
113
+ <Avatar :userName="userName" :avatar="avatar" size="md" />
114
+
115
+ <!-- 用户名称和认证状态 -->
127
116
  <div class="cosy:flex-1">
128
- <div class="cosy:flex cosy:items-center cosy:justify-between">
129
- <Text variant="h3" class="cosy:text-lg cosy:font-semibold">
117
+ <div class="cosy:flex cosy:items-center cosy:gap-2 cosy:mb-1">
118
+ <Text class="cosy:font-medium cosy:text-base-content">
130
119
  {{ userName }}
131
120
  </Text>
132
- <Badge v-if="verified" variant="success" size="sm">
133
- <SmartIcon name="check-circle" class="cosy:w-4 cosy:h-4" />
134
- 已认证
135
- </Badge>
136
- </div>
137
- <div class="cosy:flex cosy:items-center cosy:mt-1">
138
- <div class="cosy:flex cosy:space-x-1">
139
- <SmartIcon
140
- v-for="(star, index) in starArray"
141
- :key="index"
142
- :name="
143
- star.filled ? 'star-filled' : star.half ? 'star-half' : 'star'
144
- "
145
- class="cosy:w-5 cosy:h-5 cosy:text-yellow-400" />
146
- </div>
147
- <Text variant="small" class="cosy:ml-2 cosy:text-gray-500">
148
- {{ formattedDate }}
149
- </Text>
121
+ <Badge v-if="verified" variant="success" size="sm"> 已认证 </Badge>
150
122
  </div>
151
- <Text variant="p" class="cosy:mt-3">
152
- {{ comment }}
153
- </Text>
154
123
  </div>
155
124
  </div>
125
+
126
+ <!-- 评分 -->
127
+ <div class="cosy:flex cosy:items-center cosy:gap-1 cosy:mb-3">
128
+ <SmartIcon
129
+ v-for="(star, index) in starArray"
130
+ :key="index"
131
+ keyword="star"
132
+ size="16px"
133
+ :class="
134
+ star.filled || star.half ? 'cosy:text-warning' : 'cosy:text-base-300'
135
+ " />
136
+ </div>
137
+
138
+ <!-- 评论内容 -->
139
+ <div class="cosy:mb-3">
140
+ <Text class="cosy:text-base-content/80 cosy:leading-relaxed">
141
+ {{ comment }}
142
+ </Text>
143
+ </div>
144
+
145
+ <!-- 评价日期 -->
146
+ <div v-if="date">
147
+ <Text size="sm" class="cosy:text-base-content/50">
148
+ {{ formattedDate }}
149
+ </Text>
150
+ </div>
156
151
  </Card>
157
152
  </template>
@@ -10,7 +10,15 @@ import type { ICardProps } from "../card/props";
10
10
  */
11
11
  export interface IReviewProps
12
12
  extends IReviewPropsBase,
13
- Omit<ICardProps, keyof IReviewPropsBase> {
13
+ Omit<
14
+ ICardProps,
15
+ | keyof IReviewPropsBase
16
+ | "title"
17
+ | "subtitle"
18
+ | "imageUrl"
19
+ | "href"
20
+ | "compact"
21
+ > {
14
22
  /**
15
23
  * 自定义类名
16
24
  */
package/package.json CHANGED
@@ -1,89 +1,90 @@
1
1
  {
2
- "name": "@coffic/cosy-ui",
3
- "version": "0.9.84",
4
- "description": "An astro component library",
5
- "author": {
6
- "name": "nookery",
7
- "url": "https://github.com/nookery"
8
- },
9
- "repository": {
10
- "url": "https://github.com/CosyZone/cosy-ui"
11
- },
12
- "license": "MIT",
13
- "keywords": [
14
- "astro-integration",
15
- "astro-component",
16
- "withastro",
17
- "astro",
18
- "cosy-ui"
19
- ],
20
- "homepage": "https://ui.coffic.cn/en",
21
- "publishConfig": {
22
- "access": "public"
23
- },
24
- "sideEffects": [
25
- "*.css",
26
- "style.ts",
27
- "dist/style.ts"
28
- ],
29
- "main": "./dist/index-astro.ts",
30
- "module": "./dist/index-astro.ts",
31
- "types": "./dist/index-astro.ts",
32
- "exports": {
33
- ".": {
34
- "import": "./dist/index-astro.ts",
35
- "types": "./dist/index-astro.ts"
36
- },
37
- "./vue": {
38
- "import": "./dist/index-vue.ts",
39
- "types": "./dist/index-vue.ts"
40
- }
41
- },
42
- "files": [
43
- "dist",
44
- "index.ts"
45
- ],
46
- "scripts": {
47
- "build": "tsx scripts/build.ts",
48
- "check": "astro check"
49
- },
50
- "type": "module",
51
- "peerDependencies": {
52
- "astro": "^5.1.3",
53
- "vue": "^3.0.0"
54
- },
55
- "peerDependenciesMeta": {
56
- "astro": {
57
- "optional": true
58
- },
59
- "vue": {
60
- "optional": true
61
- }
62
- },
63
- "dependencies": {
64
- "@remixicon/vue": "^4.7.0",
65
- "astro-integration-kit": "^0.19.1",
66
- "fs-extra": "^11.3.2",
67
- "html-to-image": "^1.11.13",
68
- "shiki": "^3.14.0",
69
- "typescript": "^5.9.3"
70
- },
71
- "devDependencies": {
72
- "@astrojs/check": "^0.9.5",
73
- "@astrojs/mdx": "^4.3.9",
74
- "@astrojs/ts-plugin": "^1.10.5",
75
- "@tailwindcss/typography": "^0.5.19",
76
- "@tailwindcss/vite": "^4.1.16",
77
- "@tsconfig/node22": "^22.0.2",
78
- "@types/fs-extra": "^11.0.4",
79
- "@types/node": "^24.9.2",
80
- "astro": "^5.15.2",
81
- "daisyui": "^5.3.10",
82
- "globals": "^16.4.0",
83
- "rollup-plugin-copy": "^3.5.0",
84
- "sharp": "^0.34.4",
85
- "tailwindcss": "^4.1.16",
86
- "tsx": "^4.20.6",
87
- "vite": "^7.1.12"
88
- }
2
+ "name": "@coffic/cosy-ui",
3
+ "version": "0.9.85",
4
+ "description": "An astro component library",
5
+ "author": {
6
+ "name": "nookery",
7
+ "url": "https://github.com/nookery"
8
+ },
9
+ "repository": {
10
+ "url": "https://github.com/CosyZone/cosy-ui"
11
+ },
12
+ "license": "MIT",
13
+ "keywords": [
14
+ "astro-integration",
15
+ "astro-component",
16
+ "withastro",
17
+ "astro",
18
+ "cosy-ui"
19
+ ],
20
+ "homepage": "https://ui.coffic.cn/en",
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "sideEffects": [
25
+ "*.css",
26
+ "style.ts",
27
+ "dist/style.ts"
28
+ ],
29
+ "main": "./dist/index-astro.ts",
30
+ "module": "./dist/index-astro.ts",
31
+ "types": "./dist/index-astro.ts",
32
+ "exports": {
33
+ ".": {
34
+ "import": "./dist/index-astro.ts",
35
+ "types": "./dist/index-astro.ts"
36
+ },
37
+ "./vue": {
38
+ "import": "./dist/index-vue.ts",
39
+ "types": "./dist/index-vue.ts"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "index.ts"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsx scripts/build.ts",
48
+ "build:prod": "tsx scripts/build.ts --no-sourcemap",
49
+ "check": "astro check"
50
+ },
51
+ "type": "module",
52
+ "peerDependencies": {
53
+ "astro": "^5.1.3",
54
+ "vue": "^3.0.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "astro": {
58
+ "optional": true
59
+ },
60
+ "vue": {
61
+ "optional": true
62
+ }
63
+ },
64
+ "dependencies": {
65
+ "@remixicon/vue": "^4.7.0",
66
+ "astro-integration-kit": "^0.19.1",
67
+ "fs-extra": "^11.3.2",
68
+ "html-to-image": "^1.11.13",
69
+ "shiki": "^3.14.0",
70
+ "typescript": "^5.9.3"
71
+ },
72
+ "devDependencies": {
73
+ "@astrojs/check": "^0.9.5",
74
+ "@astrojs/mdx": "^4.3.9",
75
+ "@astrojs/ts-plugin": "^1.10.5",
76
+ "@tailwindcss/typography": "^0.5.19",
77
+ "@tailwindcss/vite": "^4.1.16",
78
+ "@tsconfig/node22": "^22.0.2",
79
+ "@types/fs-extra": "^11.0.4",
80
+ "@types/node": "^24.9.2",
81
+ "astro": "^5.15.2",
82
+ "daisyui": "^5.3.10",
83
+ "globals": "^16.4.0",
84
+ "rollup-plugin-copy": "^3.5.0",
85
+ "sharp": "^0.34.4",
86
+ "tailwindcss": "^4.1.16",
87
+ "tsx": "^4.20.6",
88
+ "vite": "^7.1.12"
89
+ }
89
90
  }